본문 바로가기
개발 도구

JMH(Java Microbenchmark Harness) - Gradle을 통해 Benchmark 구현

by Ahngyuho 2023. 8. 8.

이번 포스팅에서는 Gradle 빌드 도구를 사용해서 JMH 라이브러리를 다운받고 빌드해서 Benchmark를 구현해보려고 합니다.

 

환경

  • IDE: IntelliJ 2023.2
  • Gradle: gradle-8.2
  • plugin: id "me.champeau.jmh" version "0.7.1"

 

Gradle 버전에 따른 plugin 확인

Gradle 버전에 따라 사용할 plugin을 적절히 골라주셔야 합니다.

 

https://github.com/melix/jmh-gradle-plugin

build.gradle
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