이번 포스팅에서는 Gradle 빌드 도구를 사용해서 JMH 라이브러리를 다운받고 빌드해서 Benchmark를 구현해보려고 합니다.
환경
- IDE: IntelliJ 2023.2
- Gradle: gradle-8.2
- plugin: id "me.champeau.jmh" version "0.7.1"
Gradle 버전에 따른 plugin 확인
Gradle 버전에 따라 사용할 plugin을 적절히 골라주셔야 합니다.
plugins {
id 'java'
id "me.champeau.jmh" version "0.7.1"
}
plugins에 해당되는 Gradle 버전에 맞는 plugin을 골라주시면 됩니다.
참고로 0.6.0 이전의 플러그인 버전은 me.champeau.gradle.jmh플러그인 ID를 사용했습니다.
0.6.0 버전의 플러그인을 사용하시려면 id "me.champeau.gradle.jmh" version ... 이런식으로 작성해주셔야 합니다!
폴더 구성방법 - 꼭 지켜줘야 함!
폴더 구성은
src/jmh
|- java : 벤치마크용 자바 소스
| - 폴더 : 벤치마크할 클래스 폴더
| - 클래스 : 벤치마크 대상
|- 자원: 벤치마크를 위한 자원
꼭 지켜주셔야 합니다. 특히 파란색 네모박스를 보시면 벤치마크 클래스가 parallel 이라는 폴더안에 들어가 있는데
java 폴더 하위에 바로 벤치마크할 클래스를 두면 오류가 발생합니다!
함수 성능 측정을 위한 클래스 구성
package parallel;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime) //벤치마크 대상 함수 실행시 걸리는 시간의 평균 측정
@OutputTimeUnit(TimeUnit.MILLISECONDS) //벤치마크 결과를 밀리초 단위로 출력
@Fork(value = 2, jvmArgs = {"-Xms4G", "-Xmx4G"}) //4Gb의 힙 공간을 제공한 환경에서 두번 벤치마크를 수행해 결과의 신뢰성 확보!
public class ParallelStreamBenchmark {
private static final long N = 10_000_000L;
@Benchmark
public long sequentialSum() {
return Stream.iterate(1L, i -> i + 1).limit(N).reduce(0L, Long::sum);
}
@Benchmark
public long iterativeSum() {
long result = 0;
for (long i = 1L; i <= N; i++) {
result += i;
}
return result;
}
@TearDown(Level.Invocation) // 벤치마크가 gc 의 영향을 받지 않도록 하기 위해 벤치마크 끝날 때 마다 gc 실행!
public void tearDown() {
System.gc();
}
}
벤치마크 결과보기
Gradle에 jmh가 있는 걸 확인하실 수 있는데 여기서 jmh를 실행해보시면 아래와 같은 결과가 나옵니다.
iterativeSum 벤치마크 결과
sequentialSum의 벤치마크 결과
이렇게 결과 확인은 완료 되었습니다.
gradle에서 jmh 코드 블록을 통한 configuration option
jmh {
includes = ['some regular expression'] // include pattern (regular expression) for benchmarks to be executed
excludes = ['some regular expression'] // exclude pattern (regular expression) for benchmarks to be executed
iterations = 10 // Number of measurement iterations to do.
benchmarkMode = ['thrpt','ss'] // Benchmark mode. Available modes are: [Throughput/thrpt, AverageTime/avgt, SampleTime/sample, SingleShotTime/ss, All/all]
batchSize = 1 // Batch size: number of benchmark method calls per operation. (some benchmark modes can ignore this setting)
fork = 2 // How many times to forks a single benchmark. Use 0 to disable forking altogether
failOnError = false // Should JMH fail immediately if any benchmark had experienced the unrecoverable error?
forceGC = false // Should JMH force GC between iterations?
jvm = 'myjvm' // Custom JVM to use when forking.
jvmArgs = ['Custom JVM args to use when forking.']
jvmArgsAppend = ['Custom JVM args to use when forking (append these)']
jvmArgsPrepend =[ 'Custom JVM args to use when forking (prepend these)']
humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file
resultsFile = project.file("${project.buildDir}/reports/jmh/results.txt") // results file
operationsPerInvocation = 10 // Operations per invocation.
benchmarkParameters = [:] // Benchmark parameters.
profilers = [] // Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr, async]
timeOnIteration = '1s' // Time to spend at each measurement iteration.
resultFormat = 'CSV' // Result format type (one of CSV, JSON, NONE, SCSV, TEXT)
synchronizeIterations = false // Synchronize iterations?
threads = 4 // Number of worker threads to run with.
threadGroups = [2,3,4] //Override thread group distribution for asymmetric benchmarks.
jmhTimeout = '1s' // Timeout for benchmark iteration.
timeUnit = 'ms' // Output time unit. Available time units are: [m, s, ms, us, ns].
verbosity = 'NORMAL' // Verbosity mode. Available modes are: [SILENT, NORMAL, EXTRA]
warmup = '1s' // Time to spend at each warmup iteration.
warmupBatchSize = 10 // Warmup batch size: number of benchmark method calls per operation.
warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks.
warmupIterations = 1 // Number of warmup iterations to do.
warmupMode = 'INDI' // Warmup mode for warming up selected benchmarks. Warmup modes are: [INDI, BULK, BULK_INDI].
warmupBenchmarks = ['.*Warmup'] // Warmup benchmarks to include in the run in addition to already selected. JMH will not measure these benchmarks, but only use them for the warmup.
zip64 = true // Use ZIP64 format for bigger archives
jmhVersion = '1.36' // Specifies JMH version
includeTests = true // Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes.
duplicateClassesStrategy = DuplicatesStrategy.FAIL // Strategy to apply when encountring duplicate classes during creation of the fat jar (i.e. while executing jmhJar task)
}
출처: https://github.com/melix/jmh-gradle-plugin
설정에 기본값을 사용하고 싶지 않은 경우 build.gradle 파일에 jmh 블록을 추가하여 설정을 직접 해볼 수 있습니다.
정리
좀 더 자세한 설명이나 설정을 확인하고 싶으신 경우
https://github.com/melix/jmh-gradle-plugin
GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle
Integrates the JMH benchmarking framework with Gradle - GitHub - melix/jmh-gradle-plugin: Integrates the JMH benchmarking framework with Gradle
github.com
더 자세한 내용이 들어있으니 세부적인 설정을 원하시는 경우 참고하시면 되겠습니다.
이번 포스팅에서는 Gradle에서 JMH를 다운받고 빌드 후 벤치마크 구현을 해보았습니다.
Java Backend 개발의 경우 성능측정도 중요한 부분이므로 JMH를 통해 벤치마크하는 연습을 해보는 것도 좋을것 같아 글을 작성해 보았습니다!
'개발 도구' 카테고리의 다른 글
REST API (0) | 2024.07.07 |
---|---|
git이란? (0) | 2023.08.12 |
docker를 이용한 MySQL 설치 (0) | 2023.08.07 |
Docker 설치를 위한 wsl 설치 (0) | 2023.08.06 |
ec2 서버와 내 컴퓨터 ssh 연결 - IntelliJ IDEA에서 ssh 연결 (0) | 2023.07.25 |