系列文章目录
jmh学习笔记-源代码编译与bench mode
jmh学习笔记-State共享对象
jmh学习笔记-State共享对象前后置方法
jmh学习笔记-代码清除
jmh学习笔记-常量折叠
jmh学习笔记-Forking分叉
jmh学习笔记-环境配置
jmh学习笔记-缓存行的处理方式
jmh学习笔记-自定义项目引入jmh
前言
在jmh基准测试结果中首先会包含一段环境配置的信息,比如
# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=60952:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 1 s each
# Measurement: 2 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 64 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.openjdk.jmh.samples.JMHSample_17_SyncIterations.test
从前面几章的章节学习中,我们对这几个参数的作用也偶有谈及,而本章则针对这些参数如何配置进行说明。
提示:以下是本篇文章正文内容,下面案例可供参考
编码方式
package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
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;
import org.openjdk.jmh.runner.options.TimeValue;
import org.openjdk.jmh.runner.options.WarmupMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class JMHSample_17_SyncIterations {
/*
* This is the another thing that is enabled in JMH by default.
*
* Suppose we have this simple benchmark.
*/
private double src;
@Benchmark
public double test() {
double s = src;
for (int i = 0; i < 1000; i++) {
s = Math.sin(s);
}
return s;
}
/**
* It turns out if you run the benchmark with multiple threads,
* the way you start and stop the worker threads seriously affects
* performance.
*
* The natural way would be to park all the threads on some sort
* of barrier, and then let them go "at once". However, that does
* not work: there are no guarantees the worker threads will start
* at the same time, meaning other worker threads are working
* in better conditions, skewing the result.
*
* The better solution would be to introduce bogus iterations,
* ramp up the threads executing the iterations, and then atomically
* shift the system to measuring stuff. The same thing can be done
* during the rampdown. This sounds complicated, but JMH already
* handles that for you.
*
* ============================== HOW TO RUN THIS TEST: ====================================
*
* You will need to oversubscribe the system to make this effect
* clearly visible; however, this effect can also be shown on the
* unsaturated systems.*
*
* Note the performance of -si false version is more flaky, even
* though it is "better". This is the false improvement, granted by
* some of the threads executing in solo. The -si true version more stable
* and coherent.
*
* -si true is enabled by default.
*
* Say, $CPU is the number of CPUs on your machine.
*
* You can run this test with:
*
* a) Via the command line:
* $ mvn clean install
* $ java -jar target/benchmarks.jar JMHSample_17 \
* -w 1s -r 1s -f 1 -t ${CPU*16} -si {true|false}
* (we requested shorter warmup/measurement iterations, single fork,
* lots of threads, and changeable "synchronize iterations" option)
*
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_17_SyncIterations.class.getSimpleName())
// 修改预热批次
.warmupIterations(2)
// 修改预热单个批次时间
.warmupTime(TimeValue.seconds(1))
// 修改测试批次
.measurementIterations(2)
// 修改测试单个批次时间
.measurementTime(TimeValue.seconds(1))
// 使用的线程数
.threads(2)
// forkJVM数
.forks(1)
// try to switch to "false"
.syncIterations(true)
.build();
new Runner(opt).run();
}
}
# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=64412:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 1 s each
# Measurement: 2 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 2 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.openjdk.jmh.samples.JMHSample_17_SyncIterations.test
# Run progress: 0.00% complete, ETA 00:00:04
# Fork: 1 of 1
# Warmup Iteration 1: 40.337 ops/ms
# Warmup Iteration 2: 52.855 ops/ms
Iteration 1: 53.471 ops/ms
Iteration 2: 56.289 ops/ms
Result "org.openjdk.jmh.samples.JMHSample_17_SyncIterations.test":
54.880 ops/ms
# Run complete. Total time: 00:00:05
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
JMHSample_17_SyncIterations.test thrpt 2 54.880 ops/ms
注解方式
package org.openjdk.jmh.samples;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
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;
import java.util.concurrent.TimeUnit;
/**
* In addition to all the command line options usable at run time,
* we have the annotations which can provide the reasonable defaults
* for the some of the benchmarks. This is very useful when you are
* dealing with lots of benchmarks, and some of them require
* special treatment.
*
* Annotation can also be placed on class, to have the effect over
* all the benchmark methods in the same class. The rule is, the
* annotation in the closest scope takes the precedence: i.e.
* the method-based annotation overrides class-based annotation,
* etc.
*/
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
public class JMHSample_20_Annotations {
double x1 = Math.PI;
@Benchmark
@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS)
public double measure() {
return Math.log(x1);
}
/**
* Note JMH honors the default annotation settings. You can always override
* the defaults via the command line or API.
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_20_Annotations.class.getSimpleName())
.build();
new Runner(opt).run();
}
}
# JMH version: 1.26
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\Program Files\Java\jdk1.8.0_121\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar=51307:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 100 ms each
# Measurement: 5 iterations, 100 ms each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.openjdk.jmh.samples.JMHSample_20_Annotations.measure
# Run progress: 0.00% complete, ETA 00:00:01
# Fork: 1 of 1
# Warmup Iteration 1: 26.695 ops/us
# Warmup Iteration 2: 25.915 ops/us
# Warmup Iteration 3: 28.234 ops/us
# Warmup Iteration 4: 27.219 ops/us
# Warmup Iteration 5: 27.778 ops/us
Iteration 1: 27.344 ops/us
Iteration 2: 27.985 ops/us
Iteration 3: 27.734 ops/us
Iteration 4: 26.769 ops/us
Iteration 5: 22.841 ops/us
Result "org.openjdk.jmh.samples.JMHSample_20_Annotations.measure":
26.535 ±(99.9%) 8.144 ops/us [Average]
(min, avg, max) = (22.841, 26.535, 27.985), stdev = 2.115
CI (99.9%): [18.391, 34.678] (assumes normal distribution)
# Run complete. Total time: 00:00:05
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
JMHSample_20_Annotations.measure thrpt 5 26.535 ± 8.144 ops/us
当然了这些注解是可以在类上面使用的,如下所示
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(5)
public class JMHSample_22_FalseSharing {
... 省略
}
总结
直接通过OptionsBuilder构造器就可以修改JMH配置参数了。当然在指定的基准测试方法上使用注解的方式将更细粒度的控制。