- 자바 성능 수치를 직접 측정하는 내용을 자세히
- 목표는 잠재적 위험을 이해하고 기준으로 삼을 만한 성능 수치만 산출하도록 안내하는 것
🗳️ 5.1 자바 성능 측정 기초
- 성능 분석: 기초 실험과학 분야로 귀결된 다양한 기술 측면을 종합한 것
- 벤치마크 = 하나의 과학 실험 = 입출력을 지닌 일종의 블랙 박스 ≠ 단순히 데이터 수치 분석
- 목표: 벤치마크로 공정한 테스트를 하는 것
- 가급적 시스템의 어느 한곳만 변경하고 다른 외부 요인은 벤치마크 안에 두고 통제
100,000개 숫자 정렬하는 벤치마크 코드 예시
public class ClassicSort {
private static final int N = 1_000;
private static final int I = 150_000;
private static final List<Integer> testData = new ArrayList<>();
public static void main(String[] args) {
Random randomGenerator = new Random();
for (int i = 0; i < N; i++) {
testData.add(randomGenerator.nextInt(Integer.MAX_VALUE));
}
System.out.println("정렬 알고리즘 테스트");
double startTime = System.nanoTime();
for (int i = 0; i < I; i++) {
List<Integer> copy = new ArrayList<>(testData);
Collections.sort(copy);
}
double endTime = System.nanoTime();
double timePerOperation = ((endTime - startTime) / (1_000_000_000L * I));
System.out.println("결과: " + (1/timePerOperation) + "op/s");
}
}
- 문제점1) JVM 웜업을 전혀 고려하지 않음
- JIT 컴파일러는 호출 계층을 최적화하므로 벤치마크 캡처 타이밍에 따라 달라짐
- 불확정적인 가비지 수집도 고려하지 않음 → GC가 일어날 가능성이 큰 시기에는 타이밍을 캡처하지 않도록 하기
- 문제점2) 테스트하려는 코드에서 생성된 결과를 실제로 사용하지 않는 것
- JIT는 벤치마크하려던 것 자체를 최적화할 수도
- 해결방안
- 시스템 전체를 벤치마크
- 연관된 저수준의 결과를 의미있게 비교하기 위해 앞서 언급한 많은 문제를 공통 프레임워크를 이용해 처리하는 것
💙 5.2 JMH 소개
5.2.1 될 수 있으면 마이크로벤치마크하지 말지어다
- 애플리케이션이 아닌 인프라 문제였던 일화
- 개발자라면 큰 그림 보기
5.2.2 휴리스틱: 마이크로벤치마킹은 언제 하나?
- 자바 플랫폼은 원래 천성이 동적이고, 가비지 수집 및 공격적인 JIT 최적화와 같은 특성으로 성능 가늠 어렵
- 저수준 분석이나 마이크로벤치마킹을 하는 중요 유스케이스
- 사용 범위가 넗은 범용 라이브러리 코드를 개발한다.
- OpenJDK 또는 다른 자바 플랫폼 구현체를 개발한다.
- 지연에 극도로 민감한 코드를 개발한다(예: 저지연 거래)
- 마이크로벤치마킹을 수행하였을 때 가치 있는 결과 도출 조건
- 총 코드 경로 실행 시간은 적어도 1밀리초, 실제로는 100마이크로초 보다 짧아야 한다.
- 메모리 할당률을 측정하는데, 그 값은 1MB/s 미만, 가급적 0에 가까운 값이어야 한다.
- 100%에 가깝게 CPU를 사용하며 시스템 이용률은 낮게(10% 밑으로) 유지해야 한다.
- 실행 프로파일러로 CPU를 소비하는 메서드들의 분포를 이해해야 한다. (보통 2~3개만이 많은 영향)
5.2.3 JMH 프레임워크
- 거론한 이슈들을 해소하고자 개발된 프레임워크
- JVM을 빌드한 사람들이 직접 만든 프레임워크라서 최적화 베어 트랩을 어떻게 피할 때 유용
- 벤치마크 프레임워크는 컴파일 타임에 벤치마크 내용을 알 수 없으므로 동적이어야 함
- 벤치마크 코드가 결과 신뢰도에 영향을 미칠 수 있음
- JMH는 벤치마크 코드가 루프 최적화에 걸리지 않을 정도로 회수 조정
5.2.4 벤치마크 실행
- 메이븐 프로젝트에서
@Bennchmark
어노테이션을 메서드에 붙여 실행
- 주요 강력 기능
- 컴파일러를 제어한다.
- 벤치마크 도중 CPU 사용 수준을 시뮬레이션한다.
🔮 5.3 JVM 성능 통계
- 결과 데이터 분포를 다루는 일은 필수
- 모든 측정은 어느 정도의 오차 수반
5.3.1 오차 유형
- 랜덤 오차(random error): 측정 오차 또는 무관계 요인이 어떤 상관관계 없이 결과에 영향을 미침
- 계통 오차(systematic error): 원인을 알 수 없는 요인이 상관관계 있는 형태로 측정에 영향을 미침
- 예시
계통 오차
- 상황: JSON을 주고받는 백엔드 자바 웹 서비스의 성능 테스트 수행
- 작용하고 있는 두 가지 계통 효과
- 선형 패턴으로 증가하는 위쪽 그래프(특이점 서비스)를 보니 어떤 한정된 서버 리소스가 조금씩 소모 → 메모리 누수나 스레드가 요청 처리 도중 다른 리소스를 점유하고 안 놔주는 상황에서 나타나는 패턴
- 다른 서비스는 대부분 180밀리초 안팎의 일정한 응답 시간을 보임 → 서버 장거리 영향으로 120~150밀리초 범위의 지연 시간이 대부분 → 큰 계통 효과 때문에 실제 응답 시간의 차이가 묻혀버림
- 계통 오차는 애플리케이션 문제가 아니라 테스트 설정부터 잘못돼서 발생한 것
랜덤 오차
- 원인을 알 수 없는, 또는 예기치 못한 환경상의 변화 때문에 일어남
- 소프트웨어세너는 랜덤 오차의 근원은 오직 운영 환경 뿐
- 대부분 정규 분포를 따름 → JVM에는 이 모델이 맞지 않음
허위 상관
- “상관은 인과를 나타내지 않는다”
- JVM과 성능 분석 영역에서는 그럴싸해 보이는 상관관계만 보고 측정값 간의 인과 관계를 넘겨짚지 않도록 조심해야 함
5.3.2 비정규 통계학
- 정규 분포와 같은 분석 기법은 분포 그래프에 동떨어진 특이점이 몇 개만 있어도 결과가 왜곡되기 쉬운 단점
- 정상적인 응답 시간보다, 특이점을 유발하는 이벤트가 더 중요한 관심사
- JIT 컴파일돼서 GC 사이클이 없는 핫 패스의 존재는 긴 꼬리형을 초래하고, 정규 분포와 대치 됨
백분위 수
- JVM의 긴 꼬리형 비정규 분포 다루는 데 유용한 기법
- 전체 분포를 단일 결과값으로 표현하는 평균 x, 구간별 샘플링 o
50.0% level was 23 ns
90.0% level was 30 ns
99.0% level was 43 ns
99.9% level was 164 ns
99.99% level was 248 ns
99.999% level was 3,458 ns
99.9999% level was 17,463 ns
- 메서드 결과에서, 긴 꼬리형 분포임으로 평균, 90% 백분위 수를 차례로 구한 뒤, 이후에는 대수적 샘플링
- 1,000개당 한크기 정도, 100,000개당 하나는 실행 시간이 두 크기 정도 나ㅈ빠짐
- 긴 꼬리형 분포 == 고동적 범위 분포
- 측정값의 동적 범위 = 최댓값을 최소값으로 나눈 값
💊 5.4 통계치 해석
- 애플리케이션을 측정해서 얻은 결과값을 해석하는 일은 매우 어려움
웹 애플리케이션 응답 예시
- ↑ 200, 400, 500대와 같은 응답 코드가 있다는 사실만 알고 있으면 응답 히스토그램 해석 가능
- 응답 유형마다 응답 시간 분포는 다름
- ↑ 클라이언트 오류
- 매핑되지 않은 URL을 클라이언트가 요청하면 웹 서버는 곧장 응답
- ↑ 서버 오류
- 서버 에러는 장시간 처리하다가 발생하는 편
- ↑ 성공 요청
- 성공한 요청은 긴 꼬리형 분포를 보이지만, 실제로는 극대값이 여럿인 다봉 분포를 나타냄
- ↑ 모든 값을 합치니 위와 같음
- 일반적인 측정값을 보다 유의미한 하위 구성 요소들로 분해하는 개념은 아주 유용
- 분석자는 결괏값을 보고 결론을 도출하기 전에 먼저 본인이 데이터 및 도메인을 충분히 이해해야 함
🧰 5.5 마치며
- 마이크로벤치마킹은 어둠의 마법과도 같음
- 유의점
- 유스케이스를 확실히 모르는 상태에서 마이크로벤치마킹하지 않기
- 그래도 마이크로벤치마킹을 해야 한다면 JMH를 이용
- 여러분이 얻은 결과를 가능한 한 많은 사람과 공유하고 회사 동료들과 함께 의논
- 항상 잘못될 가능성을 염두에 두고 여러분의 생각을 지속적으로 검증
🦴 참고