public class ClassicSort {
private static final int N = 1000;
private static final int I = 150000;
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));
}
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) / (1000000000L * I));
System.out.pringln("결과: " + (1 / timePerOperation) + " op/s");
}
}
copy
는 사실상 죽은 코드이기에 JIT 컴파일러가 우리가 벤치마크하려던 것을 최적화해버릴 수도 있다.JMH는 자바를 비롯해 JVM을 타깃으로 하는 언어로 작성된 나노/마이크로/밀리/매크로 벤치마크를 제작, 실행, 분석하는 자바 도구다.
- 벤치마크 프레임워크는 컴파일 타임에 벤치마크 내용을 알 수 없기에 동적이어야 한다.
- 리플렉션을 사용해도 되지만 그러면 벤치마크 실행 경로에 JVM 서브시스템이 하나 더 끼어들게 된다.
- JMH는 벤치마크 코드에 애너테이션을 붙여 자바 소스를 추가 생성하는 식으로 작동한다.
- 벤치마크 프레임워크가 유저 코드를 반복 호출할 경우 루프 최적화를 수행
- 벤치마크 코드를 실행하는 실제 프로세스가 결과 신뢰도에 영향을 미칠 가능성이 있다.
- JMH는 벤치마크 코드가 루프 최적화에 걸리지 않을 정도로 반복 횟수를 설정한 루프 안에 감싸 넣는다.
JMH 설정을 마친 프로젝트에서 실행시킬 벤치마크 메서드에 @Benchmark
를 붙인다.
public class MyBenchmark {
@Benchmark
public void testMethod() {
// 코드 스텁
}
}
main()
메서드에 세팅한다. public class MyBenchmark {
public static void main(String[] args) throws RunnerException {
Option opt = new OptionBuilder()
.include(SortBenchmark.class.getSimpleName())
.warnupIterations(100)
.mesurementIterations(5).forks(1)
.jvmArgs("-server", "-Xms2048m", "-Xmx2048m").build();
new Runner(opt).run();
}
}
main()
메서드에 세팅한 매개변수를 오버라이드한다.@State
로 상태를 제어하는 기능도 제공한다.
Benchmark
, Group
, Thread
의 세 상태가 있는 Scope
enum을 받는다.@State
를 붙인 객체는 벤치마크 도중 엑세스할 수 있어 어떤 설정을 하는 용도로 쓸 수 있다.모든 측정은 오차를 수반하며 자바 성능 분석 시 흔히 맞딱뜨리는 주요 오차 유형이 있다.
자바 성능에서 특이점은 곧 느린 트랜잭션과 짜증난 고객
- 고객 상당수가 불만을 제기하는 상황이 아니라면 평균 응답 속도 단축이 목표가 될 일은 거의 없다.
- 현실적으로 이상적인 메서드(또는 트랜잭션) 시간 분포 그래프 (긴 꼬리형 비정규 분포)
클라이언트 오류
서버 오류
성공 요청
전체적인 응답 분포