jmh-basics: JMH tutorial module (dead-code elimination, Blackhole, constant folding, warmup/forks, state scopes, -prof gc)
Eight benchmark/demo classes, a deterministic test suite, and 12 captured output files generated by scripts/run-all.sh on JDK 25.0.5 (Temurin) with JMH 1.37. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6Fj2JNt7dRev4pZPe
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# jmh-basics
|
||||
|
||||
Companion code for the ankurm.com post *"JMH Tutorial: Writing Java Microbenchmarks That Don't Lie."*
|
||||
Module in `java-core-examples`, the Java-core series. There is deliberately no `docs/` folder: the
|
||||
deeper material lives in collapsible sections inside the post itself.
|
||||
|
||||
## Versions this was built and tested against
|
||||
|
||||
| Component | Version | Notes |
|
||||
|---|---|---|
|
||||
| JDK | 25.0.5+8 (Temurin, LTS) | Compiler blackholes were auto-detected on this JVM; see `output/02`. |
|
||||
| JMH | 1.37 | Latest GA on Maven Central; released 3 Aug 2023. |
|
||||
| JUnit Jupiter | 5.11.0 | |
|
||||
| Maven | 3.9.11 | |
|
||||
| Host | 2 vCPU shared sandbox | Read the shape of the numbers, not the third significant digit. |
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=/path/to/jdk-25
|
||||
mvn -q package -DskipTests # builds target/benchmarks.jar (shaded, runnable)
|
||||
java -cp target/classes com.ankurm.jmhbasics.NaiveTimingDemo
|
||||
java -jar target/benchmarks.jar DeadCodeBenchmark
|
||||
java -jar target/benchmarks.jar AllocationBenchmark -prof gc
|
||||
```
|
||||
|
||||
`scripts/run-all.sh` regenerates every file in `output/` (about 8 minutes on 2 vCPUs).
|
||||
`scripts/run.sh <ClassName>` runs one demo class ad hoc.
|
||||
|
||||
## What's in here
|
||||
|
||||
| File | What it shows |
|
||||
|---|---|
|
||||
| `NaiveTimingDemo.java` | The `System.nanoTime()` loop: a discarded result reports 0.000 ns/op, and the kept version changes from round to round while the JIT warms up. |
|
||||
| `DeadCodeBenchmark.java` | Empty baseline, discarded result, returned result, `Blackhole`, and constant folding: `Math.pow` of a constant folds, `Math.log` of a constant does not, and a `final` instance field IS folded like a constant (an early draft of the Javadoc claimed the opposite; the measurement and JMHSample_10 both disagreed). |
|
||||
| `LifecycleDemo.java` | Counts how often every `@Setup`/`@TearDown` level fires, by running JMH with `forks(0)` in-process. |
|
||||
| `ForkingBenchmark.java` | One shared call site, two implementations, run with `-f 0` and `-f 1` to show type-profile pollution. |
|
||||
| `StateScopeBenchmark.java` | `Scope.Thread` vs `Scope.Benchmark` with two threads. |
|
||||
| `AllocationBenchmark.java` | Escape analysis: the same `new Point(..)` allocates 0 or 24 bytes per call depending on whether it escapes. Use `-prof gc`. |
|
||||
| `SumBenchmark.java` | `@Param` over three input sizes, `@Setup` outside the measured region. |
|
||||
| `SortBenchmark.java` | Warmup trace (`-wi 10 -w 200ms`) and three benchmark modes in one run. |
|
||||
| `JmhBasicsTest.java` | Pins the deterministic claims: lifecycle counts, generated benchmark list, benchmark bodies. No timing assertions. |
|
||||
| `output/01-12` | Captured runs of all of the above. |
|
||||
|
||||
## Notes worth knowing before reading the post
|
||||
|
||||
- **Each `@BenchmarkMode` value is a separate benchmark entry.** `SortBenchmark.sortCopy` carries three modes, so the generated `META-INF/BenchmarkList` has three lines for it (21 entries for 19 methods; the test pins this).
|
||||
- **Run `LifecycleDemo` from the shaded jar**, not from `target/classes`: JMH is not on the plain classpath.
|
||||
- **Timing figures drift.** Several error margins in `output/` are wide on this shared host. The post draws conclusions only from differences far larger than the error bars.
|
||||
|
||||
## License
|
||||
|
||||
MIT - see the [repo-wide LICENSE](../LICENSE).
|
||||
@@ -0,0 +1,11 @@
|
||||
Naive System.nanoTime() timing, 20000000 iterations per round
|
||||
|
||||
round discarded (ns/op) kept (ns/op)
|
||||
1 0.688 9.170
|
||||
2 0.457 8.751
|
||||
3 0.000 7.994
|
||||
4 0.000 7.583
|
||||
5 0.000 7.502
|
||||
6 0.000 7.419
|
||||
|
||||
checksum (ignore): 1.897349195729284E9
|
||||
@@ -0,0 +1,343 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.baseline
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:02:08
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 0.670 ns/op
|
||||
# Warmup Iteration 2: 0.715 ns/op
|
||||
# Warmup Iteration 3: 0.320 ns/op
|
||||
Iteration 1: 0.327 ns/op
|
||||
Iteration 2: 0.320 ns/op
|
||||
Iteration 3: 0.386 ns/op
|
||||
Iteration 4: 0.316 ns/op
|
||||
Iteration 5: 0.320 ns/op
|
||||
|
||||
# Run progress: 6.25% complete, ETA 00:02:05
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 0.732 ns/op
|
||||
# Warmup Iteration 2: 0.719 ns/op
|
||||
# Warmup Iteration 3: 0.509 ns/op
|
||||
Iteration 1: 0.359 ns/op
|
||||
Iteration 2: 0.344 ns/op
|
||||
Iteration 3: 0.403 ns/op
|
||||
Iteration 4: 0.327 ns/op
|
||||
Iteration 5: 0.352 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.baseline":
|
||||
0.345 ±(99.9%) 0.045 ns/op [Average]
|
||||
(min, avg, max) = (0.316, 0.345, 0.403), stdev = 0.030
|
||||
CI (99.9%): [0.300, 0.390] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.deadCode
|
||||
|
||||
# Run progress: 12.50% complete, ETA 00:01:56
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 0.849 ns/op
|
||||
# Warmup Iteration 2: 0.664 ns/op
|
||||
# Warmup Iteration 3: 0.330 ns/op
|
||||
Iteration 1: 0.364 ns/op
|
||||
Iteration 2: 0.393 ns/op
|
||||
Iteration 3: 0.318 ns/op
|
||||
Iteration 4: 0.330 ns/op
|
||||
Iteration 5: 0.392 ns/op
|
||||
|
||||
# Run progress: 18.75% complete, ETA 00:01:48
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 0.719 ns/op
|
||||
# Warmup Iteration 2: 0.703 ns/op
|
||||
# Warmup Iteration 3: 0.366 ns/op
|
||||
Iteration 1: 0.364 ns/op
|
||||
Iteration 2: 0.371 ns/op
|
||||
Iteration 3: 0.334 ns/op
|
||||
Iteration 4: 0.367 ns/op
|
||||
Iteration 5: 0.409 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.deadCode":
|
||||
0.364 ±(99.9%) 0.045 ns/op [Average]
|
||||
(min, avg, max) = (0.318, 0.364, 0.409), stdev = 0.030
|
||||
CI (99.9%): [0.320, 0.409] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.logOfConstant
|
||||
|
||||
# Run progress: 25.00% complete, ETA 00:01:39
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 8.047 ns/op
|
||||
# Warmup Iteration 2: 7.451 ns/op
|
||||
# Warmup Iteration 3: 6.837 ns/op
|
||||
Iteration 1: 6.602 ns/op
|
||||
Iteration 2: 6.579 ns/op
|
||||
Iteration 3: 7.626 ns/op
|
||||
Iteration 4: 6.919 ns/op
|
||||
Iteration 5: 7.168 ns/op
|
||||
|
||||
# Run progress: 31.25% complete, ETA 00:01:31
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 7.435 ns/op
|
||||
# Warmup Iteration 2: 7.437 ns/op
|
||||
# Warmup Iteration 3: 6.543 ns/op
|
||||
Iteration 1: 6.941 ns/op
|
||||
Iteration 2: 6.895 ns/op
|
||||
Iteration 3: 6.854 ns/op
|
||||
Iteration 4: 8.124 ns/op
|
||||
Iteration 5: 6.648 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.logOfConstant":
|
||||
7.036 ±(99.9%) 0.743 ns/op [Average]
|
||||
(min, avg, max) = (6.579, 7.036, 8.124), stdev = 0.491
|
||||
CI (99.9%): [6.293, 7.779] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.powOfConstant
|
||||
|
||||
# Run progress: 37.50% complete, ETA 00:01:22
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 0.345 ns/op
|
||||
# Warmup Iteration 2: 0.408 ns/op
|
||||
# Warmup Iteration 3: 0.432 ns/op
|
||||
Iteration 1: 0.346 ns/op
|
||||
Iteration 2: 0.341 ns/op
|
||||
Iteration 3: 0.333 ns/op
|
||||
Iteration 4: 0.481 ns/op
|
||||
Iteration 5: 0.561 ns/op
|
||||
|
||||
# Run progress: 43.75% complete, ETA 00:01:14
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 0.359 ns/op
|
||||
# Warmup Iteration 2: 0.425 ns/op
|
||||
# Warmup Iteration 3: 0.340 ns/op
|
||||
Iteration 1: 0.428 ns/op
|
||||
Iteration 2: 0.349 ns/op
|
||||
Iteration 3: 0.497 ns/op
|
||||
Iteration 4: 0.576 ns/op
|
||||
Iteration 5: 0.494 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.powOfConstant":
|
||||
0.441 ±(99.9%) 0.142 ns/op [Average]
|
||||
(min, avg, max) = (0.333, 0.441, 0.576), stdev = 0.094
|
||||
CI (99.9%): [0.298, 0.583] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.powOfField
|
||||
|
||||
# Run progress: 50.00% complete, ETA 00:01:06
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 14.437 ns/op
|
||||
# Warmup Iteration 2: 15.012 ns/op
|
||||
# Warmup Iteration 3: 13.839 ns/op
|
||||
Iteration 1: 14.298 ns/op
|
||||
Iteration 2: 14.261 ns/op
|
||||
Iteration 3: 13.866 ns/op
|
||||
Iteration 4: 14.114 ns/op
|
||||
Iteration 5: 13.812 ns/op
|
||||
|
||||
# Run progress: 56.25% complete, ETA 00:00:57
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 14.805 ns/op
|
||||
# Warmup Iteration 2: 15.195 ns/op
|
||||
# Warmup Iteration 3: 13.683 ns/op
|
||||
Iteration 1: 13.712 ns/op
|
||||
Iteration 2: 15.469 ns/op
|
||||
Iteration 3: 16.961 ns/op
|
||||
Iteration 4: 13.916 ns/op
|
||||
Iteration 5: 14.715 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.powOfField":
|
||||
14.512 ±(99.9%) 1.522 ns/op [Average]
|
||||
(min, avg, max) = (13.712, 14.512, 16.961), stdev = 1.007
|
||||
CI (99.9%): [12.990, 16.035] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.powOfFinalField
|
||||
|
||||
# Run progress: 62.50% complete, ETA 00:00:49
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 0.396 ns/op
|
||||
# Warmup Iteration 2: 0.385 ns/op
|
||||
# Warmup Iteration 3: 0.325 ns/op
|
||||
Iteration 1: 0.603 ns/op
|
||||
Iteration 2: 0.332 ns/op
|
||||
Iteration 3: 0.327 ns/op
|
||||
Iteration 4: 0.333 ns/op
|
||||
Iteration 5: 0.359 ns/op
|
||||
|
||||
# Run progress: 68.75% complete, ETA 00:00:41
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 0.419 ns/op
|
||||
# Warmup Iteration 2: 0.366 ns/op
|
||||
# Warmup Iteration 3: 0.342 ns/op
|
||||
Iteration 1: 0.344 ns/op
|
||||
Iteration 2: 0.338 ns/op
|
||||
Iteration 3: 0.344 ns/op
|
||||
Iteration 4: 0.352 ns/op
|
||||
Iteration 5: 0.343 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.powOfFinalField":
|
||||
0.367 ±(99.9%) 0.126 ns/op [Average]
|
||||
(min, avg, max) = (0.327, 0.367, 0.603), stdev = 0.083
|
||||
CI (99.9%): [0.242, 0.493] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.returned
|
||||
|
||||
# Run progress: 75.00% complete, ETA 00:00:33
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 8.614 ns/op
|
||||
# Warmup Iteration 2: 7.575 ns/op
|
||||
# Warmup Iteration 3: 7.764 ns/op
|
||||
Iteration 1: 7.586 ns/op
|
||||
Iteration 2: 9.793 ns/op
|
||||
Iteration 3: 8.194 ns/op
|
||||
Iteration 4: 6.814 ns/op
|
||||
Iteration 5: 6.702 ns/op
|
||||
|
||||
# Run progress: 81.25% complete, ETA 00:00:24
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 7.652 ns/op
|
||||
# Warmup Iteration 2: 6.920 ns/op
|
||||
# Warmup Iteration 3: 7.625 ns/op
|
||||
Iteration 1: 7.713 ns/op
|
||||
Iteration 2: 6.881 ns/op
|
||||
Iteration 3: 8.208 ns/op
|
||||
Iteration 4: 6.475 ns/op
|
||||
Iteration 5: 8.223 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.returned":
|
||||
7.659 ±(99.9%) 1.521 ns/op [Average]
|
||||
(min, avg, max) = (6.475, 7.659, 9.793), stdev = 1.006
|
||||
CI (99.9%): [6.138, 9.180] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.DeadCodeBenchmark.viaBlackhole
|
||||
|
||||
# Run progress: 87.50% complete, ETA 00:00:16
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 8.460 ns/op
|
||||
# Warmup Iteration 2: 6.864 ns/op
|
||||
# Warmup Iteration 3: 7.808 ns/op
|
||||
Iteration 1: 8.439 ns/op
|
||||
Iteration 2: 10.136 ns/op
|
||||
Iteration 3: 9.039 ns/op
|
||||
Iteration 4: 8.146 ns/op
|
||||
Iteration 5: 7.843 ns/op
|
||||
|
||||
# Run progress: 93.75% complete, ETA 00:00:08
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 6.960 ns/op
|
||||
# Warmup Iteration 2: 7.100 ns/op
|
||||
# Warmup Iteration 3: 7.036 ns/op
|
||||
Iteration 1: 9.381 ns/op
|
||||
Iteration 2: 7.383 ns/op
|
||||
Iteration 3: 6.564 ns/op
|
||||
Iteration 4: 6.919 ns/op
|
||||
Iteration 5: 7.026 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.DeadCodeBenchmark.viaBlackhole":
|
||||
8.088 ±(99.9%) 1.766 ns/op [Average]
|
||||
(min, avg, max) = (6.564, 8.088, 10.136), stdev = 1.168
|
||||
CI (99.9%): [6.322, 9.853] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:02:12
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
DeadCodeBenchmark.baseline avgt 10 0.345 ± 0.045 ns/op
|
||||
DeadCodeBenchmark.deadCode avgt 10 0.364 ± 0.045 ns/op
|
||||
DeadCodeBenchmark.logOfConstant avgt 10 7.036 ± 0.743 ns/op
|
||||
DeadCodeBenchmark.powOfConstant avgt 10 0.441 ± 0.142 ns/op
|
||||
DeadCodeBenchmark.powOfField avgt 10 14.512 ± 1.522 ns/op
|
||||
DeadCodeBenchmark.powOfFinalField avgt 10 0.367 ± 0.126 ns/op
|
||||
DeadCodeBenchmark.returned avgt 10 7.659 ± 1.521 ns/op
|
||||
DeadCodeBenchmark.viaBlackhole avgt 10 8.088 ± 1.766 ns/op
|
||||
@@ -0,0 +1,9 @@
|
||||
Setup/TearDown counts for 1 warmup + 2 measurement iterations:
|
||||
|
||||
@Setup(Level.Trial) : 1
|
||||
@Setup(Level.Iteration) : 3
|
||||
@Setup(Level.Invocation) : 1039152 (one per benchmark call)
|
||||
benchmark method calls : 1039152
|
||||
@TearDown(Level.Invocation) : 1039152
|
||||
@TearDown(Level.Iteration) : 3
|
||||
@TearDown(Level.Trial) : 1
|
||||
@@ -0,0 +1,117 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_1_c1
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:33
|
||||
# Fork: N/A, test runs in the host VM
|
||||
# *** WARNING: Non-forked runs may silently omit JVM options, mess up profilers, disable compiler hints, etc. ***
|
||||
# *** WARNING: Use non-forked runs only for debugging purposes, not for actual performance runs. ***
|
||||
# Warmup Iteration 1: 1.169 ns/op
|
||||
# Warmup Iteration 2: 0.972 ns/op
|
||||
# Warmup Iteration 3: 0.891 ns/op
|
||||
Iteration 1: 0.797 ns/op
|
||||
Iteration 2: 1.058 ns/op
|
||||
Iteration 3: 0.842 ns/op
|
||||
Iteration 4: 0.892 ns/op
|
||||
Iteration 5: 0.870 ns/op
|
||||
Iteration 6: 1.155 ns/op
|
||||
Iteration 7: 0.884 ns/op
|
||||
Iteration 8: 0.800 ns/op
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_1_c1":
|
||||
0.912 ±(99.9%) 0.244 ns/op [Average]
|
||||
(min, avg, max) = (0.797, 0.912, 1.155), stdev = 0.128
|
||||
CI (99.9%): [0.668, 1.156] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_2_c2
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:22
|
||||
# Fork: N/A, test runs in the host VM
|
||||
# *** WARNING: Non-forked runs may silently omit JVM options, mess up profilers, disable compiler hints, etc. ***
|
||||
# *** WARNING: Use non-forked runs only for debugging purposes, not for actual performance runs. ***
|
||||
# Warmup Iteration 1: 1.287 ns/op
|
||||
# Warmup Iteration 2: 1.593 ns/op
|
||||
# Warmup Iteration 3: 2.833 ns/op
|
||||
Iteration 1: 3.000 ns/op
|
||||
Iteration 2: 2.695 ns/op
|
||||
Iteration 3: 2.754 ns/op
|
||||
Iteration 4: 2.416 ns/op
|
||||
Iteration 5: 2.173 ns/op
|
||||
Iteration 6: 2.287 ns/op
|
||||
Iteration 7: 2.217 ns/op
|
||||
Iteration 8: 2.100 ns/op
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_2_c2":
|
||||
2.455 ±(99.9%) 0.620 ns/op [Average]
|
||||
(min, avg, max) = (2.100, 2.455, 3.000), stdev = 0.324
|
||||
CI (99.9%): [1.835, 3.075] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_3_c1
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:11
|
||||
# Fork: N/A, test runs in the host VM
|
||||
# *** WARNING: Non-forked runs may silently omit JVM options, mess up profilers, disable compiler hints, etc. ***
|
||||
# *** WARNING: Use non-forked runs only for debugging purposes, not for actual performance runs. ***
|
||||
# Warmup Iteration 1: 2.188 ns/op
|
||||
# Warmup Iteration 2: 2.224 ns/op
|
||||
# Warmup Iteration 3: 2.666 ns/op
|
||||
Iteration 1: 2.274 ns/op
|
||||
Iteration 2: 2.184 ns/op
|
||||
Iteration 3: 2.297 ns/op
|
||||
Iteration 4: 2.201 ns/op
|
||||
Iteration 5: 2.344 ns/op
|
||||
Iteration 6: 2.517 ns/op
|
||||
Iteration 7: 2.654 ns/op
|
||||
Iteration 8: 2.302 ns/op
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_3_c1":
|
||||
2.347 ±(99.9%) 0.307 ns/op [Average]
|
||||
(min, avg, max) = (2.184, 2.347, 2.654), stdev = 0.161
|
||||
CI (99.9%): [2.040, 2.654] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:33
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
ForkingBenchmark.measure_1_c1 avgt 8 0.912 ± 0.244 ns/op
|
||||
ForkingBenchmark.measure_2_c2 avgt 8 2.455 ± 0.620 ns/op
|
||||
ForkingBenchmark.measure_3_c1 avgt 8 2.347 ± 0.307 ns/op
|
||||
@@ -0,0 +1,114 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_1_c1
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:33
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 0.752 ns/op
|
||||
# Warmup Iteration 2: 0.687 ns/op
|
||||
# Warmup Iteration 3: 0.725 ns/op
|
||||
Iteration 1: 0.727 ns/op
|
||||
Iteration 2: 0.793 ns/op
|
||||
Iteration 3: 0.743 ns/op
|
||||
Iteration 4: 0.716 ns/op
|
||||
Iteration 5: 0.728 ns/op
|
||||
Iteration 6: 0.800 ns/op
|
||||
Iteration 7: 0.657 ns/op
|
||||
Iteration 8: 0.658 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_1_c1":
|
||||
0.728 ±(99.9%) 0.101 ns/op [Average]
|
||||
(min, avg, max) = (0.657, 0.728, 0.800), stdev = 0.053
|
||||
CI (99.9%): [0.626, 0.829] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_2_c2
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:22
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 0.696 ns/op
|
||||
# Warmup Iteration 2: 0.717 ns/op
|
||||
# Warmup Iteration 3: 0.846 ns/op
|
||||
Iteration 1: 0.863 ns/op
|
||||
Iteration 2: 1.101 ns/op
|
||||
Iteration 3: 0.780 ns/op
|
||||
Iteration 4: 0.681 ns/op
|
||||
Iteration 5: 0.735 ns/op
|
||||
Iteration 6: 0.838 ns/op
|
||||
Iteration 7: 0.683 ns/op
|
||||
Iteration 8: 0.656 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_2_c2":
|
||||
0.792 ±(99.9%) 0.279 ns/op [Average]
|
||||
(min, avg, max) = (0.656, 0.792, 1.101), stdev = 0.146
|
||||
CI (99.9%): [0.513, 1.071] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 8 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.ForkingBenchmark.measure_3_c1
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:11
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 0.713 ns/op
|
||||
# Warmup Iteration 2: 0.713 ns/op
|
||||
# Warmup Iteration 3: 0.640 ns/op
|
||||
Iteration 1: 0.652 ns/op
|
||||
Iteration 2: 0.655 ns/op
|
||||
Iteration 3: 0.693 ns/op
|
||||
Iteration 4: 0.835 ns/op
|
||||
Iteration 5: 0.822 ns/op
|
||||
Iteration 6: 0.739 ns/op
|
||||
Iteration 7: 0.784 ns/op
|
||||
Iteration 8: 0.686 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.ForkingBenchmark.measure_3_c1":
|
||||
0.733 ±(99.9%) 0.140 ns/op [Average]
|
||||
(min, avg, max) = (0.652, 0.733, 0.835), stdev = 0.073
|
||||
CI (99.9%): [0.593, 0.873] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:33
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
ForkingBenchmark.measure_1_c1 avgt 8 0.728 ± 0.101 ns/op
|
||||
ForkingBenchmark.measure_2_c2 avgt 8 0.792 ± 0.279 ns/op
|
||||
ForkingBenchmark.measure_3_c1 avgt 8 0.733 ± 0.140 ns/op
|
||||
@@ -0,0 +1,97 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 2 threads, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.StateScopeBenchmark.perThreadCounter
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:32
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 7.678 ns/op
|
||||
# Warmup Iteration 2: 8.112 ns/op
|
||||
# Warmup Iteration 3: 7.597 ns/op
|
||||
Iteration 1: 7.543 ns/op
|
||||
Iteration 2: 7.587 ns/op
|
||||
Iteration 3: 7.824 ns/op
|
||||
Iteration 4: 8.115 ns/op
|
||||
Iteration 5: 8.116 ns/op
|
||||
|
||||
# Run progress: 25.00% complete, ETA 00:00:24
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 7.846 ns/op
|
||||
# Warmup Iteration 2: 7.687 ns/op
|
||||
# Warmup Iteration 3: 8.156 ns/op
|
||||
Iteration 1: 7.978 ns/op
|
||||
Iteration 2: 8.031 ns/op
|
||||
Iteration 3: 7.915 ns/op
|
||||
Iteration 4: 8.456 ns/op
|
||||
Iteration 5: 7.931 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.StateScopeBenchmark.perThreadCounter":
|
||||
7.950 ±(99.9%) 0.401 ns/op [Average]
|
||||
(min, avg, max) = (7.543, 7.950, 8.456), stdev = 0.265
|
||||
CI (99.9%): [7.549, 8.351] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 2 threads, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.StateScopeBenchmark.sharedCounter
|
||||
|
||||
# Run progress: 50.00% complete, ETA 00:00:16
|
||||
# Fork: 1 of 2
|
||||
# Warmup Iteration 1: 28.549 ns/op
|
||||
# Warmup Iteration 2: 35.212 ns/op
|
||||
# Warmup Iteration 3: 35.105 ns/op
|
||||
Iteration 1: 32.801 ns/op
|
||||
Iteration 2: 35.661 ns/op
|
||||
Iteration 3: 33.767 ns/op
|
||||
Iteration 4: 32.006 ns/op
|
||||
Iteration 5: 35.276 ns/op
|
||||
|
||||
# Run progress: 75.00% complete, ETA 00:00:08
|
||||
# Fork: 2 of 2
|
||||
# Warmup Iteration 1: 33.768 ns/op
|
||||
# Warmup Iteration 2: 35.397 ns/op
|
||||
# Warmup Iteration 3: 37.498 ns/op
|
||||
Iteration 1: 34.394 ns/op
|
||||
Iteration 2: 33.455 ns/op
|
||||
Iteration 3: 28.463 ns/op
|
||||
Iteration 4: 15.008 ns/op
|
||||
Iteration 5: 35.151 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.StateScopeBenchmark.sharedCounter":
|
||||
31.598 ±(99.9%) 9.365 ns/op [Average]
|
||||
(min, avg, max) = (15.008, 31.598, 35.661), stdev = 6.194
|
||||
CI (99.9%): [22.233, 40.963] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:33
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
StateScopeBenchmark.perThreadCounter avgt 10 7.950 ± 0.401 ns/op
|
||||
StateScopeBenchmark.sharedCounter avgt 10 31.598 ± 9.365 ns/op
|
||||
@@ -0,0 +1,203 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:18
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 7.135 ns/op
|
||||
# Warmup Iteration 2: 5.665 ns/op
|
||||
# Warmup Iteration 3: 5.072 ns/op
|
||||
Iteration 1: 5.345 ns/op
|
||||
gc.alloc.rate: 5707.275 MB/sec
|
||||
gc.alloc.rate.norm: 32.000 B/op
|
||||
gc.count: 18.000 counts
|
||||
gc.time: 21.000 ms
|
||||
|
||||
Iteration 2: 5.649 ns/op
|
||||
gc.alloc.rate: 5400.357 MB/sec
|
||||
gc.alloc.rate.norm: 32.000 B/op
|
||||
gc.count: 18.000 counts
|
||||
gc.time: 22.000 ms
|
||||
|
||||
Iteration 3: 5.596 ns/op
|
||||
gc.alloc.rate: 5448.664 MB/sec
|
||||
gc.alloc.rate.norm: 32.000 B/op
|
||||
gc.count: 17.000 counts
|
||||
gc.time: 22.000 ms
|
||||
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes":
|
||||
5.530 ±(99.9%) 2.966 ns/op [Average]
|
||||
(min, avg, max) = (5.345, 5.530, 5.649), stdev = 0.163
|
||||
CI (99.9%): [2.564, 8.496] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes:gc.alloc.rate":
|
||||
5518.765 ±(99.9%) 3010.780 MB/sec [Average]
|
||||
(min, avg, max) = (5400.357, 5518.765, 5707.275), stdev = 165.031
|
||||
CI (99.9%): [2507.986, 8529.545] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes:gc.alloc.rate.norm":
|
||||
32.000 ±(99.9%) 0.001 B/op [Average]
|
||||
(min, avg, max) = (32.000, 32.000, 32.000), stdev = 0.001
|
||||
CI (99.9%): [32.000, 32.000] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes:gc.count":
|
||||
53.000 ±(99.9%) 0.001 counts [Sum]
|
||||
(min, avg, max) = (17.000, 17.667, 18.000), stdev = 0.577
|
||||
CI (99.9%): [53.000, 53.000] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.arrayEscapes:gc.time":
|
||||
65.000 ±(99.9%) 0.001 ms [Sum]
|
||||
(min, avg, max) = (21.000, 21.667, 22.000), stdev = 0.577
|
||||
CI (99.9%): [65.000, 65.000] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.AllocationBenchmark.doesNotEscape
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:12
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 0.664 ns/op
|
||||
# Warmup Iteration 2: 0.732 ns/op
|
||||
# Warmup Iteration 3: 0.754 ns/op
|
||||
Iteration 1: 1.044 ns/op
|
||||
gc.alloc.rate: 0.007 MB/sec
|
||||
gc.alloc.rate.norm: ≈ 10⁻⁵ B/op
|
||||
gc.count: ≈ 0 counts
|
||||
|
||||
Iteration 2: 0.849 ns/op
|
||||
gc.alloc.rate: 0.007 MB/sec
|
||||
gc.alloc.rate.norm: ≈ 10⁻⁵ B/op
|
||||
gc.count: ≈ 0 counts
|
||||
|
||||
Iteration 3: 0.774 ns/op
|
||||
gc.alloc.rate: 0.007 MB/sec
|
||||
gc.alloc.rate.norm: ≈ 10⁻⁵ B/op
|
||||
gc.count: ≈ 0 counts
|
||||
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.AllocationBenchmark.doesNotEscape":
|
||||
0.889 ±(99.9%) 2.544 ns/op [Average]
|
||||
(min, avg, max) = (0.774, 0.889, 1.044), stdev = 0.139
|
||||
CI (99.9%): [≈ 0, 3.432] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.doesNotEscape:gc.alloc.rate":
|
||||
0.007 ±(99.9%) 0.001 MB/sec [Average]
|
||||
(min, avg, max) = (0.007, 0.007, 0.007), stdev = 0.001
|
||||
CI (99.9%): [0.007, 0.007] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.doesNotEscape:gc.alloc.rate.norm":
|
||||
≈ 10⁻⁵ B/op
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.doesNotEscape:gc.count":
|
||||
≈ 0 counts
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 3 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.AllocationBenchmark.escapes
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:06
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 6.088 ns/op
|
||||
# Warmup Iteration 2: 4.657 ns/op
|
||||
# Warmup Iteration 3: 4.507 ns/op
|
||||
Iteration 1: 4.959 ns/op
|
||||
gc.alloc.rate: 4609.827 MB/sec
|
||||
gc.alloc.rate.norm: 24.000 B/op
|
||||
gc.count: 16.000 counts
|
||||
gc.time: 20.000 ms
|
||||
|
||||
Iteration 2: 4.719 ns/op
|
||||
gc.alloc.rate: 4843.713 MB/sec
|
||||
gc.alloc.rate.norm: 24.000 B/op
|
||||
gc.count: 17.000 counts
|
||||
gc.time: 21.000 ms
|
||||
|
||||
Iteration 3: 4.898 ns/op
|
||||
gc.alloc.rate: 4671.068 MB/sec
|
||||
gc.alloc.rate.norm: 24.000 B/op
|
||||
gc.count: 16.000 counts
|
||||
gc.time: 18.000 ms
|
||||
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.AllocationBenchmark.escapes":
|
||||
4.859 ±(99.9%) 2.271 ns/op [Average]
|
||||
(min, avg, max) = (4.719, 4.859, 4.959), stdev = 0.124
|
||||
CI (99.9%): [2.588, 7.130] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.escapes:gc.alloc.rate":
|
||||
4708.203 ±(99.9%) 2212.682 MB/sec [Average]
|
||||
(min, avg, max) = (4609.827, 4708.203, 4843.713), stdev = 121.285
|
||||
CI (99.9%): [2495.521, 6920.885] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.escapes:gc.alloc.rate.norm":
|
||||
24.000 ±(99.9%) 0.001 B/op [Average]
|
||||
(min, avg, max) = (24.000, 24.000, 24.000), stdev = 0.001
|
||||
CI (99.9%): [24.000, 24.000] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.escapes:gc.count":
|
||||
49.000 ±(99.9%) 0.001 counts [Sum]
|
||||
(min, avg, max) = (16.000, 16.333, 17.000), stdev = 0.577
|
||||
CI (99.9%): [49.000, 49.000] (assumes normal distribution)
|
||||
|
||||
Secondary result "com.ankurm.jmhbasics.AllocationBenchmark.escapes:gc.time":
|
||||
59.000 ±(99.9%) 0.001 ms [Sum]
|
||||
(min, avg, max) = (18.000, 19.667, 21.000), stdev = 1.528
|
||||
CI (99.9%): [59.000, 59.000] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:19
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
AllocationBenchmark.arrayEscapes avgt 3 5.530 ± 2.966 ns/op
|
||||
AllocationBenchmark.arrayEscapes:gc.alloc.rate avgt 3 5518.765 ± 3010.780 MB/sec
|
||||
AllocationBenchmark.arrayEscapes:gc.alloc.rate.norm avgt 3 32.000 ± 0.001 B/op
|
||||
AllocationBenchmark.arrayEscapes:gc.count avgt 3 53.000 counts
|
||||
AllocationBenchmark.arrayEscapes:gc.time avgt 3 65.000 ms
|
||||
AllocationBenchmark.doesNotEscape avgt 3 0.889 ± 2.544 ns/op
|
||||
AllocationBenchmark.doesNotEscape:gc.alloc.rate avgt 3 0.007 ± 0.001 MB/sec
|
||||
AllocationBenchmark.doesNotEscape:gc.alloc.rate.norm avgt 3 ≈ 10⁻⁵ B/op
|
||||
AllocationBenchmark.doesNotEscape:gc.count avgt 3 ≈ 0 counts
|
||||
AllocationBenchmark.escapes avgt 3 4.859 ± 2.271 ns/op
|
||||
AllocationBenchmark.escapes:gc.alloc.rate avgt 3 4708.203 ± 2212.682 MB/sec
|
||||
AllocationBenchmark.escapes:gc.alloc.rate.norm avgt 3 24.000 ± 0.001 B/op
|
||||
AllocationBenchmark.escapes:gc.count avgt 3 49.000 counts
|
||||
AllocationBenchmark.escapes:gc.time avgt 3 59.000 ms
|
||||
@@ -0,0 +1,108 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.SumBenchmark.sum
|
||||
# Parameters: (size = 10)
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:24
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 11.152 ns/op
|
||||
# Warmup Iteration 2: 9.349 ns/op
|
||||
# Warmup Iteration 3: 11.239 ns/op
|
||||
Iteration 1: 11.426 ns/op
|
||||
Iteration 2: 11.575 ns/op
|
||||
Iteration 3: 11.528 ns/op
|
||||
Iteration 4: 8.986 ns/op
|
||||
Iteration 5: 10.091 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SumBenchmark.sum":
|
||||
10.721 ±(99.9%) 4.427 ns/op [Average]
|
||||
(min, avg, max) = (8.986, 10.721, 11.575), stdev = 1.150
|
||||
CI (99.9%): [6.294, 15.149] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.SumBenchmark.sum
|
||||
# Parameters: (size = 1000)
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:16
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 72.906 ns/op
|
||||
# Warmup Iteration 2: 61.956 ns/op
|
||||
# Warmup Iteration 3: 60.801 ns/op
|
||||
Iteration 1: 53.346 ns/op
|
||||
Iteration 2: 57.255 ns/op
|
||||
Iteration 3: 52.866 ns/op
|
||||
Iteration 4: 54.738 ns/op
|
||||
Iteration 5: 54.117 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SumBenchmark.sum":
|
||||
54.465 ±(99.9%) 6.611 ns/op [Average]
|
||||
(min, avg, max) = (52.866, 54.465, 57.255), stdev = 1.717
|
||||
CI (99.9%): [47.854, 61.075] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.SumBenchmark.sum
|
||||
# Parameters: (size = 100000)
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:08
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 8474.836 ns/op
|
||||
# Warmup Iteration 2: 7431.944 ns/op
|
||||
# Warmup Iteration 3: 7347.101 ns/op
|
||||
Iteration 1: 7377.289 ns/op
|
||||
Iteration 2: 7499.047 ns/op
|
||||
Iteration 3: 7046.908 ns/op
|
||||
Iteration 4: 7454.035 ns/op
|
||||
Iteration 5: 7662.632 ns/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SumBenchmark.sum":
|
||||
7407.982 ±(99.9%) 875.072 ns/op [Average]
|
||||
(min, avg, max) = (7046.908, 7407.982, 7662.632), stdev = 227.254
|
||||
CI (99.9%): [6532.910, 8283.055] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:25
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark (size) Mode Cnt Score Error Units
|
||||
SumBenchmark.sum 10 avgt 5 10.721 ± 4.427 ns/op
|
||||
SumBenchmark.sum 1000 avgt 5 54.465 ± 6.611 ns/op
|
||||
SumBenchmark.sum 100000 avgt 5 7407.982 ± 875.072 ns/op
|
||||
@@ -0,0 +1,52 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 10 iterations, 200 ms each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.SortBenchmark.sortCopy
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:07
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 9.502 us/op
|
||||
# Warmup Iteration 2: 7.662 us/op
|
||||
# Warmup Iteration 3: 4.997 us/op
|
||||
# Warmup Iteration 4: 4.586 us/op
|
||||
# Warmup Iteration 5: 4.362 us/op
|
||||
# Warmup Iteration 6: 6.260 us/op
|
||||
# Warmup Iteration 7: 6.648 us/op
|
||||
# Warmup Iteration 8: 5.701 us/op
|
||||
# Warmup Iteration 9: 4.546 us/op
|
||||
# Warmup Iteration 10: 4.359 us/op
|
||||
Iteration 1: 5.683 us/op
|
||||
Iteration 2: 6.121 us/op
|
||||
Iteration 3: 4.753 us/op
|
||||
Iteration 4: 4.963 us/op
|
||||
Iteration 5: 5.553 us/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SortBenchmark.sortCopy":
|
||||
5.414 ±(99.9%) 2.138 us/op [Average]
|
||||
(min, avg, max) = (4.753, 5.414, 6.121), stdev = 0.555
|
||||
CI (99.9%): [3.277, 7.552] (assumes normal distribution)
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:07
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
SortBenchmark.sortCopy avgt 5 5.414 ± 2.138 us/op
|
||||
@@ -0,0 +1,186 @@
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 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: com.ankurm.jmhbasics.SortBenchmark.sortCopy
|
||||
|
||||
# Run progress: 0.00% complete, ETA 00:00:24
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 0.167 ops/us
|
||||
# Warmup Iteration 2: 0.198 ops/us
|
||||
# Warmup Iteration 3: 0.199 ops/us
|
||||
Iteration 1: 0.186 ops/us
|
||||
Iteration 2: 0.200 ops/us
|
||||
Iteration 3: 0.185 ops/us
|
||||
Iteration 4: 0.156 ops/us
|
||||
Iteration 5: 0.174 ops/us
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SortBenchmark.sortCopy":
|
||||
0.180 ±(99.9%) 0.063 ops/us [Average]
|
||||
(min, avg, max) = (0.156, 0.180, 0.200), stdev = 0.016
|
||||
CI (99.9%): [0.117, 0.243] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Average time, time/op
|
||||
# Benchmark: com.ankurm.jmhbasics.SortBenchmark.sortCopy
|
||||
|
||||
# Run progress: 33.33% complete, ETA 00:00:16
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 6.346 us/op
|
||||
# Warmup Iteration 2: 5.192 us/op
|
||||
# Warmup Iteration 3: 5.520 us/op
|
||||
Iteration 1: 5.367 us/op
|
||||
Iteration 2: 5.330 us/op
|
||||
Iteration 3: 5.343 us/op
|
||||
Iteration 4: 6.901 us/op
|
||||
Iteration 5: 5.716 us/op
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SortBenchmark.sortCopy":
|
||||
5.731 ±(99.9%) 2.592 us/op [Average]
|
||||
(min, avg, max) = (5.330, 5.731, 6.901), stdev = 0.673
|
||||
CI (99.9%): [3.140, 8.323] (assumes normal distribution)
|
||||
|
||||
|
||||
# JMH version: 1.37
|
||||
# VM version: JDK 25.0.5, OpenJDK 64-Bit Server VM, 25.0.5+8-LTS
|
||||
# VM invoker: /tmp/tools/jdk-25.0.5+8/bin/java
|
||||
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
|
||||
# Warmup: 3 iterations, 1 s each
|
||||
# Measurement: 5 iterations, 1 s each
|
||||
# Timeout: 10 min per iteration
|
||||
# Threads: 1 thread, will synchronize iterations
|
||||
# Benchmark mode: Sampling time
|
||||
# Benchmark: com.ankurm.jmhbasics.SortBenchmark.sortCopy
|
||||
|
||||
# Run progress: 66.67% complete, ETA 00:00:08
|
||||
# Fork: 1 of 1
|
||||
# Warmup Iteration 1: 9.133 ±(99.9%) 1.896 us/op
|
||||
# Warmup Iteration 2: 7.146 ±(99.9%) 0.975 us/op
|
||||
# Warmup Iteration 3: 6.063 ±(99.9%) 0.366 us/op
|
||||
Iteration 1: 8.042 ±(99.9%) 1.788 us/op
|
||||
p0.00: 3.428 us/op
|
||||
p0.50: 5.960 us/op
|
||||
p0.90: 6.808 us/op
|
||||
p0.95: 7.432 us/op
|
||||
p0.99: 28.379 us/op
|
||||
p0.999: 65.180 us/op
|
||||
p0.9999: 4063.431 us/op
|
||||
p1.00: 4067.328 us/op
|
||||
|
||||
Iteration 2: 6.504 ±(99.9%) 1.880 us/op
|
||||
p0.00: 3.420 us/op
|
||||
p0.50: 4.480 us/op
|
||||
p0.90: 6.352 us/op
|
||||
p0.95: 6.744 us/op
|
||||
p0.99: 25.392 us/op
|
||||
p0.999: 51.584 us/op
|
||||
p0.9999: 4057.682 us/op
|
||||
p1.00: 11911.168 us/op
|
||||
|
||||
Iteration 3: 5.223 ±(99.9%) 0.336 us/op
|
||||
p0.00: 3.424 us/op
|
||||
p0.50: 4.280 us/op
|
||||
p0.90: 5.776 us/op
|
||||
p0.95: 6.272 us/op
|
||||
p0.99: 20.843 us/op
|
||||
p0.999: 62.631 us/op
|
||||
p0.9999: 953.847 us/op
|
||||
p1.00: 1581.056 us/op
|
||||
|
||||
Iteration 4: 7.875 ±(99.9%) 1.578 us/op
|
||||
p0.00: 3.428 us/op
|
||||
p0.50: 6.104 us/op
|
||||
p0.90: 6.808 us/op
|
||||
p0.95: 7.906 us/op
|
||||
p0.99: 35.626 us/op
|
||||
p0.999: 195.147 us/op
|
||||
p0.9999: 4194.614 us/op
|
||||
p1.00: 8192.000 us/op
|
||||
|
||||
Iteration 5: 5.648 ±(99.9%) 0.311 us/op
|
||||
p0.00: 3.416 us/op
|
||||
p0.50: 4.504 us/op
|
||||
p0.90: 6.416 us/op
|
||||
p0.95: 6.856 us/op
|
||||
p0.99: 26.271 us/op
|
||||
p0.999: 69.818 us/op
|
||||
p0.9999: 362.418 us/op
|
||||
p1.00: 2050.048 us/op
|
||||
|
||||
|
||||
|
||||
Result "com.ankurm.jmhbasics.SortBenchmark.sortCopy":
|
||||
N = 112693
|
||||
mean = 6.532 ±(99.9%) 0.580 us/op
|
||||
|
||||
Histogram, us/op:
|
||||
[ 0.000, 1250.000) = 112674
|
||||
[ 1250.000, 2500.000) = 8
|
||||
[ 2500.000, 3750.000) = 0
|
||||
[ 3750.000, 5000.000) = 9
|
||||
[ 5000.000, 6250.000) = 0
|
||||
[ 6250.000, 7500.000) = 0
|
||||
[ 7500.000, 8750.000) = 1
|
||||
[ 8750.000, 10000.000) = 0
|
||||
[10000.000, 11250.000) = 0
|
||||
[11250.000, 12500.000) = 1
|
||||
[12500.000, 13750.000) = 0
|
||||
[13750.000, 15000.000) = 0
|
||||
[15000.000, 16250.000) = 0
|
||||
[16250.000, 17500.000) = 0
|
||||
[17500.000, 18750.000) = 0
|
||||
|
||||
Percentiles, us/op:
|
||||
p(0.0000) = 3.416 us/op
|
||||
p(50.0000) = 5.352 us/op
|
||||
p(90.0000) = 6.544 us/op
|
||||
p(95.0000) = 7.024 us/op
|
||||
p(99.0000) = 26.944 us/op
|
||||
p(99.9000) = 91.151 us/op
|
||||
p(99.9900) = 3620.126 us/op
|
||||
p(99.9990) = 11439.057 us/op
|
||||
p(99.9999) = 11911.168 us/op
|
||||
p(100.0000) = 11911.168 us/op
|
||||
|
||||
|
||||
# Run complete. Total time: 00:00:25
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
|
||||
extra caution when trusting the results, look into the generated code to check the benchmark still
|
||||
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
|
||||
different JVMs are already problematic, the performance difference caused by different Blackhole
|
||||
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
|
||||
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
SortBenchmark.sortCopy thrpt 5 0.180 ± 0.063 ops/us
|
||||
SortBenchmark.sortCopy avgt 5 5.731 ± 2.592 us/op
|
||||
SortBenchmark.sortCopy sample 112693 6.532 ± 0.580 us/op
|
||||
SortBenchmark.sortCopy:p0.00 sample 3.416 us/op
|
||||
SortBenchmark.sortCopy:p0.50 sample 5.352 us/op
|
||||
SortBenchmark.sortCopy:p0.90 sample 6.544 us/op
|
||||
SortBenchmark.sortCopy:p0.95 sample 7.024 us/op
|
||||
SortBenchmark.sortCopy:p0.99 sample 26.944 us/op
|
||||
SortBenchmark.sortCopy:p0.999 sample 91.151 us/op
|
||||
SortBenchmark.sortCopy:p0.9999 sample 3620.126 us/op
|
||||
SortBenchmark.sortCopy:p1.00 sample 11911.168 us/op
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
Supported profilers:
|
||||
cl: Classloader profiling via standard MBeans
|
||||
comp: JIT compiler profiling via standard MBeans
|
||||
gc: GC profiling via standard MBeans
|
||||
jfr: Java Flight Recorder profiler
|
||||
mempool: Memory pool/footprint profiling via standard MBeans
|
||||
pauses: Pauses profiler
|
||||
safepoints: Safepoints profiler
|
||||
stack: Simple and naive Java stack profiler
|
||||
|
||||
Unsupported profilers:
|
||||
async: <none>
|
||||
Unable to load async-profiler. Ensure asyncProfiler library is on LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (Mac OS), or -Djava.library.path. Alternatively, point to explicit library location with -prof async:libPath=<path>.
|
||||
dtraceasm: <none>
|
||||
[sudo: dtrace: command not found
|
||||
]
|
||||
perf: <none>
|
||||
[Cannot run program "perf": Exec failed, error: 2 (No such file or directory) ]
|
||||
perfasm: <none>
|
||||
[Cannot run program "perf": Exec failed, error: 2 (No such file or directory) ]
|
||||
perfc2c: <none>
|
||||
[Cannot run program "perf": Exec failed, error: 2 (No such file or directory) ]
|
||||
perfnorm: <none>
|
||||
[Cannot run program "perf": Exec failed, error: 2 (No such file or directory) ]
|
||||
xperfasm: <none>
|
||||
Unable to start the profiler. Try running JMH as Administrator, and ensure that previous profiling session is stopped. Use 'xperf -stop' to stop the active profiling session.: [Cannot run program "xperf": Exec failed, error: 2 (No such file or directory) ]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-------------------------------------------------------------------------------
|
||||
Test set: com.ankurm.jmhbasics.JmhBasicsTest
|
||||
-------------------------------------------------------------------------------
|
||||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.314 s -- in com.ankurm.jmhbasics.JmhBasicsTest
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ankurm</groupId>
|
||||
<artifactId>java-core-examples</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>jmh-basics</artifactId>
|
||||
<name>jmh-basics</name>
|
||||
<description>JMH tutorial: dead-code elimination, Blackhole, constant folding, warmup and forks, state scopes, profilers, and the mistakes that make microbenchmarks lie.</description>
|
||||
|
||||
<properties>
|
||||
<jmh.version>1.37</jmh.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>benchmarks</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<release>25</release>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates every file in output/. Takes roughly 6-8 minutes on a 2-vCPU host.
|
||||
# Timing numbers drift from run to run; read the shape (which is bigger, by roughly how much).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
clean() { grep -vE 'JAVA_TOOL_OPTIONS|^Picked up|^WARNING|^# VM options:' || true; }
|
||||
JAR="java -jar target/benchmarks.jar"
|
||||
|
||||
mvn -q -B package -DskipTests 2>&1 | clean
|
||||
|
||||
java -cp target/classes com.ankurm.jmhbasics.NaiveTimingDemo 2>&1 | clean > output/01-naive-timing.txt
|
||||
$JAR DeadCodeBenchmark 2>&1 | clean > output/02-deadcode-jmh.txt
|
||||
java -cp target/benchmarks.jar com.ankurm.jmhbasics.LifecycleDemo 2>&1 | clean > output/03-lifecycle.txt
|
||||
$JAR ForkingBenchmark -f 0 -i 8 2>&1 | clean > output/04-forking-f0.txt
|
||||
$JAR ForkingBenchmark -f 1 -i 8 2>&1 | clean > output/05-forking-f1.txt
|
||||
$JAR StateScopeBenchmark 2>&1 | clean > output/06-state-scope.txt
|
||||
$JAR AllocationBenchmark -prof gc 2>&1 | clean > output/07-allocation-gc.txt
|
||||
$JAR SumBenchmark 2>&1 | clean > output/08-sum-param.txt
|
||||
$JAR SortBenchmark -f 1 -wi 10 -w 200ms -i 5 -r 1s -bm avgt -tu us 2>&1 | clean > output/09-warmup-trace.txt
|
||||
$JAR SortBenchmark -f 1 2>&1 | clean > output/10-sort-modes.txt
|
||||
$JAR -lprof 2>&1 | clean > output/11-list-profilers.txt
|
||||
|
||||
mvn -q -B test 2>&1 | clean || true
|
||||
cp target/surefire-reports/com.ankurm.jmhbasics.JmhBasicsTest.txt output/12-tests.txt
|
||||
echo "Regenerated output/01-12."
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run one demo class ad hoc, e.g.: ./scripts/run.sh NaiveTimingDemo
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mvn -q -B package -DskipTests
|
||||
java -cp target/benchmarks.jar "com.ankurm.jmhbasics.$1"
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
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.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Every method below contains the source text {@code new Point(...)} or {@code new int[4]}.
|
||||
* Whether an object is really allocated depends on whether it can escape - and only a profiler
|
||||
* can tell you. Run with the built-in GC profiler:
|
||||
*
|
||||
* java -jar target/benchmarks.jar AllocationBenchmark -prof gc
|
||||
*
|
||||
* Read the {@code gc.alloc.rate.norm} column: bytes allocated per benchmark call.
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 3, time = 1)
|
||||
@Fork(1)
|
||||
@State(Scope.Thread)
|
||||
public class AllocationBenchmark {
|
||||
|
||||
record Point(int x, int y) { }
|
||||
|
||||
int a = 3;
|
||||
int b = 4;
|
||||
|
||||
/** The Point never leaves this method: escape analysis can replace it with two locals. */
|
||||
@Benchmark
|
||||
public int doesNotEscape() {
|
||||
Point p = new Point(a, b);
|
||||
return p.x() + p.y();
|
||||
}
|
||||
|
||||
/** The Point is returned to JMH, so it escapes and must exist on the heap. */
|
||||
@Benchmark
|
||||
public Point escapes() {
|
||||
return new Point(a, b);
|
||||
}
|
||||
|
||||
/** An array that is returned: header + 4 ints, rounded up to 8-byte alignment. */
|
||||
@Benchmark
|
||||
public int[] arrayEscapes() {
|
||||
return new int[4];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
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.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Eight benchmarks around Math.log and Math.pow. Only some of them measure what their name says.
|
||||
*
|
||||
* baseline empty method: the cost of JMH's own loop. Everything else is read against it.
|
||||
* deadCode computes the log and throws it away -> candidate for dead-code elimination
|
||||
* returned computes the log and returns it -> JMH consumes it for you
|
||||
* viaBlackhole computes the log and hands it to Blackhole.consume
|
||||
* logOfConstant log of a compile-time constant -> NOT folded on this JDK (see the post)
|
||||
* powOfConstant pow of a compile-time constant -> folded away by the JIT
|
||||
* powOfFinalField pow of a final instance field -> ALSO folded (JMHSample_10 lists this as a mistake)
|
||||
* powOfField pow of a plain mutable field -> the honest, un-foldable version
|
||||
*
|
||||
* Run: java -jar target/benchmarks.jar DeadCodeBenchmark
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(2)
|
||||
@State(Scope.Thread)
|
||||
public class DeadCodeBenchmark {
|
||||
|
||||
static final double CONSTANT = Math.PI;
|
||||
|
||||
double field = Math.PI; // plain mutable field: the JIT cannot assume its value
|
||||
final double finalField = Math.PI; // final: measured below to be folded exactly like a literal constant
|
||||
|
||||
@Benchmark
|
||||
public void baseline() {
|
||||
// intentionally empty
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void deadCode() {
|
||||
Math.log(field); // result discarded
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public double returned() {
|
||||
return Math.log(field); // JMH feeds the return value to a Blackhole
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void viaBlackhole(Blackhole bh) {
|
||||
bh.consume(Math.log(field));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public double logOfConstant() {
|
||||
return Math.log(CONSTANT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public double powOfConstant() {
|
||||
return Math.pow(CONSTANT, 10);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public double powOfFinalField() {
|
||||
return Math.pow(finalField, 10);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public double powOfField() {
|
||||
return Math.pow(field, 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
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 java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Why forks exist. The three benchmarks do the same work through one call site, {@code measure}.
|
||||
*
|
||||
* measure_1_c1 and measure_3_c1 pass an {@link AddOne}; measure_2_c2 passes an {@link AddTwo}.
|
||||
*
|
||||
* Fork count is deliberately NOT set here - choose it on the command line:
|
||||
*
|
||||
* java -jar target/benchmarks.jar ForkingBenchmark -f 0 # all three share one JVM
|
||||
* java -jar target/benchmarks.jar ForkingBenchmark -f 1 # each benchmark gets a fresh JVM
|
||||
*
|
||||
* With -f 0 the first benchmark teaches HotSpot "this call site only ever sees AddOne". The second
|
||||
* benchmark then breaks that assumption, and the third inherits the damage.
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 3, time = 1)
|
||||
@State(Scope.Thread)
|
||||
public class ForkingBenchmark {
|
||||
|
||||
public interface Step { int apply(int x); }
|
||||
|
||||
public static final class AddOne implements Step {
|
||||
@Override public int apply(int x) { return x + 1; }
|
||||
}
|
||||
|
||||
public static final class AddTwo implements Step {
|
||||
@Override public int apply(int x) { return x + 2; }
|
||||
}
|
||||
|
||||
final Step one = new AddOne();
|
||||
final Step two = new AddTwo();
|
||||
|
||||
/** The single shared call site whose type profile gets polluted. */
|
||||
public int measure(Step step) {
|
||||
int acc = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
acc = step.apply(acc);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int measure_1_c1() { return measure(one); }
|
||||
|
||||
@Benchmark
|
||||
public int measure_2_c2() { return measure(two); }
|
||||
|
||||
@Benchmark
|
||||
public int measure_3_c1() { return measure(one); }
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
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 java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Counts how often each {@code @Setup}/{@code @TearDown} level fires.
|
||||
*
|
||||
* It runs JMH with forks(0) on purpose: the benchmark then runs inside this JVM, so plain static
|
||||
* counters are visible here afterwards. With a normal forked run they would live in another JVM.
|
||||
*
|
||||
* Configuration: 1 warmup iteration + 2 measurement iterations, 50 ms each.
|
||||
*
|
||||
* Run: java -cp target/benchmarks.jar com.ankurm.jmhbasics.LifecycleDemo (after mvn package)
|
||||
*/
|
||||
public class LifecycleDemo {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class Lifecycle {
|
||||
static final AtomicLong trialSetup = new AtomicLong();
|
||||
static final AtomicLong iterationSetup = new AtomicLong();
|
||||
static final AtomicLong invocationSetup = new AtomicLong();
|
||||
static final AtomicLong invocationTearDown = new AtomicLong();
|
||||
static final AtomicLong iterationTearDown = new AtomicLong();
|
||||
static final AtomicLong trialTearDown = new AtomicLong();
|
||||
static final AtomicLong benchmarkCalls = new AtomicLong();
|
||||
|
||||
static void reset() {
|
||||
trialSetup.set(0);
|
||||
iterationSetup.set(0);
|
||||
invocationSetup.set(0);
|
||||
invocationTearDown.set(0);
|
||||
iterationTearDown.set(0);
|
||||
trialTearDown.set(0);
|
||||
benchmarkCalls.set(0);
|
||||
}
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setupTrial() { trialSetup.incrementAndGet(); }
|
||||
|
||||
@Setup(Level.Iteration)
|
||||
public void setupIteration() { iterationSetup.incrementAndGet(); }
|
||||
|
||||
@Setup(Level.Invocation)
|
||||
public void setupInvocation() { invocationSetup.incrementAndGet(); }
|
||||
|
||||
@TearDown(Level.Invocation)
|
||||
public void tearDownInvocation() { invocationTearDown.incrementAndGet(); }
|
||||
|
||||
@TearDown(Level.Iteration)
|
||||
public void tearDownIteration() { iterationTearDown.incrementAndGet(); }
|
||||
|
||||
@TearDown(Level.Trial)
|
||||
public void tearDownTrial() { trialTearDown.incrementAndGet(); }
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void work(Lifecycle state) {
|
||||
Lifecycle.benchmarkCalls.incrementAndGet();
|
||||
}
|
||||
|
||||
/** Runs the benchmark in-process and returns after JMH has finished. */
|
||||
public static void run() throws RunnerException {
|
||||
Lifecycle.reset();
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(LifecycleDemo.class.getName() + ".work")
|
||||
.forks(0)
|
||||
.warmupIterations(1)
|
||||
.warmupTime(TimeValue.milliseconds(50))
|
||||
.measurementIterations(2)
|
||||
.measurementTime(TimeValue.milliseconds(50))
|
||||
.verbosity(org.openjdk.jmh.runner.options.VerboseMode.SILENT)
|
||||
.build();
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
run();
|
||||
System.out.println("Setup/TearDown counts for 1 warmup + 2 measurement iterations:");
|
||||
System.out.println();
|
||||
System.out.printf("@Setup(Level.Trial) : %d%n", Lifecycle.trialSetup.get());
|
||||
System.out.printf("@Setup(Level.Iteration) : %d%n", Lifecycle.iterationSetup.get());
|
||||
System.out.printf("@Setup(Level.Invocation) : %d (one per benchmark call)%n", Lifecycle.invocationSetup.get());
|
||||
System.out.printf("benchmark method calls : %d%n", Lifecycle.benchmarkCalls.get());
|
||||
System.out.printf("@TearDown(Level.Invocation) : %d%n", Lifecycle.invocationTearDown.get());
|
||||
System.out.printf("@TearDown(Level.Iteration) : %d%n", Lifecycle.iterationTearDown.get());
|
||||
System.out.printf("@TearDown(Level.Trial) : %d%n", Lifecycle.trialTearDown.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
/**
|
||||
* The benchmark most people write first: a loop around System.nanoTime().
|
||||
*
|
||||
* It "works" in the sense that it prints numbers. The numbers are wrong in two independent ways,
|
||||
* and this program shows both on one run:
|
||||
*
|
||||
* 1. The loop that discards its result can be removed by the JIT, so it reports a cost close to zero.
|
||||
* 2. The same code reports a different cost every round, because the JIT is still compiling it.
|
||||
*
|
||||
* Run: java -cp target/classes com.ankurm.jmhbasics.NaiveTimingDemo
|
||||
*/
|
||||
public class NaiveTimingDemo {
|
||||
|
||||
static final int N = 20_000_000;
|
||||
|
||||
/** Result thrown away - nothing observable depends on the loop. */
|
||||
static void discard() {
|
||||
for (int i = 1; i <= N; i++) {
|
||||
Math.log(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Result kept and printed - the loop must really run. */
|
||||
static double keep() {
|
||||
double sum = 0;
|
||||
for (int i = 1; i <= N; i++) {
|
||||
sum += Math.log(i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Naive System.nanoTime() timing, " + N + " iterations per round");
|
||||
System.out.println();
|
||||
System.out.printf("%-7s %-22s %-22s%n", "round", "discarded (ns/op)", "kept (ns/op)");
|
||||
double sink = 0;
|
||||
for (int round = 1; round <= 6; round++) {
|
||||
long t0 = System.nanoTime();
|
||||
discard();
|
||||
long t1 = System.nanoTime();
|
||||
sink += keep();
|
||||
long t2 = System.nanoTime();
|
||||
System.out.printf("%-7d %-22.3f %-22.3f%n", round,
|
||||
(t1 - t0) / (double) N, (t2 - t1) / (double) N);
|
||||
}
|
||||
System.out.println();
|
||||
// Printing the sink is what stops the JIT removing keep() as well.
|
||||
System.out.println("checksum (ignore): " + sink);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* One benchmark, used for two demonstrations - neither fixes the iteration counts here:
|
||||
*
|
||||
* 1. Warmup trace: java -jar target/benchmarks.jar SortBenchmark -f 1 -wi 10 -w 200ms -i 3 -r 1s
|
||||
* JMH prints every warmup iteration, so you can watch the time per call fall as the JIT works.
|
||||
*
|
||||
* 2. Benchmark modes in one run: the method carries three @BenchmarkMode values, so one run reports
|
||||
* throughput, average time and a sampled latency distribution with percentiles.
|
||||
*/
|
||||
@BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime})
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@State(Scope.Thread)
|
||||
public class SortBenchmark {
|
||||
|
||||
int[] original;
|
||||
|
||||
@Setup
|
||||
public void fill() {
|
||||
Random random = new Random(7);
|
||||
original = new int[1000];
|
||||
for (int i = 0; i < original.length; i++) {
|
||||
original[i] = random.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int[] sortCopy() {
|
||||
int[] copy = original.clone(); // sorting in place would make every call after the first trivial
|
||||
Arrays.sort(copy);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
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.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Same operation, two state scopes, two benchmark threads.
|
||||
*
|
||||
* Scope.Thread every benchmark thread gets its own PerThread instance: no sharing, no contention
|
||||
* Scope.Benchmark all benchmark threads share ONE Shared instance: they fight over one cache line
|
||||
*
|
||||
* The scope you pick decides whether you are measuring "increment a counter" or "increment a counter
|
||||
* while another core is also incrementing it".
|
||||
*
|
||||
* Run: java -jar target/benchmarks.jar StateScopeBenchmark
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(2)
|
||||
@Threads(2)
|
||||
public class StateScopeBenchmark {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class Shared {
|
||||
final AtomicLong counter = new AtomicLong();
|
||||
}
|
||||
|
||||
@State(Scope.Thread)
|
||||
public static class PerThread {
|
||||
final AtomicLong counter = new AtomicLong();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long sharedCounter(Shared state) {
|
||||
return state.counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long perThreadCounter(PerThread state) {
|
||||
return state.counter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
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.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* {@code @Param} runs the same benchmark at several input sizes, and {@code @Setup} builds the data
|
||||
* outside the measured region. Without {@code @Param} you would copy-paste three benchmark methods.
|
||||
*
|
||||
* Run: java -jar target/benchmarks.jar SumBenchmark
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 3, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(1)
|
||||
@State(Scope.Thread)
|
||||
public class SumBenchmark {
|
||||
|
||||
@Param({"10", "1000", "100000"})
|
||||
public int size;
|
||||
|
||||
int[] data;
|
||||
|
||||
@Setup
|
||||
public void fill() {
|
||||
Random random = new Random(42); // fixed seed: the same data on every run
|
||||
data = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
data[i] = random.nextInt(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long sum() {
|
||||
long total = 0;
|
||||
for (int v : data) {
|
||||
total += v;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.ankurm.jmhbasics;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Pins the claims that are deterministic. Timing ratios are NOT asserted here: on a shared 2-vCPU
|
||||
* host they are not stable enough to fail a build on. They live in output/ as captured runs.
|
||||
*/
|
||||
class JmhBasicsTest {
|
||||
|
||||
@Test
|
||||
void annotationProcessorGeneratedBenchmarkList() throws Exception {
|
||||
try (java.io.InputStream in = getClass().getResourceAsStream("/META-INF/BenchmarkList")) {
|
||||
assertTrue(in != null, "annotation processor did not generate META-INF/BenchmarkList");
|
||||
String list = new String(in.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
|
||||
assertTrue(list.contains("com.ankurm.jmhbasics.jmh_generated.DeadCodeBenchmark_deadCode_jmhTest"));
|
||||
assertTrue(list.contains("com.ankurm.jmhbasics.jmh_generated.SumBenchmark_sum_jmhTest"));
|
||||
assertTrue(list.contains("com.ankurm.jmhbasics.jmh_generated.ForkingBenchmark_measure_2_c2_jmhTest"));
|
||||
// 8 DeadCode + 1 Lifecycle + 3 Forking + 2 StateScope + 3 Allocation + 1 Sum
|
||||
// + 3 Sort: one entry per @BenchmarkMode value, so SortBenchmark.sortCopy counts three times
|
||||
assertEquals(21, list.lines().filter(l -> l.startsWith("JMH S")).count());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lifecycleLevelsFireExactlyAsDocumented() throws Exception {
|
||||
LifecycleDemo.run();
|
||||
assertEquals(1, LifecycleDemo.Lifecycle.trialSetup.get(), "Trial setup: once per fork");
|
||||
assertEquals(1, LifecycleDemo.Lifecycle.trialTearDown.get());
|
||||
assertEquals(3, LifecycleDemo.Lifecycle.iterationSetup.get(), "Iteration setup: 1 warmup + 2 measurement");
|
||||
assertEquals(3, LifecycleDemo.Lifecycle.iterationTearDown.get());
|
||||
long calls = LifecycleDemo.Lifecycle.benchmarkCalls.get();
|
||||
assertTrue(calls > 0);
|
||||
assertEquals(calls, LifecycleDemo.Lifecycle.invocationSetup.get(), "Invocation setup: once per call");
|
||||
assertEquals(calls, LifecycleDemo.Lifecycle.invocationTearDown.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void benchmarkBodiesComputeTheRightAnswers() {
|
||||
SumBenchmark sum = new SumBenchmark();
|
||||
sum.size = 1000;
|
||||
sum.fill();
|
||||
long expected = 0;
|
||||
for (int v : sum.data) expected += v;
|
||||
assertEquals(expected, sum.sum());
|
||||
|
||||
SortBenchmark sort = new SortBenchmark();
|
||||
sort.fill();
|
||||
int[] sorted = sort.sortCopy();
|
||||
for (int i = 1; i < sorted.length; i++) assertTrue(sorted[i - 1] <= sorted[i]);
|
||||
// The original must be untouched, otherwise later iterations would sort an already-sorted array.
|
||||
boolean originalUnsorted = false;
|
||||
for (int i = 1; i < sort.original.length; i++) if (sort.original[i - 1] > sort.original[i]) originalUnsorted = true;
|
||||
assertTrue(originalUnsorted);
|
||||
|
||||
ForkingBenchmark f = new ForkingBenchmark();
|
||||
assertEquals(10, f.measure_1_c1());
|
||||
assertEquals(20, f.measure_2_c2());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user