Post

마이크로벤치마킹과 통계

5. 마이크로벤치마킹과 통계

5.1 자바 성능 측정 기초

자바 코드 실행은 JIT 컴파일러, 메모리 관리, 그 밖의 자바 런타임이 제공하는 서브 시스템과 완전히 떼어놓고 생각할 수 없다

공정한 테스트인지 확인하기 위해 100,000개 숫자를 정렬하는 벤치마크 코드

이 벤치마크의 첫 번째 문제점은, JVM 웜업을 전혀 고려하지 않은 채 그냥 코드를 테스트함

JVM이 메서드 호출을 최적화하느라 시간을 보내기 때문에 어떻게 작동할지 확실히 모른다

JIT 컴파일러는 코드를 조금이라도 효율적으로 작동시키려고 호출 계층을 최적화하기 때문에 벤치마크 성능은 캡쳐 타이밍에 따라 달라짐

타이밍을 캡처하기 전에 JVM이 가동 준비를 마칠 수 있게 웜업 기간을 두는 게 좋다

보통 타이밍 세뷰 캡처하지 않은 상태로 벤치마크 대상 코드를 여러 번 반복 실행하는 식으로 JVM을 예열시킴

또 한 가지 고려할 외부 요인은 가비지 수집

가비지 수집은 원래 불확정적이어서 사람 마음대로 어찌할 도리가 없다

벤치마크에서 흔히 저지르는 또 다른 실수는, 테스트하려는 코드에서 생성된 결과를 실제로 사용하지 않는 것

또, 한번 측정한 결과로는 평균을 내도 벤치마크가 어떻게 수행됐는지 전체 사정을 속속들이 알 길이 없다

허용 오차를 구해 수집한 값의 신뢰도를 파악하는 게 좋음

동시 코드를 벤치마크할 때에는 하드웨어 설정치를 가볍게 웃돌 가능성도 있어서 하드웨어 역시 잘 살펴야 함

벤치마크 코드를 바로잡는 일은 대단히 복잡하고 여러 요인을 고려해야 함

해결 방안

  • 시스템 전체를 벤치마크

    저수준 수치는 수집하지 않거나 무시

  • 연관된 저수준의 결과를 의미있게 비교하기 위해 앞서 언급한 많은 문제를 공통 프레임워크를 이용해 처리 -> JMH

5.2 JMH 소개

5.2.1 될 수 있으면 마이크로벤치마크하지 말지어다

개발자는 작은 코드를 세세히 뜯어보며 원인을 찾으려고 하지만, 이 정도 수준으로 벤치마킹하는 건 몹시 어려울 뿐만 아니라 ‘베어 트랩’에 빠질 위험도 있다

5.2.2 휴리스틱: 마이크로벤치마킹은 언제 하나?

  • 사용 범위가 넓은 범용 라이브러리 코드를 개발
  • OpenJDK 또는 다른 자바 플랫폼 구현체를 개발
  • 지연에 극도로 민감한 코드를 개발

마이크로벤치마킹은 거의 쓸일이 없는 고급 기법이지만, 마이크로벤치마킹의 일부 기본 이론과 복잡성은 잘 알아두는 게 좋다

5.2.3 JMH 프레임워크

JMH는 자바를 비롯해 JVM을 타깃으로 하는 언어로 작성된 나노/마이크로/밀리/매크로 벤치마크를 제작,실행,분석하는 자바 도구

벤치마크 프레임워크는 컴파일 타임에 벤치마크 내용을 알 수 없으므로 동적이어야 함

리플렉션을 써서 작성한 벤치마크를 실행하는 우회 방법도 있지만, 벤치마크 실행 경로에 복잡한 JVM 서브시스템이 하나 끼어들게 됨

JMH는 벤치마크 코드에 애너테이션을 붙여 자바 소스를 추가 생성하는 식으로 작동

벤치마크 프레임워크가 유저 코드를 엄청나게 반복 호출할 경우, 루프 최적화를 수행하는 것도 문제

JMH는 벤치마크 코드가 루프 최적화에 걸리지 않을 정도로 조심스레 반복 횟수를 설정한 루프 안에 감싸 넣는 기지를 발휘함

5.2.4 벤치마크 실행

JMH에서 복잡한 부분은 유저가 볼 수 없게 숨겨져 있고, 유저는 메이븐일 이영해서 단순 벤치마크를 쉽게 설정 가능

JMH 프로젝트 명령어

1
2
3
4
5
6
mvn archetype:generate \
		-DinteractiveMode=false \
		-DarchetypeGroupId=org.openjdk.jmh \
		-DarchetypeArtifactId=jmh-java-benchmark-archetype \
		-DartifactId=test \
		-Dversion=1.0

실행시킬 벤치마크 메서드에서는 @Benchmark를 붙임

1
2
3
4
5
6
public class MyBenchmark {
  @Benchmark
  public void testMethod() {
    //
  }
}

벤치마크 실행을 설정하는 매개변수는 명령줄에 넣거나, 다음과 같이 main()메서드에 세팅

1
2
3
4
5
6
7
8
9
10
11
public class MyBenchmark {
  public static void main(String[] args) throws RuntimeException {
    Options opt = new OPtionsBuilder()
      .include(SortBenchmark.class.getSimpleName())
      .warmupIterations(100)
      .measurementIterations(5).forks(1)
      .jvmArgs("-server", "-Xms2048m", "-Xmx2048m").build();
    
    new Runner(opt).run();
  }
}

JMH 프레임워크는 상태를 제어하는 기능까지 제공

@State는 상태를 정의하는 애너테이션으로, Benchmark, Group, Thread 세 상태가 정의된 Scope 이늄을 받음

@State를 붙인 객체는 벤치마크 도중에 액세스할 수 있으므로 어떤 설정을 하는 용도로 쓸 수 있다

일반적으로 JVM은 메서드 내에서 실행된 코드가 부수 효과를 전혀 일으키지 않고 그 결과를 사용하지 않을 경우 해당 메서드를 삭제 대상으로 삼음

JMH는 이런 일이 없도록 벤치마크 메서드가 반환한 단일 결괏값을 암묵적으로 블랙홀에 할당

블랙홀은 네 가지 장치를 이용해 벤치마크에 영향을 줄 수 있는 최적화로부터 보호

  • 런타임에 죽은 코드를 제거하는 최적화를 못 하게 함
  • 반복되는 계산을 상수 폴딩(constant folding, 컴파일러가 컴파일 타임에 미리 계산 가능한 표현식을 상수로 바꾸어 처리하는 최적화 과정)하지 않게 만든다
  • 값을 읽거나 쓰는 행위가 현재 캐시 라인에 영향을 끼치는 잘못된 공유 현상을 방지
  • 쓰기 장벽으로부터 보호(쓰기 장벽에 이르면 캐시에 영향을 미치고 쓰기 전용 버퍼가 오염될 수 있음)

이하 상세 설명… 생략

5.3 JVM 성능 통계

5.3.1 오차 유형

랜덤 오차(random error)

측정 오차 또는 무관계 요인이 어떤 상관관계 없이 결과에 영향을 미침

계통 오차(systematic error)

원인을 알 수 없는 요인이 상관관계 있는 형태로 측정에 영향을 미침

정확도(accuracy)는 계통 오차의 수준을 나타내는 용어, 정확도가 높으면 계통 오차가 낮은 것

정밀도(precision)는 랜덤 오차를 나타내는 용어로서, 정밀도가 높으면 랜덤 오차가 낮은 것

계통 오차

테스트 대상 서버와 부하 테스트 서버의 거리가 멀어 계통 오차가 생길 수 있음

랜덤 오차

소프트웨어에서는 측정 툴을 못 믿을 이유가 없으므로 랜덤 오차의 근원은 오직 운영 환경뿐

허위 상관

“상관은 인과를 나타내지 않는다” -> 두 변수가 비슷하게 움직인다고 해서 이들 사이에 연결고리가 있다고 볼 수는 없다

JVM과 성능 분석 영역에서는 그럴싸해 보이는 연결고리와 상관관계만 보고 측정값 간의 인과 관계를 넘겨짚지 않도록 조심해야 한다

5.3.2 비정규 통계학

모든 관련 코드가 이미 JIT 컴파일돼서 GC 사이클이 없는 핫 패스의 존재로 인해 비정규적임

전체 분포를 단일 결괏값으로 표현하는 평균은 계산하지 말고 구간별로 샘플링을 하면?

긴 꼬리형 분포는 고동적 범위 분포(higy dynamic distribution)

고도 동적 범위에 분포된 데이터셋을 처리하는 HdrHistogram이라는 공개 라이브러리를 이용하면 좀 더 졍교한 분석 가능

5.4 통계치 해석

경험 데이터와 측정 결과는 아무 의미 없이 그냥 존재하지 않음

일반적인 측정값을 보다 유의미한 하위 구성 요소들로 분해하는 개념은 아주 유용함

5.5 마치며

  • 유스케이스를 확실히 모르는 상태에서 마이크로벤치마킹하지 마세요
  • 그래도 마이크로벤치마킹을 해야 한다면 JMH를 이용하세요
  • 얻은 결과를 가능한 한 많은 사람과 공유하고 회사 동료들과 함께 의논하세요
  • 항상 잘못될 가능성을 염두에 두고 여러분의 생각을 지속적으로 검증하세요

참조

  1. Optimizing Java(자바 최적화)
This post is licensed under CC BY 4.0 by the author.