本ブログ内で、JMHを使ってベンチマークの取得をいくつか実施してきましたが、実行条件は(mainメソッドの中で)OptionsBuilder
のメソッドを実行して指定してきました。
JMHには、実行条件の指定をするためのアノテーションも用意されています。
それらアノテーションを使ったコードサンプルを以下に記載します。
そのために用意されているのが以下のようなアノテーションになります
@Warmup
- 計測の前に実行されるウォームアップのイテレーション回数などを指定します@Measurement
- 計測のイテレーション回数などを指定します@BenchMarkMode
- 計測モードを指定します@Fork
- fork数を指定します@OutputTimeUnit
- 計測結果を出力する際の時間の単位を指定します@Threads
- スレッド数を指定します@Timeout
- 計測のタイムアウト値を指定します
実行条件の指定にアノテーションを使わない場合
計測の実行条件の指定にアノテーションを使わない場合、以下のようなコードを書くことで実現できます
計測を実行する側(main)で、OptionsBuilder
の各種メソッドを呼び出して実行条件を指定します
本例での実行条件は以下となっています
- ウォームアップのイテレーション=15回
- frok数=1
- 計測モード=Throughput
- 出力の時間の単位=ミリ秒
■ベンチマーク取得対象のクラス
import org.openjdk.jmh.annotations.Benchmark; public class ExampleBenchmark1 { @Benchmark public void date() { java.util.Date d = new java.util.Date(); } @Benchmark public void calndar() { java.util.Calendar c = java.util.Calendar.getInstance(); } }
■計測実行用の処理
import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Mode; 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 BenchmarkLauncher1 { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(ExampleBenchmark1.class.getName()) .warmupIterations(15) .forks(1) .mode(Mode.Throughput) .timeUnit(TimeUnit.MILLISECONDS) .build(); new Runner(opt).run(); } }
■実行結果
(一部省略)
# Warmup: 5 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 # Benchmark: org.jmh.sample.ExampleBenchmark1.calndar # Run progress: 0.00% complete, ETA 00:00:50 # Fork: 1 of 1 # Warmup Iteration 1: 861.749 ops/ms # Warmup Iteration 2: 1862.797 ops/ms # Warmup Iteration 3: 2086.060 ops/ms # Warmup Iteration 4: 2234.196 ops/ms # Warmup Iteration 5: 2243.222 ops/ms Iteration 1: 2204.222 ops/ms Iteration 2: 2244.006 ops/ms Iteration 3: 2250.402 ops/ms Iteration 4: 2195.908 ops/ms Iteration 5: 2213.133 ops/ms Iteration 6: 2208.291 ops/ms Iteration 7: 2254.084 ops/ms Iteration 8: 2203.429 ops/ms Iteration 9: 2172.140 ops/ms Iteration 10: 2217.819 ops/ms Iteration 11: 2231.414 ops/ms Iteration 12: 2209.062 ops/ms Iteration 13: 2214.501 ops/ms Iteration 14: 2171.192 ops/ms Iteration 15: 2194.317 ops/ms Iteration 16: 2238.315 ops/ms Iteration 17: 2223.204 ops/ms Iteration 18: 2210.162 ops/ms Iteration 19: 2206.248 ops/ms Iteration 20: 2226.900 ops/ms Result "calndar": 2214.437 ±(99.9%) 19.548 ops/ms [Average] (min, avg, max) = (2171.192, 2214.437, 2254.084), stdev = 22.511 CI (99.9%): [2194.890, 2233.985] (assumes normal distribution) # Warmup: 5 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 # Benchmark: org.jmh.sample.ExampleBenchmark1.date # Run progress: 50.00% complete, ETA 00:00:26 # Fork: 1 of 1 # Warmup Iteration 1: 35249.245 ops/ms # Warmup Iteration 2: 39838.538 ops/ms # Warmup Iteration 3: 43733.803 ops/ms # Warmup Iteration 4: 43663.578 ops/ms # Warmup Iteration 5: 43655.036 ops/ms Iteration 1: 43993.768 ops/ms Iteration 2: 43269.545 ops/ms Iteration 3: 43614.727 ops/ms Iteration 4: 43441.403 ops/ms Iteration 5: 43472.141 ops/ms Iteration 6: 43363.339 ops/ms Iteration 7: 44011.688 ops/ms Iteration 8: 43423.339 ops/ms Iteration 9: 43427.006 ops/ms Iteration 10: 43973.837 ops/ms Iteration 11: 43350.491 ops/ms Iteration 12: 43293.717 ops/ms Iteration 13: 43886.610 ops/ms Iteration 14: 43485.981 ops/ms Iteration 15: 42867.314 ops/ms Iteration 16: 43501.033 ops/ms Iteration 17: 43125.571 ops/ms Iteration 18: 43069.324 ops/ms Iteration 19: 43349.363 ops/ms Iteration 20: 43822.362 ops/ms Result "date": 43487.128 ±(99.9%) 274.190 ops/ms [Average] (min, avg, max) = (42867.314, 43487.128, 44011.688), stdev = 315.757 CI (99.9%): [43212.938, 43761.318] (assumes normal distribution) # Run complete. Total time: 00:00:52 Benchmark Mode Cnt Score Error Units ExampleBenchmark1.calndar thrpt 20 2214.437 ± 19.548 ops/ms ExampleBenchmark1.date thrpt 20 43487.128 ± 274.190 ops/ms
実行条件の指定にアノテーションを使う場合
計測の実行条件の指定にアノテーションを使う場合、以下のようなコードを書くことで実現できます
計測を実行する側(main)ではなく、ベンチマーク取得対象のクラス側で実行条件を記述することになります。
ここでは、「実行条件の指定にアノテーションを使わない場合」のサンプルをアノテーションを使用したコードに置き換えてみています。
■ベンチマーク取得対象のクラス
ベンチマーク取得対象のクラスに実行条件の各種アノテーションが付与されていることが分かります
import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Warmup; @Warmup(iterations=15) @OutputTimeUnit(TimeUnit.MILLISECONDS) @BenchmarkMode(Mode.Throughput) @Fork(1) public class ExampleBenchmark2 { @Benchmark public void date() { java.util.Date d = new java.util.Date(); } @Benchmark public void calndar() { java.util.Calendar c = java.util.Calendar.getInstance(); } }
■計測実行用の処理
どのクラスを対象にするかという内容以外の実行条件の指定のコードが削除されていることが分かります
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 BenchmarkLauncher2 { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(ExampleBenchmark2.class.getName()) .build(); new Runner(opt).run(); } }
■実行結果
(一部省略)
# Warmup: 5 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 # Benchmark: org.jmh.sample.ExampleBenchmark2.calndar # Run progress: 0.00% complete, ETA 00:00:50 # Fork: 1 of 1 # Warmup Iteration 1: 830.180 ops/ms # Warmup Iteration 2: 1813.431 ops/ms # Warmup Iteration 3: 2099.242 ops/ms # Warmup Iteration 4: 2243.294 ops/ms # Warmup Iteration 5: 2210.313 ops/ms Iteration 1: 2244.349 ops/ms Iteration 2: 2221.076 ops/ms Iteration 3: 2242.440 ops/ms Iteration 4: 2197.051 ops/ms Iteration 5: 2203.198 ops/ms Iteration 6: 2181.174 ops/ms Iteration 7: 2214.532 ops/ms Iteration 8: 2061.776 ops/ms Iteration 9: 2214.944 ops/ms Iteration 10: 2161.181 ops/ms Iteration 11: 1733.958 ops/ms Iteration 12: 2105.287 ops/ms Iteration 13: 2209.597 ops/ms Iteration 14: 2204.175 ops/ms Iteration 15: 2170.124 ops/ms Iteration 16: 2239.582 ops/ms Iteration 17: 2195.874 ops/ms Iteration 18: 2167.110 ops/ms Iteration 19: 2172.308 ops/ms Iteration 20: 2121.897 ops/ms Result "calndar": 2163.082 ±(99.9%) 96.588 ops/ms [Average] (min, avg, max) = (1733.958, 2163.082, 2244.349), stdev = 111.231 CI (99.9%): [2066.494, 2259.670] (assumes normal distribution) # Warmup: 5 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 # Benchmark: org.jmh.sample.ExampleBenchmark2.date # Run progress: 50.00% complete, ETA 00:00:26 # Fork: 1 of 1 # Warmup Iteration 1: 32257.483 ops/ms # Warmup Iteration 2: 36190.540 ops/ms # Warmup Iteration 3: 42043.936 ops/ms # Warmup Iteration 4: 42342.327 ops/ms # Warmup Iteration 5: 40960.967 ops/ms Iteration 1: 40321.739 ops/ms Iteration 2: 41924.838 ops/ms Iteration 3: 42757.773 ops/ms Iteration 4: 43310.839 ops/ms Iteration 5: 43586.979 ops/ms Iteration 6: 43188.242 ops/ms Iteration 7: 42846.474 ops/ms Iteration 8: 42704.245 ops/ms Iteration 9: 43567.226 ops/ms Iteration 10: 43095.412 ops/ms Iteration 11: 42914.787 ops/ms Iteration 12: 43407.346 ops/ms Iteration 13: 43654.773 ops/ms Iteration 14: 43706.141 ops/ms Iteration 15: 42202.706 ops/ms Iteration 16: 41867.707 ops/ms Iteration 17: 43452.996 ops/ms Iteration 18: 42754.032 ops/ms Iteration 19: 42233.397 ops/ms Iteration 20: 42209.165 ops/ms Result "date": 42785.341 ±(99.9%) 715.005 ops/ms [Average] (min, avg, max) = (40321.739, 42785.341, 43706.141), stdev = 823.400 CI (99.9%): [42070.336, 43500.345] (assumes normal distribution) # Run complete. Total time: 00:00:52 Benchmark Mode Cnt Score Error Units ExampleBenchmark2.calndar thrpt 20 2163.082 ± 96.588 ops/ms ExampleBenchmark2.date thrpt 20 42785.341 ± 715.005 ops/ms
先の例(アノテーションを使わないで実行条件を指定した場合)と同じ内容での計測が行われていることが分かります
ベンチマーク対象のメソッドにアノテーションを付与
アノテーションで実行条件を指定する場合、ベンチ―取得のクラスだけではなくメソッドにも付与して条件を指定するとができます
■ベンチマーク取得対象のクラス
クラスに各メソッドで共通となる条件を指定して、各メソッドに個別の条件を指定しています
import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Warmup; @BenchmarkMode(Mode.Throughput) @Fork(1) public class ExampleBenchmark3 { @Warmup(iterations=5) @Measurement(iterations=6) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Benchmark public void date() { java.util.Date d = new java.util.Date(); } @Warmup(iterations=10) @Measurement(iterations=5) @OutputTimeUnit(TimeUnit.SECONDS) @Benchmark public void calndar() { java.util.Calendar c = java.util.Calendar.getInstance(); } }
■計測実行用の処理
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 BenchmarkLauncher3 { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(ExampleBenchmark3.class.getName()) .build(); new Runner(opt).run(); } }
■実行結果
(見やすいように一部省略しています)
# Warmup: 10 iterations, 1 s each # Measurement: 5 iterations, 1 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Throughput, ops/time # Benchmark: org.jmh.sample.ExampleBenchmark3.calndar # Run progress: 0.00% complete, ETA 00:00:26 # Fork: 1 of 1 # Warmup Iteration 1: 831998.265 ops/s # Warmup Iteration 2: 1843097.120 ops/s # Warmup Iteration 3: 2135803.035 ops/s # Warmup Iteration 4: 2230390.265 ops/s # Warmup Iteration 5: 2257604.127 ops/s # Warmup Iteration 6: 2182396.572 ops/s # Warmup Iteration 7: 2194433.253 ops/s # Warmup Iteration 8: 2238500.506 ops/s # Warmup Iteration 9: 2202598.413 ops/s # Warmup Iteration 10: 2186766.643 ops/s Iteration 1: 2209673.894 ops/s Iteration 2: 2212802.599 ops/s Iteration 3: 2227286.424 ops/s Iteration 4: 2203750.319 ops/s Iteration 5: 2232765.697 ops/s Result "calndar": 2217255.786 ±(99.9%) 47195.318 ops/s [Average] (min, avg, max) = (2203750.319, 2217255.786, 2232765.697), stdev = 12256.474 CI (99.9%): [2170060.468, 2264451.105] (assumes normal distribution) # Warmup: 5 iterations, 1 s each # Measurement: 6 iterations, 1 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Throughput, ops/time # Benchmark: org.jmh.sample.ExampleBenchmark3.date # Run progress: 57.69% complete, ETA 00:00:11 # Fork: 1 of 1 # Warmup Iteration 1: 37112.900 ops/ms # Warmup Iteration 2: 39439.666 ops/ms # Warmup Iteration 3: 43483.290 ops/ms # Warmup Iteration 4: 43699.785 ops/ms # Warmup Iteration 5: 43782.495 ops/ms Iteration 1: 43773.365 ops/ms Iteration 2: 43375.840 ops/ms Iteration 3: 43685.114 ops/ms Iteration 4: 43565.948 ops/ms Iteration 5: 43899.921 ops/ms Iteration 6: 43913.513 ops/ms Result "date": 43702.283 ±(99.9%) 580.376 ops/ms [Average] (min, avg, max) = (43375.840, 43702.283, 43913.513), stdev = 206.968 CI (99.9%): [43121.908, 44282.659] (assumes normal distribution) # Run complete. Total time: 00:00:28 Benchmark Mode Cnt Score Error Units ExampleBenchmark3.calndar thrpt 5 2217255.786 ± 47195.318 ops/s ExampleBenchmark3.date thrpt 6 43702.283 ± 580.376 ops/ms
各メソッドの個別の実行条件が反映された計測になっていることが分かります