티스토리 뷰

Java & Kotlin

[JVM] 예시로 살펴보는 IntelliJ 프로파일링 결과 분석 및 성능 최적화 방법

망나니개발자 2024. 3. 12. 10:00
반응형

 

 

1. 예시로 살펴보는 IntelliJ 프로파일링 결과 분석 및 성능 최적화 방법


[ 샘플 코드 작성 및 프로파일링하기 ]

IntelliJ Ultimate를 사용하고 있다면, 손쉽게 프로파일링을 진행하고 결과를 확인할 수 있다.

예를 들어 다음과 같은 컨트롤러와 서비스가 있다고 하자.

@RestController
@RequiredArgsConstructor
class BoxingController {

    private final BoxingService boxingService;

    @GetMapping("/boxing")
    int boxing() {
        int result1 = boxingService.max1(100000000L);
        int result2 = boxingService.max2(100000000L);
        return result1 + result2;
    }
}

@Service
public class BoxingService {

    int max1(Long loop) {
        var result = new ArrayList<Long>();
        for (int i = 0; i < loop; i++) {
            result.add((long) i);
        }

        return result.stream()
            .filter(v -> (v % 2 == 0))
            .mapToInt(Long::intValue)
            .max()
            .orElseThrow();
    }

    int max2(Long loop) {
        var result = new ArrayList<Long>();
        for (int i = 0; i < loop; i++) {
            result.add((long) i);
        }

        return result.stream()
            .filter(v -> (v % 2 == 0))
            .mapToInt(Long::intValue)
            .max()
            .orElseThrow();
    }
}

 

 

IntelliJ에서 프로파일링과 함께 서버를 실행해주고, 해당 기능을 호출한 후에 프로파일링 결과를 보도록 하자.

 

.

 

 

 

[ 프로파일링 결과 분석하고 성능 최적화하기 ]

먼저 Flame Graph에서 프로파일링 결과를 살펴보도록 하자. 가로는 소요 시간, 세로는 호출 스택에 해당한다. 결과를 보니 직접 작성된 코드인 노랑색 영역 중 BoxingService의 max1과 max2에서 CPU 처리 시간이 상당히 오래 소요되었음을 확인할 수 있다.

 

 

Method List로 넘어가서 실제 CPU 처리 시간이 오래 소요된 메서드를 확인해보도록 하자. 이 중에서 원시값인 long을 래퍼 타입인 Long으로 바꾸는데 약 520ms 소요되었음을 파악할 수 있다.

 

 

해당 메서드를 어디에서 많이 호출했는지를 확인해야 한다. 따라서 Long.valueOf(long) 부분에서 우클릭을 통해 해당 메서드를 호출한 다른 메서드들을 확인하도록 하자.

 

 

확인해보니 Flame Graph에서 살펴보았던 BoxingService가 확인되었다.

 

 

따라서 이어서 해당 메서드의 위치로 이동해주도록 하자.

 

 

직접 코드로 이동해서 확인해보니 int 형 타입을 long으로 바꾸고, 해당 long 타입을 Long으로 변환하는 과정에서 시간이 많이 소요되는 것으로 보인다.

 

 

따라서 해당 메서드 내부를 개선하여 원인을 해결해주도록 하자.

@Service
public class BoxingService {

    int max1(Long loop) {
        return IntStream.range(0, loop.intValue())
            .filter(v -> (v % 2 == 0))
            .max()
            .orElseThrow();
    }

    int max2(Long loop) {
        return IntStream.range(0, loop.intValue())
            .filter(v -> (v % 2 == 0))
            .max()
            .orElseThrow();
    }
}

 

 

그리고 이후에 다시 프로파일링을 해보면 해당 부분이 개선되었음을 확인할 수 있다.

먼저 Flame Graph를 살펴보면 BoxingService에서 처리되는 시간 비율(가로)이 줄어들었음을 확인할 수 있다.

 

 

보다 상세한 지표를 위해 Method List로 가서 CPU 순으로 정렬해보아도 개선되었음을 확인할 수 있다.

 

 

 

실무 코드를 기반으로 프로파일링한 결과를 분석해보면 개선할 수 있는 포인트들을 상당히 많이 잡을 수 있을 것이다. 불필요한 코드는 최적화하고 가용되는 리소스를 최소화하여 인프라 최소화에 기여할 수 있도록 하자.

 

 

 

 

관련 포스팅

  1. Async-Profiler 소개 및 IntelliJ에서 프로파일링 결과 분석하는 방법
  2. 예시로 살펴보는 IntelliJ 프로파일링 결과 분석 및 성능 최적화 방법

 

 

 

 

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/05   »
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
글 보관함