Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

csct3434

[자바 성능 튜닝 이야기] 05. 조건문과 반복문의 성능 본문

개발 서적/자바 성능 튜닝 이야기

[자바 성능 튜닝 이야기] 05. 조건문과 반복문의 성능

csct3434 2024. 5. 5. 20:08

if문

대상 응답 시간 (마이크로초)
if 0개 * 1000회 0.46
if 10개 * 1000회 5
if 100개 * 1000회 63

경유하는 if문의 개수를 달리하여 동일한 작업을 처리하는데 소요되는 시간을 측정한 결과이다. if문 10,000회를 실행하는데 4.54 마이크로초가 소요되었고, 100,000회를 수행하는데 62.54 마이크로초가 소요되었다. if문을 1회 실행하는데 0.4 ~ 0.6 나노초가 소요된 것으로, 아주 큰 성능 저하가 발생한다고 보기는 어렵다.


switch문

switch문도 if문과 마찬가지로 빠른 응답 결과가 나온다.

JDK 7부터 String 문자열을 switch문에 사용할 수 있는데, 이는 컴파일러가 문자열을 hashCode() 메서드의 반환값으로 처리하기 때문이다. 자바 컴파일러는 각 문자열을 hashCode() 반환값으로 변환하여 case문을 오름차순으로 정렬 후, String의 equals() 메서드를 통해 분기 여부를 결정한다.


for문

성능 비교

import java.util.ArrayList;
import java.util.List;

class Solution {
    
    int LOOP_COUNT = 100000;
    List<Integer> list;
    
    public void setUp() {
        list = new ArrayList<Integer>(LOOP_COUNT);
        for(int loop=0; loop<LOOP_COUNT; loop++) {
            list.add(loop);
        }
    }
    
    public void traditionalForLoop() {
        for (int loop = 0, listSize = list.size(); loop < listSize; loop++) {
            resultProcess(list.get(loop));
        }
    }
    
    public void traditionalSizeForLoop() {
        for (int loop = 0; loop < list.size(); loop++) {
            resultProcess(list.get(loop));
        }
    }

    public void timeForEachLoop() {
        for (Integer loop:list) {
            resultProcess(loop);
        }
    }
    
    int current;
    public void resultProcess(int result) {
        current = result;
    }
}
대상 응답 시간(마이크로초)
traditionalForLoop() 410
traditionalSizeForLoop() 413
timeForEachLoop() 481

가장 빠른 방법은 크기를 먼저 읽어온 후 반복문을 돌리는 것이다.

예제에서 10만 번을 반복했기 때문에 차이가 났을 뿐, 실제 운영중인 웹 시스템에선 이렇게 많이 반복하지 않기 때문에 별차이가 없을 것이다. 하지만 검증부에서 계속해서 크기를 비교하는 것은 피해서 개발하기를 바란다.

반복문에서의 필요 없는 반복

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

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

이 코드의 문제점은 toArray() 메서드를 불필요하게 반복해서 수행한다는 점이다. 게다가 for문에서 treeSet.size() 메서드를 지속적으로 호출하도록 되어 있다.

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

불필요한 반복을 제거하여 수정한 결과는 위와 같다.


성능 튜닝은 응답 시간의 비중이 큰 부분부터 하는 것이 기본 중의 기본이다. 하지만 작은 부분을 차지하는 반복문이 큰 성능 저하를 가져올 수도 있다는 것을 명심하자.