8 Kasım 2022 Salı

Java Microbenchmark Harness (JMH) Kullanımı

Gradle
Şu satırı dahil ederiz
plugins {
...
  id "me.champeau.gradle.jmh" version "0.5.3"
...
}
Çalıştırmak için şöyle yaparız
gradle jmh
@State Anotasyonu
Scope.Benchmark
Açıklaması şöyle. Tek bir nesneyi multi-threaded olarak test etmek içindir
By using benchmark scope, all of the threads used on the benchmark scope will share the same object. 
@OutputTimeUnit Anotasyonu
Açıklaması şöyle.
We would like our results to be reported in microseconds, therefore we shall use @OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode Anotasyonu
Açıklaması şöyle.
On JMH, we have various benchmark modes depending on what we want to measure.

Throughput is when we want to measure the number of operations per unit of time.
AverageTime is when we want to measure the average time per operation.
SampleTime is when we want to sample the time for each operation including min, max time, and more than just the average.
SingleShotTime is when we want to measure the time for a single operation. This can help when we want to identify how the operation will do on a cold start.
All We also have the option to measure all of the above with @BenchmarkMode(Mode.All)
@Threads Anotasyonu
Kaç thread ile koşacağını belirtir

@Warmup Anotasyonu
Kaç defa ısınma yapılacağını belirtir

@Fork Anotasyonu
Benchmark testin kaç defa yapılacağını belirtir

@Measurement
Kaç yineleme yapılacağını belirtir. Çıktıda count sütununda gösterilir.

Kullanım
Örnek
Şöyle yaparız. Test sonucu nano saniyedir ve ortalaması alınır. Multi-threaded test yapar. Test 2 defa fork edilir ve her birisi 2 yineleme yapar. 
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Benchmark)
@Fork(value = 2)
@Measurement(iterations = 2)
@Warmup(iterations = 1)
public class MyBenchmark {

  @Benchmark
  public long foo() {
   ...
  }

  @Benchmark
  public long bar() {
      ...
  }
}

Örnek
Şöyle yaparız
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.GCProfiler;
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;

@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.All)
public class RateLimiterBenchmark {
  private static final int FORK_COUNT = 2;
  private static final int WARMUP_COUNT = 10;
  private static final int ITERATION_COUNT = 10;
  private static final int THREAD_COUNT = 2;
 
  public static void main(String[] args) throws RunnerException {
    Options options = new OptionsBuilder().addProfiler(GCProfiler.class).build();
     new Runner(options).run();
  }
 
  @Setup
  public void setUp() {
    ...
  }
 
  @Benchmark
  @Threads(value = THREAD_COUNT)
  @Warmup(iterations = WARMUP_COUNT)
  @Fork(value = FORK_COUNT)
  @Measurement(iterations = ITERATION_COUNT)
  public String refillPermission() {
    ...
  }
}
Çıktı şöyle. Score sütunu değeri daha düşük olan daha hızlıdır
Benchmark                                                         Mode       Cnt      Score   Error   Units
RateLimiterBenchmark.refillPermission                            thrpt        20     13.594 ± 0.217  ops/us
RateLimiterBenchmark.refillPermission                             avgt        20      0.147 ± 0.002   us/op
RateLimiterBenchmark.refillPermission                           sample  10754462      0.711 ± 0.025   us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.00    sample                  ≈ 0           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.50    sample                0.084           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.90    sample                0.125           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.95    sample                0.125           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.99    sample                0.209           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.999   sample              139.008           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p0.9999  sample              935.936           us/op
RateLimiterBenchmark.refillPermission:refillPermission·p1.00    sample            20709.376           us/op
RateLimiterBenchmark.refillPermission    
Cnt ile kaç yineleme yapıldığı belirtilir.@Measurement ile count yani iteration/yineleme değeri belirtilir. Açıklaması şöyle
Count is the number of iterations. Apart from throughput where we measure the operations by time, the rest is the time per operation. Throughput, Average, and Single shot are straightforward. Sample lists the percentiles. Error is the margin of error.
Örnek
Şöyle yaparız. Burada @Warmup ile @Setup ile işaretli kod çalıştırılıyor
import org.openjdk.jmh.annotations.Benchmark;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 20, time = 1)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@OperationsPerInvocation
public class MapBenchmark {
  private static final int SIZE = 10;

  private Map<Integer, String> mapOf;
  private Map<Integer, String> hashMap;

  @Setup
  public void setup() {
    mapOf = Map.of(
                0, "value0",
                1, "value1",
                2, "value2",
                3, "value3",
                4, "value4",
                5, "value5",
                6, "value6",
                7, "value7",
                8, "value8",
                9, "value9"
    );
    hashMap = new HashMap<>();
    hashMap.put(0, "value0");
    hashMap.put(1, "value1");
    hashMap.put(2, "value2");
    hashMap.put(3, "value3");
    hashMap.put(4, "value4");
    hashMap.put(5, "value5");
    hashMap.put(6, "value6");
    hashMap.put(7, "value7");
    hashMap.put(8, "value8");
    hashMap.put(9, "value9");
  }
  @Benchmark
  public void testMapOf(Blackhole blackhole) {
    Map<Integer, String> map = Map.of(
                0, "value0",
                1, "value1",
                2, "value2",
                3, "value3",
                4, "value4",
                5, "value5",
                6, "value6",
                7, "value7",
                8, "value8",
                9, "value9"
    );
    blackhole.consume(map);
  }
  @Benchmark
  public void testHashMap(Blackhole blackhole) {
    Map<Integer, String> hashMap = new HashMap<>();
    hashMap.put(0, "value0");
    hashMap.put(1, "value1");
    hashMap.put(2, "value2");
    hashMap.put(3, "value3");
    hashMap.put(4, "value4");
    hashMap.put(5, "value5");
    hashMap.put(6, "value6");
    hashMap.put(7, "value7");
    hashMap.put(8, "value8");
    hashMap.put(9, "value9");
    blackhole.consume(hashMap);
  }
  @Benchmark
  public void testGetMapOf() {
    for (int i = 0; i < 10; i++) {
      mapOf.get(i);
    }
  }
  @Benchmark
  public void testGetHashMap() {
    for (int i = 0; i < SIZE; i++) {
      hashMap.get(i);
    }
  }
}
Çıktı şöyle. Score sütunu değeri daha düşük olan daha hızlıdır
Benchmark                    Mode  Cnt   Score   Error  Units
MapBenchmark.testGetHashMap  avgt   20  14.999 ± 0.433  ns/op
MapBenchmark.testGetMapOf    avgt   20  16.327 ± 0.119  ns/op
MapBenchmark.testHashMap     avgt   20  84.920 ± 1.737  ns/op
MapBenchmark.testMapOf       avgt   20  83.290 ± 0.471  ns/op


Hiç yorum yok:

Yorum Gönder