JMH: Java Benchmark Tool

Asterisk
6 min readFeb 26, 2017

--

Java를 사용하여 프로그램을 문득 어떻게짜야 더 효율적인가에 대해 고민하게 되는 경우가 많다.

이 글에서 소개하려는 JMH는 이 고민을 해결해주는 Micro benchmark framework이다.
(Java뿐만 아니라 Scala, Kotlin, groovy 등 다른 JVM 언어도 지원한다.)

How to Use

JMH을 사용하여 benchmark가 위한 과정 크게 세단계로 나뉜다.

  1. benchmarking project 생성
  2. benchmark 코드 작성
  3. benchmark 수행

첫 단계인 benchmarking project 생성은 권장하는 방식인 Maven을 이용하여 진행해보자.

아래의 mvn archetype 명령을 통해 template JMH project을 생성한다.
(다른 JVM 언어에 대해 benchmark가 필요한 경우는 여기에서 맞는 artifect ID를 찾아보기 바란다.)

[code language="bash"]
mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=org.sample \
-DartifactId=test \
-Dversion=1.0
[/code]

정상적으로 완료됐다면 명령을 입력한 경로에 test라는 이름의 디렉토리가 생성되었을 것이다.
이제 test/src/main/java/org/sample/MyBenchmark.java 파일을 열어 benchmark 코드를 작성하면 된다.

[code language="java"]
package org.sample;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class MyBenchmark { @Benchmark
public void testMethod() {
// write benchmark code.
}
}
[/code]

위와 같이 @Benchmark 어노테이션이 붙은 method 단위로 측정이 가능하다.
benchmark 코드 작성이 완료됐다면 다음과 같이 install하고 실행하면 측정이 수행된다.

[code language="bash"]
mvn clean install # install
java -jar target/benchmarks.jar # run
[/code]

Settings

benckmark가 수행되면 처음에 아래와 같은 메시지가 출력되는데,
이를 통해 기본 옵션을 알 수 있다.

[code language="bash"]
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
[/code]

설정을 변경하고 싶다면 class 상단에 동명의 annotaion등을 사용하여 가능하다.

Warmup / Measurement

측정 전 웜업과 측정 회수를 조절하고 싶으면 각각 @Warmup, @Measurement 어노테이션을 사용하여 횟수 등을 조절할 수 있다.

Timeout

거의 사용하지 않지만 @Timeout 어노테이션을 통해 timeout을 설정할 수 있다.

Threads

@Threads 어노테이션은 말 그대로 사용할 쓰레드의 개수를 지정하는데 쓰인다.
가능한 최대 쓰레드를 사용하고 싶다면 Threads.MAX 혹은 -1을 사용하자.

Benchmark Mode

@BenchamrkMode 어노테이션을 사용하여 모드를 설정할 수 있으며 기본값은 Mode.Throughput 이다.
Mode.Throughput, Mode.AverageTime, Mode.SampleTime, Mode.SingleShotTime의 네가지가 있으며, Mode.All로 지정할 경우 경우 모든 Mode가 순차적으로 수행된다.

OutputTimeUnit

@OutputTimeUnit 어노테이션에 java.util.concurrent.TimeUnit enum argument로 시간 단위를 설정할 수 있고, 기본값은 ns이다.

State

@State 어노테이션으로 테스트 argument의 상태를 지정할 수 있으며
Scope.Thread는 쓰레드별로 인스턴스를 생성하고,
Scope.Benchmark는 동일한 테스트 내의 모든 쓰레드에서 동일한 인스턴스를 공유한다. (멀티스레딩 성능 테스트에 사용)
Scope.Group는 쓰레드 그룹마다 인스턴스를 생성한다.

Samples

JMH에 대한 여러 sample들은 이곳에서 확인할 수 있다.

Consideration

JMH는 OpenJDK Code Tools Project 중 하나로 운영되고 있으며,
Oracle의 JIT complier 개발자가 만들었기 때문에 타 benchmark framework보다 신뢰할 수 있다는 것이 장점이다.

하지만 일반적인 test framework처럼 기존 프로젝트 내에서 일정 unit에 대한 benchmark test가 가능한 것이 아니라 template JMH project에 추가로 benchmark 대상 코드를 작성하여 테스트해야한다는 번거로움이 단점 또한 존재한다.

참조: http://openjdk.java.net/projects/code-tools/jmh

--

--