본문 바로가기

프로그래밍(TA, AA)/JVM 언어

[자바성능] for 루프 성능

1. 조건문에서의 속도

조건문은 성능에 얼마나 많이 영향을 줄까요? 먼저 조건문에 어떤 것들이 있는지 보고, 조건문이 얼마나 성능에 영향을 주는지 알아보면, 조건문의 종류는 다음과 같습니다.

 

if-else if-else

switch

 

if문 안에는 boolean 형태의 결과값만 사용할 수 있습니다. switch문은 byte, short, char, int, string 등을 사용한 조건 분기가 가능합니다. 일반적으로 if문에서 분기를 많이 하면 시간이 많이 소요된다고 생각하는데, 실제로는 그렇지 않습니다.

if문 조건 안에 들어가는 비교 구문에서 속도를 잡아먹지 않는 한, if 문장 자체에서는 그리 많은 시간이 소요되지 않습니다.

 

StopWatchAverage s1 = new StopWatchAverage("IF1");

int runCount=100000;
for(int loop=0; loop<runCount;loop++) {
     sw1.start();
     if(loop < 50) { 
     } else if(loop < 150) { 
     } else if(loop < 250) { 
     } else if(loop < 350) { 
     } else if(loop < 450) { 
     } else if(loop < 550) { 
     } else if(loop < 650) { 
     } else if(loop < 750) { 
     } else if(loop < 850) { 
     } else if(loop < 950) { 
     } else { 
     }
     sw1.stop(); 
}

out.println("<BR>"+sw1);

여기에 있는 if 문장에 대한 성능 측정결과는 다음과 같습니다.

 

Run Count : 100000, Total : 215.60 ms, Average : 0.002156 ms

 

10회의 if 문장을 수행할때 소요되는 시간은 0.0021ms입니다. 다시 말하면, 0.0000021초가 소요된다는 것입니다. 위 소스가 유닉스나 윈도우 기반 서버에서 수행된다면 더 빠른 결과가 나올 것입니다. switch 문장도 마찬가지로 빠른 응답 결과가 나옵니다. Sun에 있는 문서를 보면 switch는 숫자 비교시 if보다 가독성이 좋으므로 정해져있는 숫자로 분기시에는 switch를 권장한다고 나와있습니다.

 

 

2. 반복 구문에서의 속도

자바에서 사용하는 반복 구문은 다음 세 가지입니다.

 

for

do-while

while

일반적으로 for문을 많이 사용하고, 가끔 while문을 사용하기도 하는데, while문은 잘못하면 무한 루프를 돌 수 있으므로 되도록이면 for문을 사용하기를 권장합니다.

 

for(int loop=0; loop<v.size();loop++)

위처럼 코딩을 하는 습관은 좋지 않습니다. 매번 반복하면서 v.size() 메소드를 호출하기 때문입니다.

 

int vSize=v.size();
for(int loop=0; loop<vSize; loop++)

이렇게 하면 필요없는 size() 메소드 반복 호출이 없어지므로 더 빠르게 처리됩니다.

 

Vector<String> v = new Vector<String>();
...
for(String str : v)

별도로 형변환하거나, get() 메소드 또는 elementAt() 메소드를 호출할 필요 없이 순서에 따라서 String 객체를 for 문장 안에서 사용할 수 있으므로 매우 편리합니다.

 

위 코드 세가지를 순서대로 성능을 비교한 결과는 아래와 같습니다.

 

[for 1] Run Count : 10, Total: 9.084953 ms, Average: 0.908495 ms

[for 2] Run Count : 10, Total: 1.481752 ms, Average: 0.148175 ms

[for 3] Run Count : 10, Total: 0.023749 ms, Average: 0.002374 ms

 

v.size() 메소드를 반복해서 호출한 부분이 가장 느립니다. 위의 경우는 for loop안에 실제 동작 코드가 없이 수행한경우라 큰 차이가 없습니다. for loop안에 동작 코드를 추가한다면 아래의 성능결과가 나옵니다. 예상외로 마지막 for 구문이 가장 느린것으로 나옵니다.

 

[for 1] Run Count : 10, Total: 23.258822 ms, Average: 2.325882 ms

[for 2] Run Count : 10, Total: 16.636193 ms, Average: 1.663619 ms

[for 3] Run Count : 10, Total: 58.526435 ms, Average: 5.852643 ms

 

가장 빠르고 편리한 방법은 배열이나 Vector의 크기를 먼저 읽어온 후 반복 구문을 돌리는 것입니다. 물론 예제에서 10만 번을 반복했기 때문에 차이가 났을 뿐, 실제 운영중인 웹 시스템에서는 별 차이가 없을 것입니다. 하지만 for loop 검증부분에 크기를 계속 비교하는 구문은 피해서 개발하는 것이 좋습니다.

 

 

3. 반복 구문 속도 향상 사례

 

반복 구문에서의 필요 없는 반복 제거

public void sample(DataVO data, String key) {
     TreeSet treeSet2=null;
     treeSet2=(TreeSet)data.get(key);
     if(treeSet2 != null) {
          for(int i=0; i<treeSet2.size(); i++) {
          DataVO2 data2=(DataVO2)treeSet2.toArray()[i];
          ...
          }
     }
}

 

TreeSet 형태의 데이터를 갖고 있는 DataVO에서 TreeSet을 하나 추출하여 처리하는 부분입니다.

이 소스의 문제는 toArray() 메소드를 반복해서 수행한다는 점입니다. 이 코드는 toArray() 메소드가 반복되지 않도록 for 문장 앞으로 옮겨 수정하면 됩니다.

 

 

4. 마무리

반복 구문은 어떤 애플리케이션을 개발하더라도 반드시 사용해야 하는 부분입니다. 하지만 조금이라도 생각을 잘못하면 무한 루프를 수행하여 애플리케이션을 재시작하거나 스레드를 찾아서 중단시켜야 하는 경우가 발생하여 성능상 문제가 되는 프로그램이 되기도합니다. 반대로 생각하면, 반복 구문의 문제점을 찾으면 성능상 문제가 되는 부분을 더 쉽게 해결할 수 있습니다.

 

위 성능 비교는 보통 1,000번에서 10,000번 정도 반복을 해서 비교한 결과입니다. 한 사람의 보험료를 80세까지 계산하는 시스템이나, 연간 세금을 계산하는 시스템, 자동차의 부품과 같이 엄청나게 많은 부품을 관리하는 시스템의 경우 1,000번 10,000번 반복하는 로직은 기본입니다. 성능 튜닝은 응답 시간의 비중이 큰 부분부터 하는 것이 기본 중의 기본입니다. 작은 반복 구문이 큰 성능 저하를 가져 올 수도 있다는 것은 염두해 두어야 합니다.