-
🧪 JMH (Java Microbenchmark Harness)☕️ java 2023. 10. 23. 13:49
자바에서 제공하는 Random 함수에 대해 공부하는 과정에서
java.util.Random 과 java.util.concurrent.ThreadLocalRandom 의 성능을 비교해보고자 JMH라는 개념을 도입하고자 공부를 하게 되었다.
⭐ JMH
📍 JMH(Java Microbenchmark Harness)는 openjdk에서 만든 라이브러리이다.
- JVM 상에서 실행되는 코드의 성능을 측정하기 위한 벤치마크 도구이다.
📍 성능 측정
- 코드 실행 시간
- 메모리 사용량
- GC 활동
- 등등
⇒ 측정하고 수행하여 실행 결과에 대한 통계를 제공한다.
📖 벤치마크 (Benchmark)
- 컴퓨터 시스템, 소프트웨어, 하드웨어 등의 성능을 측정하고 비교하기 위해 사용되는 표준화된 기준이나 테스트
🔍 Annotation
1️⃣ @BenchmarkMode
- JMH는 벤치마크를 다양한 방법으로 수행할 수 있다.
Mode Description Throughput 초당 작업수 측정, 기본값 AverageTime 작업이 수행되는 평균 시간 측정 SampleTime 최대, 최소 시간 등 작업이 수행하는데 걸리는 시간 측정 SingleShotTime 단일 작업 수행 소요 시간 측정 All 위 모든 시간을 측정 @Benchmark @BenchmarkMode(Mode.Throughput) pulic void test() { ... }
2️⃣ @OutputTimeUnit
- 벤치마크 결과를 출력할 시간 단위를 설정할 수 있다.
- java.util.concurrent.TimeUnit 열거 타입을 통해 설정 가능
@Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MINUTES) public void testMethod() { ... }
3️⃣ @State
- JMH는 벤치마크 코드에서 필요한 일부 변수의 상태를 지정할 수 있다.
- 테스트 상황에 따라 초기화를 해야하거나 값이 유지되어야 하는 경우가 발생
Scope Description Thread Thread 별 인스턴스 생성 Benchmark 동일 테스트내 모든 Thread에서 동일 Instance를 공유 Group Thread 그룹마다 Instance를 생성 ⚠️ @State 어노테이션이 적용되는 클래스는 아래와 같은 규칙을 준수해야 함.
- 클래스는 public으로 선언되어야 한다.
- 중첩 클래스인 경우 static으로 선언해야 한다 (public static class .. )
- public 기본 생성자를 가지고 있어야 한다.
public class MyBenchmark { @State(Scope.Thread) public static class MyState { public int a = 1; public int b = 2; public int sum ; } @Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MINUTES) public void testMethod(MyState state) { state.sum = state.a + state.b; } }
3️⃣ @Setup / @TearDown
- 상태 클래스의 method에 적용가능
@Setup 은 JMH에게 벤치마크가 시작되기 전에 상태 Object를 설정하기 위해 해당 메서드를 호출되어야 함을 JMH에게 알려준다.
@TearDown 은 벤치마크가 실행된 후 상태 Object를 정리하기 위해 이 메서드를 호출해야 함을 JMH에게 알려준다.
📍 @Setup, @TearDown가 적용된 Method의 실행시간은 벤치마크 시간에 포함되지 않는다 ❌
📍 @Setup / @TearDown은 Level Argument의 설정이 가능하다
- 메소드를 언제 호출해야 할지를 JMH에게 알려준다.
Level Description Trial 벤치마크를 실행할 때 마다 한번씩 호출 Iteration 벤치마크를 반복할 때마다 한번씩 호출 Invocation 벤치마크 메소드를 호출할 때마다 호출 @State(Scope.Thread) public static class MyState { @Setup(Level.Trial) public void doSetup() { sum = 0; System.out.println("Do Setup"); } @TearDown(Level.Trial) public void doTearDown() { System.out.println("Do TearDown"); } public int a = 1; public int b = 2; public int sum ; }
🕳️ Black Hole
JMH는 벤치마크 코드에서 계산된 값을 반환하지 않아도 JVM에서 실제로 사용하고 있다고 속이는 Blackhole 방법을 제공한다.
public class MyBenchmark { @Benchmark public void testMethod(Blackhole blackhole) { int a = 1; int b = 2; int sum = a + b; blackhole.consume(sum); } }
JMH가 제공하는 Blackhole 클래스를 파라미터로 받고 해당 코드를 수행할 시
- Method에서 계산된 값을 넘겨주면 JVM에서는 계산된 값을 사용한 것으로 인식해 정확한 측정이 가능하다.
- method내 여러 계산된 값이 도출된다면 지속적으로 consume()를 호출하여 값을 전달하면 된다.
🧑🏻💻 설치 및 실행
📍 gradle project에서 JMH 라이브러리를 사용했습니다.
🐘 build.gradle
📍 plugin 추가
- gradle 버전에 따라 라이브러리 이름과 버전이 상이하다.
- jmh 깃허브 홈페이지 를 참고
plugins { id 'me.champeau.jmh' version '0.6.6' }
📍 dependencies 에서 버전 변경하기
dependencies { jmh 'org.openjdk.jmh:jmh-core:0.9' jmh 'org.openjdk.jmh:jmh-generator-annprocess:0.9' }
- 버전을 변경하는 것으로 버전을 업그레이드 할 수 있다.
📁 src/jmh 폴더 생성
- 벤치마크 소스 파일은 src/jmh 에서 찾을 수 있다.
src/jmh |- java : java sources for benchmarks |- resources : resources for benchmarks
⌨️ 실행
@State(Scope.Benchmark) public class Sample { private static final int N = 1000; @Benchmark public void testRandom(Blackhole blackhole) { Random random = new Random(); for (int i = 0; i < N; i++) { int result = random.nextInt(); blackhole.consume(result); } } @Benchmark public void testThreadLocalRandom(Blackhole blackhole) { for (int i = 0; i < N; i++) { int result = ThreadLocalRandom.current().nextInt(); blackhole.consume(result); } } }
gradle을 이용해 빌드
./gradlew jmh
/build/results/jmh/results.txt 에서 결과를 확인할 수 있다.
Benchmark Mode Cnt Score Error Units Sample.testRandom thrpt 105661.179 ops/s Sample.testThreadLocalRandom thrpt 294349.298 ops/s
참고자료
Microbenchmarking with Java | Baeldung
GitHub - openjdk/jmh: https://openjdk.org/projects/code-tools/jmh
GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle
How to Do Benchmarking in Java?
'☕️ java' 카테고리의 다른 글
👀 Unit Test 네이밍 컨벤션 (0) 2023.10.24 일급 컬렉션 (1) 2023.10.24 🆚 Random 함수 비교 (0) 2023.10.23 DAO DTO / VO (0) 2023.03.10 [Java] 실수 표현 - 고정소수점, 부동소수점 (0) 2023.03.07