├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src └── jmh │ └── java │ ├── io │ └── markovic │ │ └── jmh │ │ └── experiments │ │ ├── SystemNanosVsMillis.java │ │ ├── UnparkVsCondvar.java │ │ ├── VarArgsOverhead.java │ │ ├── InstantOverhead.java │ │ ├── IteratorPerf.java │ │ ├── LambdaOverhead.java │ │ ├── Time4JMonoticClock.java │ │ ├── Streams.java │ │ └── IteratorGC.java │ └── org │ └── openjdk │ └── jmh │ └── samples │ ├── JMHSample_04_DefaultState.java │ ├── JMHSample_20_Annotations.java │ ├── JMHSample_27_Params.java │ ├── JMHSample_08_DeadCode.java │ ├── JMHSample_18_Control.java │ ├── JMHSample_06_FixtureLevel.java │ ├── JMHSample_01_HelloWorld.java │ ├── JMHSample_10_ConstantFold.java │ ├── JMHSample_09_Blackholes.java │ ├── JMHSample_13_RunToRun.java │ ├── JMHSample_23_AuxCounters.java │ ├── JMHSample_05_StateFixtures.java │ ├── JMHSample_21_ConsumeCPU.java │ ├── JMHSample_30_Interrupts.java │ ├── JMHSample_17_SyncIterations.java │ ├── JMHSample_24_Inheritance.java │ ├── JMHSample_16_CompilerControl.java │ ├── JMHSample_03_States.java │ ├── JMHSample_28_BlackholeHelpers.java │ ├── JMHSample_26_BatchSize.java │ ├── JMHSample_15_Asymmetric.java │ ├── JMHSample_11_Loops.java │ ├── JMHSample_31_InfraParams.java │ ├── JMHSample_32_BulkWarmup.java │ ├── JMHSample_33_SecurityManager.java │ ├── JMHSample_12_Forking.java │ ├── JMHSample_07_FixtureLevelInvocation.java │ ├── JMHSample_29_StatesDAG.java │ ├── JMHSample_37_CacheAccess.java │ └── JMHSample_36_BranchPrediction.java ├── gradlew.bat └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Valloric/jmh-playground/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java 2 | *.class 3 | *.jar 4 | *.war 5 | *.ear 6 | 7 | # Eclipse 8 | .project 9 | .classpath 10 | .settings 11 | 12 | # Idea 13 | .idea 14 | *.iml 15 | *.iws 16 | *.ipr 17 | 18 | # OS 19 | Thumbs.db 20 | .DS_Store 21 | 22 | # Gradle 23 | .gradle 24 | !gradle-wrapper.jar 25 | 26 | # Maven 27 | target 28 | 29 | # Build 30 | out 31 | build 32 | bin 33 | 34 | # Other 35 | *.log 36 | *.swp 37 | *.bak -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/SystemNanosVsMillis.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import org.openjdk.jmh.annotations.Benchmark; 5 | import org.openjdk.jmh.annotations.BenchmarkMode; 6 | import org.openjdk.jmh.annotations.Fork; 7 | import org.openjdk.jmh.annotations.Measurement; 8 | import org.openjdk.jmh.annotations.Mode; 9 | import org.openjdk.jmh.annotations.OutputTimeUnit; 10 | import org.openjdk.jmh.annotations.Warmup; 11 | 12 | @BenchmarkMode(Mode.AverageTime) 13 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 14 | @Warmup(iterations = 5) 15 | @Measurement(iterations = 5) 16 | @Fork(2) 17 | public class SystemNanosVsMillis { 18 | @Benchmark 19 | public long currentTimeMillis() { 20 | return System.currentTimeMillis(); 21 | } 22 | 23 | @Benchmark 24 | public long nanoTime() { 25 | return System.nanoTime(); 26 | } 27 | 28 | // RESULTS! 29 | // 30 | // SystemNanosVsMillis.currentTimeMillis avgt 10 20.649 ± 0.042 ns/op 31 | // SystemNanosVsMillis.nanoTime avgt 10 19.191 ± 0.061 ns/op 32 | // 33 | // This was measured on a system running Linux 4.9; CPU: Intel Core i5-4590. 34 | // Seems like the concern that System.nanoTime() is much slower than 35 | // currentTimeMillis is unfounded! (Well, at least on Linux.) 36 | } 37 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_04_DefaultState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.Scope; 35 | import org.openjdk.jmh.annotations.State; 36 | import org.openjdk.jmh.runner.Runner; 37 | import org.openjdk.jmh.runner.RunnerException; 38 | import org.openjdk.jmh.runner.options.Options; 39 | import org.openjdk.jmh.runner.options.OptionsBuilder; 40 | 41 | /* 42 | * Fortunately, in many cases you just need a single state object. 43 | * In that case, we can mark the benchmark instance itself to be 44 | * the @State. Then, we can reference its own fields as any 45 | * Java program does. 46 | */ 47 | 48 | @State(Scope.Thread) 49 | public class JMHSample_04_DefaultState { 50 | 51 | double x = Math.PI; 52 | 53 | @Benchmark 54 | public void measure() { 55 | x++; 56 | } 57 | 58 | /* 59 | * ============================== HOW TO RUN THIS TEST: ==================================== 60 | * 61 | * You can see the benchmark runs as usual. 62 | * 63 | * You can run this test: 64 | * 65 | * a) Via the command line: 66 | * $ mvn clean install 67 | * $ java -jar target/benchmarks.jar JMHSample_04 -wi 5 -i 5 -f 1 68 | * (we requested 5 warmup/measurement iterations, single fork) 69 | * 70 | * b) Via the Java API: 71 | * (see the JMH homepage for possible caveats when running from IDE: 72 | * http://openjdk.java.net/projects/code-tools/jmh/) 73 | */ 74 | 75 | public static void main(String[] args) throws RunnerException { 76 | Options opt = new OptionsBuilder() 77 | .include(JMHSample_04_DefaultState.class.getSimpleName()) 78 | .warmupIterations(5) 79 | .measurementIterations(5) 80 | .forks(1) 81 | .build(); 82 | 83 | new Runner(opt).run(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/UnparkVsCondvar.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.Iterator; 4 | import java.util.concurrent.ConcurrentLinkedQueue; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.locks.Condition; 7 | import java.util.concurrent.locks.Lock; 8 | import java.util.concurrent.locks.LockSupport; 9 | import java.util.concurrent.locks.ReentrantLock; 10 | import org.openjdk.jmh.annotations.Benchmark; 11 | import org.openjdk.jmh.annotations.BenchmarkMode; 12 | import org.openjdk.jmh.annotations.Fork; 13 | import org.openjdk.jmh.annotations.Measurement; 14 | import org.openjdk.jmh.annotations.Mode; 15 | import org.openjdk.jmh.annotations.OutputTimeUnit; 16 | import org.openjdk.jmh.annotations.Param; 17 | import org.openjdk.jmh.annotations.Scope; 18 | import org.openjdk.jmh.annotations.Setup; 19 | import org.openjdk.jmh.annotations.State; 20 | import org.openjdk.jmh.annotations.TearDown; 21 | import org.openjdk.jmh.annotations.Warmup; 22 | 23 | @BenchmarkMode(Mode.AverageTime) 24 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 25 | @Warmup(iterations = 5) 26 | @Measurement(iterations = 5) 27 | @State(Scope.Thread) 28 | @Fork(2) 29 | public class UnparkVsCondvar { 30 | @Param({"1", "10", "100"}) 31 | public int numThreads; 32 | 33 | ConcurrentLinkedQueue waiters = new ConcurrentLinkedQueue<>(); 34 | volatile boolean canDie = false; 35 | Lock lock = new ReentrantLock(); 36 | Condition condition = lock.newCondition(); 37 | 38 | @Setup 39 | public void setup() { 40 | for (int i = 0; i < numThreads; i++) { 41 | Thread thread = new Thread(() -> { 42 | lock.lock(); 43 | try { 44 | while (!canDie) { 45 | condition.await(); 46 | } 47 | } catch (InterruptedException e) { 48 | return; 49 | } finally { 50 | lock.unlock(); 51 | } 52 | }); 53 | thread.setDaemon(true); 54 | thread.start(); 55 | } 56 | 57 | for (int i = 0; i < numThreads; i++) { 58 | Thread thread = new Thread(() -> { 59 | boolean wasInterrupted = false; 60 | waiters.add(Thread.currentThread()); 61 | while (!canDie) { 62 | LockSupport.park(this); 63 | // Ignore interrupts while waiting 64 | if (Thread.interrupted()) { 65 | wasInterrupted = true; 66 | } 67 | } 68 | 69 | // Restore interrupt status on exit 70 | if (wasInterrupted) { 71 | Thread.currentThread().interrupt(); 72 | } 73 | }); 74 | thread.setDaemon(true); 75 | thread.start(); 76 | } 77 | } 78 | 79 | @TearDown 80 | public void teardown() { 81 | canDie = true; 82 | } 83 | 84 | @Benchmark 85 | public void condVarSignalAll() { 86 | lock.lock(); 87 | try { 88 | condition.signalAll(); 89 | } finally { 90 | lock.unlock(); 91 | } 92 | } 93 | 94 | @Benchmark 95 | public void unparkAll() { 96 | Iterator it = waiters.iterator(); 97 | while (it.hasNext()) { 98 | Thread thread = it.next(); 99 | LockSupport.unpark(thread); 100 | } 101 | } 102 | 103 | // RESULTS! 104 | // 105 | // Benchmark (numThreads) Mode Cnt Score Error Units 106 | // UnparkVsCondvar.condVarSignalAll 1 avgt 10 98.859 ± 3.107 ns/op 107 | // UnparkVsCondvar.condVarSignalAll 10 avgt 10 321.160 ± 30.316 ns/op 108 | // UnparkVsCondvar.condVarSignalAll 100 avgt 10 3783.884 ± 730.839 ns/op 109 | // UnparkVsCondvar.unparkAll 1 avgt 10 177.295 ± 14.871 ns/op 110 | // UnparkVsCondvar.unparkAll 10 avgt 10 16058.986 ± 406.366 ns/op 111 | // UnparkVsCondvar.unparkAll 100 avgt 10 153492.890 ± 4900.969 ns/op 112 | // 113 | // Don't understimate condvars, they're really fast. 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_20_Annotations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.Fork; 35 | import org.openjdk.jmh.annotations.Measurement; 36 | import org.openjdk.jmh.annotations.OutputTimeUnit; 37 | import org.openjdk.jmh.annotations.Scope; 38 | import org.openjdk.jmh.annotations.State; 39 | import org.openjdk.jmh.annotations.Warmup; 40 | import org.openjdk.jmh.runner.Runner; 41 | import org.openjdk.jmh.runner.RunnerException; 42 | import org.openjdk.jmh.runner.options.Options; 43 | import org.openjdk.jmh.runner.options.OptionsBuilder; 44 | 45 | import java.util.concurrent.TimeUnit; 46 | 47 | @State(Scope.Thread) 48 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 49 | @Fork(1) 50 | public class JMHSample_20_Annotations { 51 | 52 | double x1 = Math.PI; 53 | 54 | /* 55 | * In addition to all the command line options usable at run time, 56 | * we have the annotations which can provide the reasonable defaults 57 | * for the some of the benchmarks. This is very useful when you are 58 | * dealing with lots of benchmarks, and some of them require 59 | * special treatment. 60 | * 61 | * Annotation can also be placed on class, to have the effect over 62 | * all the benchmark methods in the same class. The rule is, the 63 | * annotation in the closest scope takes the precedence: i.e. 64 | * the method-based annotation overrides class-based annotation, 65 | * etc. 66 | */ 67 | 68 | @Benchmark 69 | @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) 70 | @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) 71 | public double measure() { 72 | return Math.log(x1); 73 | } 74 | 75 | /* 76 | * ============================== HOW TO RUN THIS TEST: ==================================== 77 | * 78 | * Note JMH honors the default annotation settings. You can always override 79 | * the defaults via the command line or API. 80 | * 81 | * You can run this test: 82 | * 83 | * a) Via the command line: 84 | * $ mvn clean install 85 | * $ java -jar target/benchmarks.jar JMHSample_20 86 | * 87 | * b) Via the Java API: 88 | * (see the JMH homepage for possible caveats when running from IDE: 89 | * http://openjdk.java.net/projects/code-tools/jmh/) 90 | */ 91 | 92 | public static void main(String[] args) throws RunnerException { 93 | Options opt = new OptionsBuilder() 94 | .include(JMHSample_20_Annotations.class.getSimpleName()) 95 | .build(); 96 | 97 | new Runner(opt).run(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_27_Params.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.OutputTimeUnit; 39 | import org.openjdk.jmh.annotations.Param; 40 | import org.openjdk.jmh.annotations.Scope; 41 | import org.openjdk.jmh.annotations.State; 42 | import org.openjdk.jmh.annotations.Warmup; 43 | import org.openjdk.jmh.runner.Runner; 44 | import org.openjdk.jmh.runner.RunnerException; 45 | import org.openjdk.jmh.runner.options.Options; 46 | import org.openjdk.jmh.runner.options.OptionsBuilder; 47 | 48 | import java.math.BigInteger; 49 | import java.util.concurrent.TimeUnit; 50 | 51 | @BenchmarkMode(Mode.AverageTime) 52 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 54 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 55 | @Fork(1) 56 | @State(Scope.Benchmark) 57 | public class JMHSample_27_Params { 58 | 59 | /** 60 | * In many cases, the experiments require walking the configuration space 61 | * for a benchmark. This is needed for additional control, or investigating 62 | * how the workload performance changes with different settings. 63 | */ 64 | 65 | @Param({"1", "31", "65", "101", "103"}) 66 | public int arg; 67 | 68 | @Param({"0", "1", "2", "4", "8", "16", "32"}) 69 | public int certainty; 70 | 71 | @Benchmark 72 | public boolean bench() { 73 | return BigInteger.valueOf(arg).isProbablePrime(certainty); 74 | } 75 | 76 | /* 77 | * ============================== HOW TO RUN THIS TEST: ==================================== 78 | * 79 | * Note the performance is different with different parameters. 80 | * 81 | * You can run this test: 82 | * 83 | * a) Via the command line: 84 | * $ mvn clean install 85 | * $ java -jar target/benchmarks.jar JMHSample_27 86 | * 87 | * You can juggle parameters through the command line, e.g. with "-p arg=41,42" 88 | * 89 | * b) Via the Java API: 90 | * (see the JMH homepage for possible caveats when running from IDE: 91 | * http://openjdk.java.net/projects/code-tools/jmh/) 92 | */ 93 | 94 | public static void main(String[] args) throws RunnerException { 95 | Options opt = new OptionsBuilder() 96 | .include(JMHSample_27_Params.class.getSimpleName()) 97 | // .param("arg", "41", "42") // Use this to selectively constrain/override parameters 98 | .build(); 99 | 100 | new Runner(opt).run(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_08_DeadCode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | import java.util.concurrent.TimeUnit; 40 | 41 | @State(Scope.Thread) 42 | @BenchmarkMode(Mode.AverageTime) 43 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 | public class JMHSample_08_DeadCode { 45 | 46 | /* 47 | * The downfall of many benchmarks is Dead-Code Elimination (DCE): compilers 48 | * are smart enough to deduce some computations are redundant and eliminate 49 | * them completely. If the eliminated part was our benchmarked code, we are 50 | * in trouble. 51 | * 52 | * Fortunately, JMH provides the essential infrastructure to fight this 53 | * where appropriate: returning the result of the computation will ask JMH 54 | * to deal with the result to limit dead-code elimination (returned results 55 | * are implicitly consumed by Blackholes, see JMHSample_09_Blackholes). 56 | */ 57 | 58 | private double x = Math.PI; 59 | 60 | @Benchmark 61 | public void baseline() { 62 | // do nothing, this is a baseline 63 | } 64 | 65 | @Benchmark 66 | public void measureWrong() { 67 | // This is wrong: result is not used and the entire computation is optimized away. 68 | Math.log(x); 69 | } 70 | 71 | @Benchmark 72 | public double measureRight() { 73 | // This is correct: the result is being used. 74 | return Math.log(x); 75 | } 76 | 77 | /* 78 | * ============================== HOW TO RUN THIS TEST: ==================================== 79 | * 80 | * You can see the unrealistically fast calculation in with measureWrong(), 81 | * while realistic measurement with measureRight(). 82 | * 83 | * You can run this test: 84 | * 85 | * a) Via the command line: 86 | * $ mvn clean install 87 | * $ java -jar target/benchmarks.jar JMHSample_08 -wi 5 -i 5 -f 1 88 | * (we requested 5 warmup/measurement iterations, single fork) 89 | * 90 | * b) Via the Java API: 91 | * (see the JMH homepage for possible caveats when running from IDE: 92 | * http://openjdk.java.net/projects/code-tools/jmh/) 93 | */ 94 | 95 | public static void main(String[] args) throws RunnerException { 96 | Options opt = new OptionsBuilder() 97 | .include(JMHSample_08_DeadCode.class.getSimpleName()) 98 | .warmupIterations(5) 99 | .measurementIterations(5) 100 | .forks(1) 101 | .build(); 102 | 103 | new Runner(opt).run(); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_18_Control.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.Group; 35 | import org.openjdk.jmh.annotations.Scope; 36 | import org.openjdk.jmh.annotations.State; 37 | import org.openjdk.jmh.infra.Control; 38 | import org.openjdk.jmh.runner.Runner; 39 | import org.openjdk.jmh.runner.RunnerException; 40 | import org.openjdk.jmh.runner.options.Options; 41 | import org.openjdk.jmh.runner.options.OptionsBuilder; 42 | 43 | import java.util.concurrent.atomic.AtomicBoolean; 44 | 45 | @State(Scope.Group) 46 | public class JMHSample_18_Control { 47 | 48 | /* 49 | * Sometimes you need the tap into the harness mind to get the info 50 | * on the transition change. For this, we have the experimental state object, 51 | * Control, which is updated by JMH as we go. 52 | */ 53 | 54 | /* 55 | * In this example, we want to estimate the ping-pong speed for the simple 56 | * AtomicBoolean. Unfortunately, doing that in naive manner will livelock 57 | * one of the threads, because the executions of ping/pong are not paired 58 | * perfectly. We need the escape hatch to terminate the loop if threads 59 | * are about to leave the measurement. 60 | */ 61 | 62 | public final AtomicBoolean flag = new AtomicBoolean(); 63 | 64 | @Benchmark 65 | @Group("pingpong") 66 | public void ping(Control cnt) { 67 | while (!cnt.stopMeasurement && !flag.compareAndSet(false, true)) { 68 | // this body is intentionally left blank 69 | } 70 | } 71 | 72 | @Benchmark 73 | @Group("pingpong") 74 | public void pong(Control cnt) { 75 | while (!cnt.stopMeasurement && !flag.compareAndSet(true, false)) { 76 | // this body is intentionally left blank 77 | } 78 | } 79 | 80 | /* 81 | * ============================== HOW TO RUN THIS TEST: ==================================== 82 | * 83 | * You can run this test: 84 | * 85 | * a) Via the command line: 86 | * $ mvn clean install 87 | * $ java -jar target/benchmarks.jar JMHSample_18 -wi 1 -i 5 -t 2 -f 1 88 | * (we requested 1 warmup iterations, 5 iterations, 2 threads, and single fork) 89 | * 90 | * b) Via the Java API: 91 | * (see the JMH homepage for possible caveats when running from IDE: 92 | * http://openjdk.java.net/projects/code-tools/jmh/) 93 | */ 94 | 95 | public static void main(String[] args) throws RunnerException { 96 | Options opt = new OptionsBuilder() 97 | .include(JMHSample_18_Control.class.getSimpleName()) 98 | .warmupIterations(1) 99 | .measurementIterations(5) 100 | .threads(2) 101 | .forks(1) 102 | .build(); 103 | 104 | new Runner(opt).run(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/VarArgsOverhead.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import org.openjdk.jmh.annotations.Benchmark; 5 | import org.openjdk.jmh.annotations.BenchmarkMode; 6 | import org.openjdk.jmh.annotations.Fork; 7 | import org.openjdk.jmh.annotations.Measurement; 8 | import org.openjdk.jmh.annotations.Mode; 9 | import org.openjdk.jmh.annotations.OutputTimeUnit; 10 | import org.openjdk.jmh.annotations.Warmup; 11 | import org.openjdk.jmh.infra.Blackhole; 12 | 13 | @BenchmarkMode(Mode.AverageTime) 14 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 15 | @Warmup(iterations = 5) 16 | @Measurement(iterations = 5) 17 | @Fork(2) 18 | public class VarArgsOverhead { 19 | private static String TEST_STRING1 = "foo"; 20 | private static String TEST_STRING2 = "bar"; 21 | private static String TEST_STRING3 = "goo"; 22 | 23 | private void threeParams(Blackhole blackhole, 24 | String s1, String s2, String s3) { 25 | blackhole.consume(s1); 26 | blackhole.consume(s2); 27 | blackhole.consume(s3); 28 | } 29 | 30 | private void threeParamsVarArgs(Blackhole blackhole, 31 | String... strings) { 32 | blackhole.consume(strings[0]); 33 | blackhole.consume(strings[1]); 34 | blackhole.consume(strings[2]); 35 | } 36 | 37 | private void NParamsVarArgs(Blackhole blackhole, 38 | String... strings) { 39 | for (int i = 0; i < strings.length; i++) { 40 | blackhole.consume(strings[i]); 41 | } 42 | } 43 | 44 | @Benchmark 45 | public void noVarArgs(Blackhole blackhole) { 46 | threeParams(blackhole, TEST_STRING1, TEST_STRING2, TEST_STRING3); 47 | } 48 | 49 | @Benchmark 50 | public void withVarArgsFixed(Blackhole blackhole) { 51 | threeParamsVarArgs(blackhole, TEST_STRING1, TEST_STRING2, TEST_STRING3); 52 | } 53 | 54 | @Benchmark 55 | public void withVarArgsDyn(Blackhole blackhole) { 56 | NParamsVarArgs(blackhole, TEST_STRING1, TEST_STRING2, TEST_STRING3); 57 | } 58 | 59 | // RESULTS! (When run with `-prof gc`) 60 | // 61 | // Benchmark Mode Cnt Score Error Units 62 | // VarArgsOverhead.noVarArgs avgt 10 7.661 ± 0.233 ns/op 63 | // VarArgsOverhead.noVarArgs:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec 64 | // VarArgsOverhead.noVarArgs:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁵ B/op 65 | // VarArgsOverhead.noVarArgs:·gc.count avgt 10 ≈ 0 counts 66 | // VarArgsOverhead.withVarArgsDyn avgt 10 10.383 ± 0.063 ns/op 67 | // VarArgsOverhead.withVarArgsDyn:·gc.alloc.rate avgt 10 1958.857 ± 11.925 MB/sec 68 | // VarArgsOverhead.withVarArgsDyn:·gc.alloc.rate.norm avgt 10 32.000 ± 0.001 B/op 69 | // VarArgsOverhead.withVarArgsDyn:·gc.churn.PS_Eden_Space avgt 10 1956.343 ± 70.947 MB/sec 70 | // VarArgsOverhead.withVarArgsDyn:·gc.churn.PS_Eden_Space.norm avgt 10 31.959 ± 1.152 B/op 71 | // VarArgsOverhead.withVarArgsDyn:·gc.churn.PS_Survivor_Space avgt 10 0.098 ± 0.070 MB/sec 72 | // VarArgsOverhead.withVarArgsDyn:·gc.churn.PS_Survivor_Space.norm avgt 10 0.002 ± 0.001 B/op 73 | // VarArgsOverhead.withVarArgsDyn:·gc.count avgt 10 195.000 counts 74 | // VarArgsOverhead.withVarArgsDyn:·gc.time avgt 10 95.000 ms 75 | // VarArgsOverhead.withVarArgsFixed avgt 10 7.635 ± 0.021 ns/op 76 | // VarArgsOverhead.withVarArgsFixed:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec 77 | // VarArgsOverhead.withVarArgsFixed:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁵ B/op 78 | // VarArgsOverhead.withVarArgsFixed:·gc.count avgt 10 ≈ 0 counts 79 | // 80 | // The interesting line to look at for each benchmark is gc.alloc.rate.norm 81 | // which shows the heap allocation rate per benchmark iteration. 82 | // We can see the only benchmark that allocates is the one that 83 | // "dynamically" uses a varargs array. If the callee knows the length of 84 | // the array (which is rare; what's the point of the varargs then?) and can 85 | // directly extract the values, then the array is optimized out. 86 | // In the common case where the callee doesn't know how many params are 87 | // passed and needs to iterate over them, a full array needs to be 88 | // allocated on the heap. 89 | } 90 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_06_FixtureLevel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | @State(Scope.Thread) 40 | public class JMHSample_06_FixtureLevel { 41 | 42 | double x; 43 | 44 | /* 45 | * Fixture methods have different levels to control when they should be run. 46 | * There are at least three Levels available to the user. These are, from 47 | * top to bottom: 48 | * 49 | * Level.Trial: before or after the entire benchmark run (the sequence of iterations) 50 | * Level.Iteration: before or after the benchmark iteration (the sequence of invocations) 51 | * Level.Invocation; before or after the benchmark method invocation (WARNING: read the Javadoc before using) 52 | * 53 | * Time spent in fixture methods does not count into the performance 54 | * metrics, so you can use this to do some heavy-lifting. 55 | */ 56 | 57 | @TearDown(Level.Iteration) 58 | public void check() { 59 | assert x > Math.PI : "Nothing changed?"; 60 | } 61 | 62 | @Benchmark 63 | public void measureRight() { 64 | x++; 65 | } 66 | 67 | @Benchmark 68 | public void measureWrong() { 69 | double x = 0; 70 | x++; 71 | } 72 | 73 | /* 74 | * ============================== HOW TO RUN THIS TEST: ==================================== 75 | * 76 | * You can see measureRight() yields the result, and measureWrong() fires 77 | * the assert at the end of first iteration! This will not generate the results 78 | * for measureWrong(). You can also prevent JMH for proceeding further by 79 | * requiring "fail on error". 80 | * 81 | * You can run this test: 82 | * 83 | * a) Via the command line: 84 | * $ mvn clean install 85 | * $ java -ea -jar target/benchmarks.jar JMHSample_06 -wi 5 -i 5 -f 1 86 | * (we requested 5 warmup/measurement iterations, single fork) 87 | * 88 | * You can optionally supply -foe to fail the complete run. 89 | * 90 | * b) Via the Java API: 91 | * (see the JMH homepage for possible caveats when running from IDE: 92 | * http://openjdk.java.net/projects/code-tools/jmh/) 93 | */ 94 | 95 | public static void main(String[] args) throws RunnerException { 96 | Options opt = new OptionsBuilder() 97 | .include(JMHSample_06_FixtureLevel.class.getSimpleName()) 98 | .warmupIterations(5) 99 | .measurementIterations(5) 100 | .forks(1) 101 | .jvmArgs("-ea") 102 | .shouldFailOnError(false) // switch to "true" to fail the complete run 103 | .build(); 104 | 105 | new Runner(opt).run(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_01_HelloWorld.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | public class JMHSample_01_HelloWorld { 40 | 41 | /* 42 | * This is our first benchmark method. 43 | * 44 | * JMH works as follows: users annotate the methods with @Benchmark, and 45 | * then JMH produces the generated code to run this particular benchmark as 46 | * reliably as possible. In general one might think about @Benchmark methods 47 | * as the benchmark "payload", the things we want to measure. The 48 | * surrounding infrastructure is provided by the harness itself. 49 | * 50 | * Read the Javadoc for @Benchmark annotation for complete semantics and 51 | * restrictions. At this point we only note that the methods names are 52 | * non-essential, and it only matters that the methods are marked with 53 | * @Benchmark. You can have multiple benchmark methods within the same 54 | * class. 55 | * 56 | * Note: if the benchmark method never finishes, then JMH run never finishes 57 | * as well. If you throw an exception from the method body the JMH run ends 58 | * abruptly for this benchmark and JMH will run the next benchmark down the 59 | * list. 60 | * 61 | * Although this benchmark measures "nothing" it is a good showcase for the 62 | * overheads the infrastructure bear on the code you measure in the method. 63 | * There are no magical infrastructures which incur no overhead, and it is 64 | * important to know what are the infra overheads you are dealing with. You 65 | * might find this thought unfolded in future examples by having the 66 | * "baseline" measurements to compare against. 67 | */ 68 | 69 | @Benchmark 70 | public void wellHelloThere() { 71 | // this method was intentionally left blank. 72 | } 73 | 74 | /* 75 | * ============================== HOW TO RUN THIS TEST: ==================================== 76 | * 77 | * You are expected to see the run with large number of iterations, and 78 | * very large throughput numbers. You can see that as the estimate of the 79 | * harness overheads per method call. In most of our measurements, it is 80 | * down to several cycles per call. 81 | * 82 | * a) Via command-line: 83 | * $ mvn clean install 84 | * $ java -jar target/benchmarks.jar JMHSample_01 85 | * 86 | * JMH generates self-contained JARs, bundling JMH together with it. 87 | * The runtime options for the JMH are available with "-h": 88 | * $ java -jar target/benchmarks.jar -h 89 | * 90 | * b) Via the Java API: 91 | * (see the JMH homepage for possible caveats when running from IDE: 92 | * http://openjdk.java.net/projects/code-tools/jmh/) 93 | */ 94 | 95 | public static void main(String[] args) throws RunnerException { 96 | Options opt = new OptionsBuilder() 97 | .include(JMHSample_01_HelloWorld.class.getSimpleName()) 98 | .forks(1) 99 | .build(); 100 | 101 | new Runner(opt).run(); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_10_ConstantFold.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | import java.util.concurrent.TimeUnit; 40 | 41 | @State(Scope.Thread) 42 | @BenchmarkMode(Mode.AverageTime) 43 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 | public class JMHSample_10_ConstantFold { 45 | 46 | /* 47 | * The flip side of dead-code elimination is constant-folding. 48 | * 49 | * If JVM realizes the result of the computation is the same no matter what, 50 | * it can cleverly optimize it. In our case, that means we can move the 51 | * computation outside of the internal JMH loop. 52 | * 53 | * This can be prevented by always reading the inputs from non-final 54 | * instance fields of @State objects, computing the result based on those 55 | * values, and follow the rules to prevent DCE. 56 | */ 57 | 58 | // IDEs will say "Oh, you can convert this field to local variable". Don't. Trust. Them. 59 | // (While this is normally fine advice, it does not work in the context of measuring correctly.) 60 | private double x = Math.PI; 61 | 62 | // IDEs will probably also say "Look, it could be final". Don't. Trust. Them. Either. 63 | // (While this is normally fine advice, it does not work in the context of measuring correctly.) 64 | private final double wrongX = Math.PI; 65 | 66 | @Benchmark 67 | public double baseline() { 68 | // simply return the value, this is a baseline 69 | return Math.PI; 70 | } 71 | 72 | @Benchmark 73 | public double measureWrong_1() { 74 | // This is wrong: the source is predictable, and computation is foldable. 75 | return Math.log(Math.PI); 76 | } 77 | 78 | @Benchmark 79 | public double measureWrong_2() { 80 | // This is wrong: the source is predictable, and computation is foldable. 81 | return Math.log(wrongX); 82 | } 83 | 84 | @Benchmark 85 | public double measureRight() { 86 | // This is correct: the source is not predictable. 87 | return Math.log(x); 88 | } 89 | 90 | /* 91 | * ============================== HOW TO RUN THIS TEST: ==================================== 92 | * 93 | * You can see the unrealistically fast calculation in with measureWrong_*(), 94 | * while realistic measurement with measureRight(). 95 | * 96 | * You can run this test: 97 | * 98 | * a) Via the command line: 99 | * $ mvn clean install 100 | * $ java -jar target/benchmarks.jar JMHSample_10 -i 5 -f 1 101 | * (we requested 5 iterations, single fork) 102 | * 103 | * b) Via the Java API: 104 | * (see the JMH homepage for possible caveats when running from IDE: 105 | * http://openjdk.java.net/projects/code-tools/jmh/) 106 | */ 107 | 108 | public static void main(String[] args) throws RunnerException { 109 | Options opt = new OptionsBuilder() 110 | .include(JMHSample_10_ConstantFold.class.getSimpleName()) 111 | .warmupIterations(5) 112 | .measurementIterations(5) 113 | .forks(1) 114 | .build(); 115 | 116 | new Runner(opt).run(); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_09_Blackholes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.infra.Blackhole; 35 | import org.openjdk.jmh.runner.Runner; 36 | import org.openjdk.jmh.runner.RunnerException; 37 | import org.openjdk.jmh.runner.options.Options; 38 | import org.openjdk.jmh.runner.options.OptionsBuilder; 39 | 40 | import java.util.concurrent.TimeUnit; 41 | 42 | @BenchmarkMode(Mode.AverageTime) 43 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 | @State(Scope.Thread) 45 | public class JMHSample_09_Blackholes { 46 | 47 | /* 48 | * Should your benchmark require returning multiple results, you have to 49 | * consider two options (detailed below). 50 | * 51 | * NOTE: If you are only producing a single result, it is more readable to 52 | * use the implicit return, as in JMHSample_08_DeadCode. Do not make your benchmark 53 | * code less readable with explicit Blackholes! 54 | */ 55 | 56 | double x1 = Math.PI; 57 | double x2 = Math.PI * 2; 58 | 59 | /* 60 | * Baseline measurement: how much single Math.log costs. 61 | */ 62 | 63 | @Benchmark 64 | public double baseline() { 65 | return Math.log(x1); 66 | } 67 | 68 | /* 69 | * While the Math.log(x2) computation is intact, Math.log(x1) 70 | * is redundant and optimized out. 71 | */ 72 | 73 | @Benchmark 74 | public double measureWrong() { 75 | Math.log(x1); 76 | return Math.log(x2); 77 | } 78 | 79 | /* 80 | * This demonstrates Option A: 81 | * 82 | * Merge multiple results into one and return it. 83 | * This is OK when is computation is relatively heavyweight, and merging 84 | * the results does not offset the results much. 85 | */ 86 | 87 | @Benchmark 88 | public double measureRight_1() { 89 | return Math.log(x1) + Math.log(x2); 90 | } 91 | 92 | /* 93 | * This demonstrates Option B: 94 | * 95 | * Use explicit Blackhole objects, and sink the values there. 96 | * (Background: Blackhole is just another @State object, bundled with JMH). 97 | */ 98 | 99 | @Benchmark 100 | public void measureRight_2(Blackhole bh) { 101 | bh.consume(Math.log(x1)); 102 | bh.consume(Math.log(x2)); 103 | } 104 | 105 | /* 106 | * ============================== HOW TO RUN THIS TEST: ==================================== 107 | * 108 | * You will see measureWrong() running on-par with baseline(). 109 | * Both measureRight() are measuring twice the baseline, so the logs are intact. 110 | * 111 | * You can run this test: 112 | * 113 | * a) Via the command line: 114 | * $ mvn clean install 115 | * $ java -jar target/benchmarks.jar JMHSample_09 -wi 5 -i 5 -f 1 116 | * (we requested 5 warmup/measurement iterations, single fork) 117 | * 118 | * b) Via the Java API: 119 | * (see the JMH homepage for possible caveats when running from IDE: 120 | * http://openjdk.java.net/projects/code-tools/jmh/) 121 | */ 122 | 123 | public static void main(String[] args) throws RunnerException { 124 | Options opt = new OptionsBuilder() 125 | .include(JMHSample_09_Blackholes.class.getSimpleName()) 126 | .warmupIterations(5) 127 | .measurementIterations(5) 128 | .forks(1) 129 | .build(); 130 | 131 | new Runner(opt).run(); 132 | } 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_13_RunToRun.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Mode; 37 | import org.openjdk.jmh.annotations.OutputTimeUnit; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.Setup; 40 | import org.openjdk.jmh.annotations.State; 41 | import org.openjdk.jmh.runner.Runner; 42 | import org.openjdk.jmh.runner.RunnerException; 43 | import org.openjdk.jmh.runner.options.Options; 44 | import org.openjdk.jmh.runner.options.OptionsBuilder; 45 | 46 | import java.util.concurrent.TimeUnit; 47 | 48 | @State(Scope.Thread) 49 | @BenchmarkMode(Mode.AverageTime) 50 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 51 | public class JMHSample_13_RunToRun { 52 | 53 | /* 54 | * Forking also allows to estimate run-to-run variance. 55 | * 56 | * JVMs are complex systems, and the non-determinism is inherent for them. 57 | * This requires us to always account the run-to-run variance as the one 58 | * of the effects in our experiments. 59 | * 60 | * Luckily, forking aggregates the results across several JVM launches. 61 | */ 62 | 63 | /* 64 | * In order to introduce readily measurable run-to-run variance, we build 65 | * the workload which performance differs from run to run. Note that many workloads 66 | * will have the similar behavior, but we do that artificially to make a point. 67 | */ 68 | 69 | @State(Scope.Thread) 70 | public static class SleepyState { 71 | public long sleepTime; 72 | 73 | @Setup 74 | public void setup() { 75 | sleepTime = (long) (Math.random() * 1000); 76 | } 77 | } 78 | 79 | /* 80 | * Now, we will run this different number of times. 81 | */ 82 | 83 | @Benchmark 84 | @Fork(1) 85 | public void baseline(SleepyState s) throws InterruptedException { 86 | TimeUnit.MILLISECONDS.sleep(s.sleepTime); 87 | } 88 | 89 | @Benchmark 90 | @Fork(5) 91 | public void fork_1(SleepyState s) throws InterruptedException { 92 | TimeUnit.MILLISECONDS.sleep(s.sleepTime); 93 | } 94 | 95 | @Benchmark 96 | @Fork(20) 97 | public void fork_2(SleepyState s) throws InterruptedException { 98 | TimeUnit.MILLISECONDS.sleep(s.sleepTime); 99 | } 100 | 101 | /* 102 | * ============================== HOW TO RUN THIS TEST: ==================================== 103 | * 104 | * Note the baseline is random within [0..1000] msec; and both forked runs 105 | * are estimating the average 500 msec with some confidence. 106 | * 107 | * You can run this test: 108 | * 109 | * a) Via the command line: 110 | * $ mvn clean install 111 | * $ java -jar target/benchmarks.jar JMHSample_13 -wi 0 -i 3 112 | * (we requested no warmup, 3 measurement iterations) 113 | * 114 | * b) Via the Java API: 115 | * (see the JMH homepage for possible caveats when running from IDE: 116 | * http://openjdk.java.net/projects/code-tools/jmh/) 117 | */ 118 | 119 | public static void main(String[] args) throws RunnerException { 120 | Options opt = new OptionsBuilder() 121 | .include(JMHSample_13_RunToRun.class.getSimpleName()) 122 | .warmupIterations(0) 123 | .measurementIterations(5) 124 | .build(); 125 | 126 | new Runner(opt).run(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_23_AuxCounters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.AuxCounters; 34 | import org.openjdk.jmh.annotations.Benchmark; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.OutputTimeUnit; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.State; 40 | import org.openjdk.jmh.annotations.Warmup; 41 | import org.openjdk.jmh.runner.Runner; 42 | import org.openjdk.jmh.runner.RunnerException; 43 | import org.openjdk.jmh.runner.options.Options; 44 | import org.openjdk.jmh.runner.options.OptionsBuilder; 45 | 46 | import java.util.concurrent.TimeUnit; 47 | 48 | @OutputTimeUnit(TimeUnit.SECONDS) 49 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 50 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 51 | @Fork(1) 52 | public class JMHSample_23_AuxCounters { 53 | 54 | /* 55 | * In some weird cases you need to get the separate throughput/time 56 | * metrics for the benchmarked code depending on the outcome of the 57 | * current code. Trying to accommodate the cases like this, JMH optionally 58 | * provides the special annotation which treats @State objects 59 | * as the object bearing user counters. See @AuxCounters javadoc for 60 | * the limitations. 61 | */ 62 | 63 | @State(Scope.Thread) 64 | @AuxCounters(AuxCounters.Type.OPERATIONS) 65 | public static class OpCounters { 66 | // These fields would be counted as metrics 67 | public int case1; 68 | public int case2; 69 | 70 | // This accessor will also produce a metric 71 | public int total() { 72 | return case1 + case2; 73 | } 74 | } 75 | 76 | @State(Scope.Thread) 77 | @AuxCounters(AuxCounters.Type.EVENTS) 78 | public static class EventCounters { 79 | // This field would be counted as metric 80 | public int wows; 81 | } 82 | 83 | /* 84 | * This code measures the "throughput" in two parts of the branch. 85 | * The @AuxCounters state above holds the counters which we increment 86 | * ourselves, and then let JMH to use their values in the performance 87 | * calculations. 88 | */ 89 | 90 | @Benchmark 91 | public void splitBranch(OpCounters counters) { 92 | if (Math.random() < 0.1) { 93 | counters.case1++; 94 | } else { 95 | counters.case2++; 96 | } 97 | } 98 | 99 | @Benchmark 100 | public void runSETI(EventCounters counters) { 101 | float random = (float) Math.random(); 102 | float wowSignal = (float) Math.PI / 4; 103 | if (random == wowSignal) { 104 | // WOW, that's unusual. 105 | counters.wows++; 106 | } 107 | } 108 | 109 | /* 110 | * ============================== HOW TO RUN THIS TEST: ==================================== 111 | * 112 | * You can run this test: 113 | * 114 | * a) Via the command line: 115 | * $ mvn clean install 116 | * $ java -jar target/benchmarks.jar JMHSample_23 117 | * 118 | * b) Via the Java API: 119 | * (see the JMH homepage for possible caveats when running from IDE: 120 | * http://openjdk.java.net/projects/code-tools/jmh/) 121 | */ 122 | 123 | public static void main(String[] args) throws RunnerException { 124 | Options opt = new OptionsBuilder() 125 | .include(JMHSample_23_AuxCounters.class.getSimpleName()) 126 | .build(); 127 | 128 | new Runner(opt).run(); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_05_StateFixtures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | @State(Scope.Thread) 40 | public class JMHSample_05_StateFixtures { 41 | 42 | double x; 43 | 44 | /* 45 | * Since @State objects are kept around during the lifetime of the 46 | * benchmark, it helps to have the methods which do state housekeeping. 47 | * These are usual fixture methods, you are probably familiar with them from 48 | * JUnit and TestNG. 49 | * 50 | * Fixture methods make sense only on @State objects, and JMH will fail to 51 | * compile the test otherwise. 52 | * 53 | * As with the State, fixture methods are only called by those benchmark 54 | * threads which are using the state. That means you can operate in the 55 | * thread-local context, and (not) use synchronization as if you are 56 | * executing in the context of benchmark thread. 57 | * 58 | * Note: fixture methods can also work with static fields, although the 59 | * semantics of these operations fall back out of State scope, and obey 60 | * usual Java rules (i.e. one static field per class). 61 | */ 62 | 63 | /* 64 | * Ok, let's prepare our benchmark: 65 | */ 66 | 67 | @Setup 68 | public void prepare() { 69 | x = Math.PI; 70 | } 71 | 72 | /* 73 | * And, check the benchmark went fine afterwards: 74 | */ 75 | 76 | @TearDown 77 | public void check() { 78 | assert x > Math.PI : "Nothing changed?"; 79 | } 80 | 81 | /* 82 | * This method obviously does the right thing, incrementing the field x 83 | * in the benchmark state. check() will never fail this way, because 84 | * we are always guaranteed to have at least one benchmark call. 85 | */ 86 | 87 | @Benchmark 88 | public void measureRight() { 89 | x++; 90 | } 91 | 92 | /* 93 | * This method, however, will fail the check(), because we deliberately 94 | * have the "typo", and increment only the local variable. This should 95 | * not pass the check, and JMH will fail the run. 96 | */ 97 | 98 | @Benchmark 99 | public void measureWrong() { 100 | double x = 0; 101 | x++; 102 | } 103 | 104 | /* 105 | * ============================== HOW TO RUN THIS TEST: ==================================== 106 | * 107 | * You can see measureRight() yields the result, and measureWrong() fires 108 | * the assert at the end of the run. 109 | * 110 | * You can run this test: 111 | * 112 | * a) Via the command line: 113 | * $ mvn clean install 114 | * $ java -ea -jar target/benchmarks.jar JMHSample_05 -wi 5 -i 5 -f 1 115 | * (we requested 5 warmup/measurement iterations, single fork) 116 | * 117 | * b) Via the Java API: 118 | * (see the JMH homepage for possible caveats when running from IDE: 119 | * http://openjdk.java.net/projects/code-tools/jmh/) 120 | */ 121 | 122 | public static void main(String[] args) throws RunnerException { 123 | Options opt = new OptionsBuilder() 124 | .include(JMHSample_05_StateFixtures.class.getSimpleName()) 125 | .warmupIterations(5) 126 | .measurementIterations(5) 127 | .forks(1) 128 | .jvmArgs("-ea") 129 | .build(); 130 | 131 | new Runner(opt).run(); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_21_ConsumeCPU.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Mode; 36 | import org.openjdk.jmh.annotations.OutputTimeUnit; 37 | import org.openjdk.jmh.infra.Blackhole; 38 | import org.openjdk.jmh.runner.Runner; 39 | import org.openjdk.jmh.runner.RunnerException; 40 | import org.openjdk.jmh.runner.options.Options; 41 | import org.openjdk.jmh.runner.options.OptionsBuilder; 42 | 43 | import java.util.concurrent.TimeUnit; 44 | 45 | @BenchmarkMode(Mode.AverageTime) 46 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 47 | public class JMHSample_21_ConsumeCPU { 48 | 49 | /* 50 | * At times you require the test to burn some of the cycles doing nothing. 51 | * In many cases, you *do* want to burn the cycles instead of waiting. 52 | * 53 | * For these occasions, we have the infrastructure support. Blackholes 54 | * can not only consume the values, but also the time! Run this test 55 | * to get familiar with this part of JMH. 56 | * 57 | * (Note we use static method because most of the use cases are deep 58 | * within the testing code, and propagating blackholes is tedious). 59 | */ 60 | 61 | @Benchmark 62 | public void consume_0000() { 63 | Blackhole.consumeCPU(0); 64 | } 65 | 66 | @Benchmark 67 | public void consume_0001() { 68 | Blackhole.consumeCPU(1); 69 | } 70 | 71 | @Benchmark 72 | public void consume_0002() { 73 | Blackhole.consumeCPU(2); 74 | } 75 | 76 | @Benchmark 77 | public void consume_0004() { 78 | Blackhole.consumeCPU(4); 79 | } 80 | 81 | @Benchmark 82 | public void consume_0008() { 83 | Blackhole.consumeCPU(8); 84 | } 85 | 86 | @Benchmark 87 | public void consume_0016() { 88 | Blackhole.consumeCPU(16); 89 | } 90 | 91 | @Benchmark 92 | public void consume_0032() { 93 | Blackhole.consumeCPU(32); 94 | } 95 | 96 | @Benchmark 97 | public void consume_0064() { 98 | Blackhole.consumeCPU(64); 99 | } 100 | 101 | @Benchmark 102 | public void consume_0128() { 103 | Blackhole.consumeCPU(128); 104 | } 105 | 106 | @Benchmark 107 | public void consume_0256() { 108 | Blackhole.consumeCPU(256); 109 | } 110 | 111 | @Benchmark 112 | public void consume_0512() { 113 | Blackhole.consumeCPU(512); 114 | } 115 | 116 | @Benchmark 117 | public void consume_1024() { 118 | Blackhole.consumeCPU(1024); 119 | } 120 | 121 | /* 122 | * ============================== HOW TO RUN THIS TEST: ==================================== 123 | * 124 | * Note the single token is just a few cycles, and the more _tokens 125 | * you request, then more work is spent (almost linearly) 126 | * 127 | * You can run this test: 128 | * 129 | * a) Via the command line: 130 | * $ mvn clean install 131 | * $ java -jar target/benchmarks.jar JMHSample_21 -w 1 -i 5 -f 1 132 | * 133 | * b) Via the Java API: 134 | * (see the JMH homepage for possible caveats when running from IDE: 135 | * http://openjdk.java.net/projects/code-tools/jmh/) 136 | */ 137 | 138 | public static void main(String[] args) throws RunnerException { 139 | Options opt = new OptionsBuilder() 140 | .include(JMHSample_21_ConsumeCPU.class.getSimpleName()) 141 | .warmupIterations(1) 142 | .measurementIterations(5) 143 | .forks(1) 144 | .build(); 145 | 146 | new Runner(opt).run(); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_30_Interrupts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Group; 36 | import org.openjdk.jmh.annotations.Mode; 37 | import org.openjdk.jmh.annotations.OutputTimeUnit; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.Setup; 40 | import org.openjdk.jmh.annotations.State; 41 | import org.openjdk.jmh.runner.Runner; 42 | import org.openjdk.jmh.runner.RunnerException; 43 | import org.openjdk.jmh.runner.options.Options; 44 | import org.openjdk.jmh.runner.options.OptionsBuilder; 45 | import org.openjdk.jmh.runner.options.TimeValue; 46 | 47 | import java.util.concurrent.ArrayBlockingQueue; 48 | import java.util.concurrent.BlockingQueue; 49 | import java.util.concurrent.TimeUnit; 50 | 51 | @BenchmarkMode(Mode.AverageTime) 52 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 | @State(Scope.Group) 54 | public class JMHSample_30_Interrupts { 55 | 56 | /* 57 | * JMH can also detect when threads are stuck in the benchmarks, and try 58 | * to forcefully interrupt the benchmark thread. JMH tries to do that 59 | * when it is arguably sure it would not affect the measurement. 60 | */ 61 | 62 | /* 63 | * In this example, we want to measure the simple performance characteristics 64 | * of the ArrayBlockingQueue. Unfortunately, doing that without a harness 65 | * support will deadlock one of the threads, because the executions of 66 | * take/put are not paired perfectly. Fortunately for us, both methods react 67 | * to interrupts well, and therefore we can rely on JMH to terminate the 68 | * measurement for us. JMH will notify users about the interrupt actions 69 | * nevertheless, so users can see if those interrupts affected the measurement. 70 | * JMH will start issuing interrupts after the default or user-specified timeout 71 | * had been reached. 72 | * 73 | * This is a variant of org.openjdk.jmh.samples.JMHSample_18_Control, but without 74 | * the explicit control objects. This example is suitable for the methods which 75 | * react to interrupts gracefully. 76 | */ 77 | 78 | private BlockingQueue q; 79 | 80 | @Setup 81 | public void setup() { 82 | q = new ArrayBlockingQueue<>(1); 83 | } 84 | 85 | @Group("Q") 86 | @Benchmark 87 | public Integer take() throws InterruptedException { 88 | return q.take(); 89 | } 90 | 91 | @Group("Q") 92 | @Benchmark 93 | public void put() throws InterruptedException { 94 | q.put(42); 95 | } 96 | 97 | /* 98 | * ============================== HOW TO RUN THIS TEST: ==================================== 99 | * 100 | * You can run this test: 101 | * 102 | * a) Via the command line: 103 | * $ mvn clean install 104 | * $ java -jar target/benchmarks.jar JMHSample_30 -wi 5 -i 5 -t 2 -f 5 -to 5 105 | * (we requested 5 warmup iterations, 5 iterations, 2 threads, 5 forks, and 5 sec timeout) 106 | * 107 | * b) Via the Java API: 108 | * (see the JMH homepage for possible caveats when running from IDE: 109 | * http://openjdk.java.net/projects/code-tools/jmh/) 110 | */ 111 | 112 | public static void main(String[] args) throws RunnerException { 113 | Options opt = new OptionsBuilder() 114 | .include(JMHSample_30_Interrupts.class.getSimpleName()) 115 | .warmupIterations(5) 116 | .measurementIterations(5) 117 | .threads(2) 118 | .forks(5) 119 | .timeout(TimeValue.seconds(5)) 120 | .build(); 121 | 122 | new Runner(opt).run(); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/InstantOverhead.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.time.Clock; 4 | import java.time.Duration; 5 | import java.time.Instant; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | import java.util.concurrent.TimeUnit; 8 | import org.openjdk.jmh.annotations.Benchmark; 9 | import org.openjdk.jmh.annotations.BenchmarkMode; 10 | import org.openjdk.jmh.annotations.Fork; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Warmup; 15 | import org.openjdk.jmh.infra.Blackhole; 16 | 17 | // You MUST run this with `-prof GC` on the command line to see GC 18 | // statistics; without those, the results are useless (we don't care about 19 | // time in these benchmarks, only GC utilization). 20 | @BenchmarkMode(Mode.AverageTime) 21 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 22 | @Warmup(iterations = 5) 23 | @Measurement(iterations = 5) 24 | @Fork(2) 25 | public class InstantOverhead { 26 | private static final Clock clock = Clock.systemUTC(); 27 | 28 | @Benchmark 29 | public void rawMillis(Blackhole blackhole) throws InterruptedException { 30 | long start = System.currentTimeMillis(); 31 | Thread.sleep(2); 32 | blackhole.consume(System.currentTimeMillis() - start); 33 | } 34 | 35 | // Uses Instants and Durations to measure time; does this allocate? 36 | @Benchmark 37 | public void withInstant(Blackhole blackhole) throws InterruptedException { 38 | Instant start = Instant.now(); 39 | Thread.sleep(2); 40 | blackhole.consume(Duration.between(start, Instant.now()).toMillis()); 41 | } 42 | 43 | // Instant.now() actually allocates a Clock internally every time it's 44 | // called, so let's see what happens if we avoid that. 45 | @Benchmark 46 | public void withClock(Blackhole blackhole) throws InterruptedException { 47 | Instant start = clock.instant(); 48 | Thread.sleep(2); 49 | blackhole.consume(Duration.between(start, clock.instant()).toMillis()); 50 | } 51 | 52 | // This definitely allocates at least the Duration object because it's sent 53 | // into a Blackhole. 54 | @Benchmark 55 | public void control(Blackhole blackhole) throws InterruptedException { 56 | Instant start = Instant.now(); 57 | Thread.sleep(2); 58 | blackhole.consume(Duration.between(start, Instant.now())); // NO toMillis! 59 | } 60 | 61 | @Benchmark 62 | public void instantCreationWithoutClock(Blackhole blackhole) { 63 | Instant instant = Instant.ofEpochMilli( 64 | ThreadLocalRandom.current().nextInt(1, 1000)); 65 | useInstant(instant, blackhole); 66 | } 67 | 68 | private void useInstant(Instant instant, Blackhole blackhole) { 69 | blackhole.consume(instant.toEpochMilli()); 70 | } 71 | 72 | // RESULTS! (When run with `-prof gc`) 73 | // 74 | // Benchmark Mode Cnt Score Error Units 75 | // InstantOverhead.control avgt 10 2.109 ± 0.005 ms/op 76 | // InstantOverhead.control:·gc.alloc.rate avgt 10 0.032 ± 0.001 MB/sec 77 | // InstantOverhead.control:·gc.alloc.rate.norm avgt 10 105.167 ± 0.714 B/op 78 | // InstantOverhead.control:·gc.count avgt 10 ≈ 0 counts 79 | // InstantOverhead.instantCreationWithoutClock avgt 10 ≈ 10⁻⁵ ms/op 80 | // InstantOverhead.instantCreationWithoutClock:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec 81 | // InstantOverhead.instantCreationWithoutClock:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁵ B/op 82 | // InstantOverhead.instantCreationWithoutClock:·gc.count avgt 10 ≈ 0 counts 83 | // InstantOverhead.rawMillis avgt 10 2.109 ± 0.003 ms/op 84 | // InstantOverhead.rawMillis:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec 85 | // InstantOverhead.rawMillis:·gc.alloc.rate.norm avgt 10 0.917 ± 0.033 B/op 86 | // InstantOverhead.rawMillis:·gc.count avgt 10 ≈ 0 counts 87 | // InstantOverhead.withClock avgt 10 2.107 ± 0.010 ms/op 88 | // InstantOverhead.withClock:·gc.alloc.rate avgt 10 0.022 ± 0.001 MB/sec 89 | // InstantOverhead.withClock:·gc.alloc.rate.norm avgt 10 73.145 ± 0.724 B/op 90 | // InstantOverhead.withClock:·gc.count avgt 10 ≈ 0 counts 91 | // InstantOverhead.withInstant avgt 10 2.108 ± 0.004 ms/op 92 | // InstantOverhead.withInstant:·gc.alloc.rate avgt 10 0.032 ± 0.001 MB/sec 93 | // InstantOverhead.withInstant:·gc.alloc.rate.norm avgt 10 105.159 ± 0.743 B/op 94 | // InstantOverhead.withInstant:·gc.count avgt 10 ≈ 0 counts 95 | // 96 | // The interesting line to look at for each benchmark is gc.alloc.rate.norm 97 | // which shows the heap allocation rate per benchmark iteration. 98 | // Using Instants & Durations to measure time sadly allocates... but 99 | // creating an Instant "manually" won't, much like with Duration (see 100 | // DurationOverhead.java). 101 | } 102 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/IteratorPerf.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | import java.util.concurrent.TimeUnit; 7 | import org.apache.commons.lang3.RandomStringUtils; 8 | import org.openjdk.jmh.annotations.Benchmark; 9 | import org.openjdk.jmh.annotations.BenchmarkMode; 10 | import org.openjdk.jmh.annotations.Fork; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Param; 15 | import org.openjdk.jmh.annotations.Scope; 16 | import org.openjdk.jmh.annotations.Setup; 17 | import org.openjdk.jmh.annotations.State; 18 | import org.openjdk.jmh.annotations.Warmup; 19 | import org.openjdk.jmh.infra.Blackhole; 20 | 21 | @BenchmarkMode(Mode.AverageTime) 22 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 23 | @Warmup(iterations = 5) 24 | @Measurement(iterations = 5) 25 | @State(Scope.Thread) 26 | @Fork(2) 27 | public class IteratorPerf { 28 | @Param({"10", "100", "1000", "10000"}) 29 | public int numItems; 30 | 31 | // Use something more complex than an int/long/double because it's more 32 | // likely that app code is manipulating complex objects. 33 | List strings; 34 | List ints; 35 | 36 | private static String getRandomString() { 37 | return RandomStringUtils.random( 38 | ThreadLocalRandom.current().nextInt(5, 10)); 39 | } 40 | 41 | @Setup 42 | public void setup() { 43 | strings = new ArrayList<>(numItems); 44 | ints = new ArrayList<>(numItems); 45 | for (int i = 0; i < numItems; i++) { 46 | strings.add(getRandomString()); 47 | ints.add(ThreadLocalRandom.current().nextInt(5, 10)); 48 | } 49 | } 50 | 51 | // Needed to prevent the JIT from realizing that summing the list always 52 | // returns the same result and thus the whole loop can just be replaced 53 | // with a constant! 54 | private void perturbStringList() { 55 | ThreadLocalRandom random = ThreadLocalRandom.current(); 56 | int index = random.nextInt(0, strings.size()); 57 | strings.set(index, getRandomString()); 58 | } 59 | 60 | // Needed to prevent the JIT from realizing that summing the list always 61 | // returns the same result and thus the whole loop can just be replaced 62 | // with a constant! 63 | private void perturbIntList() { 64 | ThreadLocalRandom random = ThreadLocalRandom.current(); 65 | int index = random.nextInt(0, strings.size()); 66 | ints.set(index, random.nextInt(10, 50)); 67 | } 68 | 69 | @Benchmark 70 | public int rawForLoopStrings() { 71 | perturbStringList(); 72 | int sum = 0; 73 | for (int i = 0; i < strings.size(); i++) { 74 | sum += strings.get(i).length(); 75 | } 76 | return sum; 77 | } 78 | 79 | @Benchmark 80 | public int rawForLoopInts() { 81 | perturbIntList(); 82 | int sum = 0; 83 | for (int i = 0; i < ints.size(); i++) { 84 | sum += ints.get(i); 85 | } 86 | return sum; 87 | } 88 | 89 | @Benchmark 90 | public int forEachLoopStrings(Blackhole blackhole) { 91 | perturbStringList(); 92 | int sum = 0; 93 | for (String s : strings) { 94 | sum += s.length(); 95 | } 96 | return sum; 97 | } 98 | 99 | @Benchmark 100 | public int forEachLoopInts(Blackhole blackhole) { 101 | perturbIntList(); 102 | int sum = 0; 103 | for (int i : ints) { 104 | sum += i; 105 | } 106 | return sum; 107 | } 108 | 109 | // RESULTS! 110 | // 111 | // Benchmark (numItems) Mode Cnt Score Error Units 112 | // IteratorPerf.forEachLoopInts 10 avgt 10 24.127 ± 0.443 ns/op 113 | // IteratorPerf.forEachLoopInts 100 avgt 10 92.017 ± 0.726 ns/op 114 | // IteratorPerf.forEachLoopInts 1000 avgt 10 661.325 ± 1.638 ns/op 115 | // IteratorPerf.forEachLoopInts 10000 avgt 10 6191.103 ± 13.076 ns/op 116 | // IteratorPerf.forEachLoopStrings 10 avgt 10 1004.910 ± 5.967 ns/op 117 | // IteratorPerf.forEachLoopStrings 100 avgt 10 1088.029 ± 5.823 ns/op 118 | // IteratorPerf.forEachLoopStrings 1000 avgt 10 2325.451 ± 25.217 ns/op 119 | // IteratorPerf.forEachLoopStrings 10000 avgt 10 24893.045 ± 320.995 ns/op 120 | // IteratorPerf.rawForLoopInts 10 avgt 10 24.148 ± 0.368 ns/op 121 | // IteratorPerf.rawForLoopInts 100 avgt 10 87.894 ± 4.566 ns/op 122 | // IteratorPerf.rawForLoopInts 1000 avgt 10 611.584 ± 2.214 ns/op 123 | // IteratorPerf.rawForLoopInts 10000 avgt 10 5656.809 ± 13.956 ns/op 124 | // IteratorPerf.rawForLoopStrings 10 avgt 10 1006.259 ± 4.681 ns/op 125 | // IteratorPerf.rawForLoopStrings 100 avgt 10 1091.263 ± 9.166 ns/op 126 | // IteratorPerf.rawForLoopStrings 1000 avgt 10 2327.705 ± 19.821 ns/op 127 | // IteratorPerf.rawForLoopStrings 10000 avgt 10 25147.685 ± 368.331 ns/op 128 | // 129 | // When looping over a list of primitives like ints, the raw for loop is 130 | // about ~10% faster... but that's pretty much only for primitives. As soon 131 | // as you switch to objects like Strings, the overhead of cache misses 132 | // (since object contents need to be dereferenced) eliminates any benefit 133 | // for loops might have. 134 | } 135 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_17_SyncIterations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.OutputTimeUnit; 35 | import org.openjdk.jmh.annotations.Scope; 36 | import org.openjdk.jmh.annotations.State; 37 | import org.openjdk.jmh.runner.Runner; 38 | import org.openjdk.jmh.runner.RunnerException; 39 | import org.openjdk.jmh.runner.options.Options; 40 | import org.openjdk.jmh.runner.options.OptionsBuilder; 41 | 42 | import java.util.concurrent.TimeUnit; 43 | 44 | @State(Scope.Thread) 45 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 46 | public class JMHSample_17_SyncIterations { 47 | 48 | /* 49 | * This is the another thing that is enabled in JMH by default. 50 | * 51 | * Suppose we have this simple benchmark. 52 | */ 53 | 54 | private double src; 55 | 56 | @Benchmark 57 | public double test() { 58 | double s = src; 59 | for (int i = 0; i < 1000; i++) { 60 | s = Math.sin(s); 61 | } 62 | return s; 63 | } 64 | 65 | /* 66 | * It turns out if you run the benchmark with multiple threads, 67 | * the way you start and stop the worker threads seriously affects 68 | * performance. 69 | * 70 | * The natural way would be to park all the threads on some sort 71 | * of barrier, and the let them go "at once". However, that does 72 | * not work: there are no guarantees the worker threads will start 73 | * at the same time, meaning other worker threads are working 74 | * in better conditions, skewing the result. 75 | * 76 | * The better solution would be to introduce bogus iterations, 77 | * ramp up the threads executing the iterations, and then atomically 78 | * shift the system to measuring stuff. The same thing can be done 79 | * during the rampdown. This sounds complicated, but JMH already 80 | * handles that for you. 81 | * 82 | 83 | */ 84 | 85 | /* 86 | * ============================== HOW TO RUN THIS TEST: ==================================== 87 | * 88 | * You will need to oversubscribe the system to make this effect 89 | * clearly visible; however, this effect can also be shown on the 90 | * unsaturated systems.* 91 | * 92 | * Note the performance of -si false version is more flaky, even 93 | * though it is "better". This is the false improvement, granted by 94 | * some of the threads executing in solo. The -si true version more stable 95 | * and coherent. 96 | * 97 | * -si true is enabled by default. 98 | * 99 | * Say, $CPU is the number of CPUs on your machine. 100 | * 101 | * You can run this test with: 102 | * 103 | * a) Via the command line: 104 | * $ mvn clean install 105 | * $ java -jar target/benchmarks.jar JMHSample_17 \ 106 | * -i 20 -wi 1 -f 1 -t ${CPU*16} -si {true|false} 107 | * (we requested 1 warmup iterations, 20 iterations, single fork, 108 | * lots of threads, and changeable "synchronize iterations" option) 109 | * 110 | * b) Via the Java API: 111 | * (see the JMH homepage for possible caveats when running from IDE: 112 | * http://openjdk.java.net/projects/code-tools/jmh/) 113 | */ 114 | 115 | public static void main(String[] args) throws RunnerException { 116 | Options opt = new OptionsBuilder() 117 | .include(JMHSample_17_SyncIterations.class.getSimpleName()) 118 | .warmupIterations(1) 119 | .measurementIterations(20) 120 | .threads(Runtime.getRuntime().availableProcessors()*16) 121 | .forks(1) 122 | .syncIterations(true) // try to switch to "false" 123 | .build(); 124 | 125 | new Runner(opt).run(); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_24_Inheritance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.OutputTimeUnit; 39 | import org.openjdk.jmh.annotations.Scope; 40 | import org.openjdk.jmh.annotations.Setup; 41 | import org.openjdk.jmh.annotations.State; 42 | import org.openjdk.jmh.annotations.Warmup; 43 | import org.openjdk.jmh.runner.Runner; 44 | import org.openjdk.jmh.runner.RunnerException; 45 | import org.openjdk.jmh.runner.options.Options; 46 | import org.openjdk.jmh.runner.options.OptionsBuilder; 47 | 48 | import java.util.concurrent.TimeUnit; 49 | 50 | public class JMHSample_24_Inheritance { 51 | 52 | /* 53 | * In very special circumstances, you might want to provide the benchmark 54 | * body in the (abstract) superclass, and specialize it with the concrete 55 | * pieces in the subclasses. 56 | * 57 | * The rule of thumb is: if some class has @Benchmark method, then all the subclasses 58 | * are also having the "synthetic" @Benchmark method. The caveat is, because we only 59 | * know the type hierarchy during the compilation, it is only possible during 60 | * the same compilation session. That is, mixing in the subclass extending your 61 | * benchmark class *after* the JMH compilation would have no effect. 62 | * 63 | * Note how annotations now have two possible places. The closest annotation 64 | * in the hierarchy wins. 65 | */ 66 | 67 | @BenchmarkMode(Mode.AverageTime) 68 | @Fork(1) 69 | @State(Scope.Thread) 70 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 71 | public static abstract class AbstractBenchmark { 72 | int x; 73 | 74 | @Setup 75 | public void setup() { 76 | x = 42; 77 | } 78 | 79 | @Benchmark 80 | @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) 81 | @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) 82 | public double bench() { 83 | return doWork() * doWork(); 84 | } 85 | 86 | protected abstract double doWork(); 87 | } 88 | 89 | public static class BenchmarkLog extends AbstractBenchmark { 90 | @Override 91 | protected double doWork() { 92 | return Math.log(x); 93 | } 94 | } 95 | 96 | public static class BenchmarkSin extends AbstractBenchmark { 97 | @Override 98 | protected double doWork() { 99 | return Math.sin(x); 100 | } 101 | } 102 | 103 | public static class BenchmarkCos extends AbstractBenchmark { 104 | @Override 105 | protected double doWork() { 106 | return Math.cos(x); 107 | } 108 | } 109 | 110 | /* 111 | * ============================== HOW TO RUN THIS TEST: ==================================== 112 | * 113 | * You can run this test, and observe the three distinct benchmarks running the squares 114 | * of Math.log, Math.sin, and Math.cos, accordingly. 115 | * 116 | * a) Via the command line: 117 | * $ mvn clean install 118 | * $ java -jar target/benchmarks.jar JMHSample_24 119 | * 120 | * b) Via the Java API: 121 | * (see the JMH homepage for possible caveats when running from IDE: 122 | * http://openjdk.java.net/projects/code-tools/jmh/) 123 | */ 124 | 125 | public static void main(String[] args) throws RunnerException { 126 | Options opt = new OptionsBuilder() 127 | .include(JMHSample_24_Inheritance.class.getSimpleName()) 128 | .build(); 129 | 130 | new Runner(opt).run(); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_16_CompilerControl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.CompilerControl; 36 | import org.openjdk.jmh.annotations.Mode; 37 | import org.openjdk.jmh.annotations.OutputTimeUnit; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.State; 40 | import org.openjdk.jmh.runner.Runner; 41 | import org.openjdk.jmh.runner.RunnerException; 42 | import org.openjdk.jmh.runner.options.Options; 43 | import org.openjdk.jmh.runner.options.OptionsBuilder; 44 | 45 | import java.util.concurrent.TimeUnit; 46 | 47 | @State(Scope.Thread) 48 | @BenchmarkMode(Mode.AverageTime) 49 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 50 | public class JMHSample_16_CompilerControl { 51 | 52 | /* 53 | * We can use HotSpot-specific functionality to tell the compiler what 54 | * do we want to do with particular methods. To demonstrate the effects, 55 | * we end up with 3 methods in this sample. 56 | */ 57 | 58 | /** 59 | * These are our targets: 60 | * - first method is prohibited from inlining 61 | * - second method is forced to inline 62 | * - third method is prohibited from compiling 63 | * 64 | * We might even place the annotations directly to the benchmarked 65 | * methods, but this expresses the intent more clearly. 66 | */ 67 | 68 | public void target_blank() { 69 | // this method was intentionally left blank 70 | } 71 | 72 | @CompilerControl(CompilerControl.Mode.DONT_INLINE) 73 | public void target_dontInline() { 74 | // this method was intentionally left blank 75 | } 76 | 77 | @CompilerControl(CompilerControl.Mode.INLINE) 78 | public void target_inline() { 79 | // this method was intentionally left blank 80 | } 81 | 82 | @CompilerControl(CompilerControl.Mode.EXCLUDE) 83 | public void target_exclude() { 84 | // this method was intentionally left blank 85 | } 86 | 87 | /* 88 | * These method measures the calls performance. 89 | */ 90 | 91 | @Benchmark 92 | public void baseline() { 93 | // this method was intentionally left blank 94 | } 95 | 96 | @Benchmark 97 | public void blank() { 98 | target_blank(); 99 | } 100 | 101 | @Benchmark 102 | public void dontinline() { 103 | target_dontInline(); 104 | } 105 | 106 | @Benchmark 107 | public void inline() { 108 | target_inline(); 109 | } 110 | 111 | @Benchmark 112 | public void exclude() { 113 | target_exclude(); 114 | } 115 | 116 | /* 117 | * ============================== HOW TO RUN THIS TEST: ==================================== 118 | * 119 | * Note the performance of the baseline, blank, and inline methods are the same. 120 | * dontinline differs a bit, because we are making the proper call. 121 | * exclude is severely slower, becase we are not compiling it at all. 122 | * 123 | * You can run this test: 124 | * 125 | * a) Via the command line: 126 | * $ mvn clean install 127 | * $ java -jar target/benchmarks.jar JMHSample_16 -wi 1 -i 3 -f 1 128 | * (we requested 1 warmup iterations, 3 iterations, single fork) 129 | * 130 | * b) Via the Java API: 131 | * (see the JMH homepage for possible caveats when running from IDE: 132 | * http://openjdk.java.net/projects/code-tools/jmh/) 133 | */ 134 | 135 | public static void main(String[] args) throws RunnerException { 136 | Options opt = new OptionsBuilder() 137 | .include(JMHSample_16_CompilerControl.class.getSimpleName()) 138 | .warmupIterations(1) 139 | .measurementIterations(3) 140 | .forks(1) 141 | .build(); 142 | 143 | new Runner(opt).run(); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_03_States.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.Scope; 35 | import org.openjdk.jmh.annotations.State; 36 | import org.openjdk.jmh.runner.Runner; 37 | import org.openjdk.jmh.runner.RunnerException; 38 | import org.openjdk.jmh.runner.options.Options; 39 | import org.openjdk.jmh.runner.options.OptionsBuilder; 40 | 41 | public class JMHSample_03_States { 42 | 43 | /* 44 | * Most of the time, you need to maintain some state while the benchmark is 45 | * running. Since JMH is heavily used to build concurrent benchmarks, we 46 | * opted for an explicit notion of state-bearing objects. 47 | * 48 | * Below are two state objects. Their class names are not essential, it 49 | * matters they are marked with @State. These objects will be instantiated 50 | * on demand, and reused during the entire benchmark trial. 51 | * 52 | * The important property is that state is always instantiated by one of 53 | * those benchmark threads which will then have the access to that state. 54 | * That means you can initialize the fields as if you do that in worker 55 | * threads (ThreadLocals are yours, etc). 56 | */ 57 | 58 | @State(Scope.Benchmark) 59 | public static class BenchmarkState { 60 | volatile double x = Math.PI; 61 | } 62 | 63 | @State(Scope.Thread) 64 | public static class ThreadState { 65 | volatile double x = Math.PI; 66 | } 67 | 68 | /* 69 | * Benchmark methods can reference the states, and JMH will inject the 70 | * appropriate states while calling these methods. You can have no states at 71 | * all, or have only one state, or have multiple states referenced. This 72 | * makes building multi-threaded benchmark a breeze. 73 | * 74 | * For this exercise, we have two methods. 75 | */ 76 | 77 | @Benchmark 78 | public void measureUnshared(ThreadState state) { 79 | // All benchmark threads will call in this method. 80 | // 81 | // However, since ThreadState is the Scope.Thread, each thread 82 | // will have it's own copy of the state, and this benchmark 83 | // will measure unshared case. 84 | state.x++; 85 | } 86 | 87 | @Benchmark 88 | public void measureShared(BenchmarkState state) { 89 | // All benchmark threads will call in this method. 90 | // 91 | // Since BenchmarkState is the Scope.Benchmark, all threads 92 | // will share the state instance, and we will end up measuring 93 | // shared case. 94 | state.x++; 95 | } 96 | 97 | /* 98 | * ============================== HOW TO RUN THIS TEST: ==================================== 99 | * 100 | * You are expected to see the drastic difference in shared and unshared cases, 101 | * because you either contend for single memory location, or not. This effect 102 | * is more articulated on large machines. 103 | * 104 | * You can run this test: 105 | * 106 | * a) Via the command line: 107 | * $ mvn clean install 108 | * $ java -jar target/benchmarks.jar JMHSample_03 -wi 5 -i 5 -t 4 -f 1 109 | * (we requested 5 measurement/warmup iterations, with 4 threads, single fork) 110 | * 111 | * b) Via the Java API: 112 | * (see the JMH homepage for possible caveats when running from IDE: 113 | * http://openjdk.java.net/projects/code-tools/jmh/) 114 | */ 115 | 116 | public static void main(String[] args) throws RunnerException { 117 | Options opt = new OptionsBuilder() 118 | .include(JMHSample_03_States.class.getSimpleName()) 119 | .warmupIterations(5) 120 | .measurementIterations(5) 121 | .threads(4) 122 | .forks(1) 123 | .build(); 124 | 125 | new Runner(opt).run(); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_28_BlackholeHelpers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.OutputTimeUnit; 39 | import org.openjdk.jmh.annotations.Scope; 40 | import org.openjdk.jmh.annotations.Setup; 41 | import org.openjdk.jmh.annotations.State; 42 | import org.openjdk.jmh.annotations.Warmup; 43 | import org.openjdk.jmh.infra.Blackhole; 44 | import org.openjdk.jmh.runner.Runner; 45 | import org.openjdk.jmh.runner.RunnerException; 46 | import org.openjdk.jmh.runner.options.Options; 47 | import org.openjdk.jmh.runner.options.OptionsBuilder; 48 | 49 | import java.util.concurrent.TimeUnit; 50 | 51 | @BenchmarkMode(Mode.AverageTime) 52 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 54 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 55 | @Fork(1) 56 | @State(Scope.Thread) 57 | public class JMHSample_28_BlackholeHelpers { 58 | 59 | /** 60 | * Sometimes you need the black hole not in @Benchmark method, but in 61 | * helper methods, because you want to pass it through to the concrete 62 | * implementation which is instantiated in helper methods. In this case, 63 | * you can request the black hole straight in the helper method signature. 64 | * This applies to both @Setup and @TearDown methods, and also to other 65 | * JMH infrastructure objects, like Control. 66 | * 67 | * Below is the variant of {@link org.openjdk.jmh.samples.JMHSample_08_DeadCode} 68 | * test, but wrapped in the anonymous classes. 69 | */ 70 | 71 | public interface Worker { 72 | void work(); 73 | } 74 | 75 | private Worker workerBaseline; 76 | private Worker workerRight; 77 | private Worker workerWrong; 78 | 79 | @Setup 80 | public void setup(final Blackhole bh) { 81 | workerBaseline = new Worker() { 82 | double x; 83 | 84 | @Override 85 | public void work() { 86 | // do nothing 87 | } 88 | }; 89 | 90 | workerWrong = new Worker() { 91 | double x; 92 | 93 | @Override 94 | public void work() { 95 | Math.log(x); 96 | } 97 | }; 98 | 99 | workerRight = new Worker() { 100 | double x; 101 | 102 | @Override 103 | public void work() { 104 | bh.consume(Math.log(x)); 105 | } 106 | }; 107 | 108 | } 109 | 110 | @Benchmark 111 | public void baseline() { 112 | workerBaseline.work(); 113 | } 114 | 115 | @Benchmark 116 | public void measureWrong() { 117 | workerWrong.work(); 118 | } 119 | 120 | @Benchmark 121 | public void measureRight() { 122 | workerRight.work(); 123 | } 124 | 125 | /* 126 | * ============================== HOW TO RUN THIS TEST: ==================================== 127 | * 128 | * You will see measureWrong() running on-par with baseline(). 129 | * Both measureRight() are measuring twice the baseline, so the logs are intact. 130 | * 131 | * You can run this test: 132 | * 133 | * a) Via the command line: 134 | * $ mvn clean install 135 | * $ java -jar target/benchmarks.jar JMHSample_28 136 | * 137 | * b) Via the Java API: 138 | * (see the JMH homepage for possible caveats when running from IDE: 139 | * http://openjdk.java.net/projects/code-tools/jmh/) 140 | */ 141 | 142 | public static void main(String[] args) throws RunnerException { 143 | Options opt = new OptionsBuilder() 144 | .include(JMHSample_28_BlackholeHelpers.class.getSimpleName()) 145 | .build(); 146 | 147 | new Runner(opt).run(); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_26_BatchSize.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Level; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.Setup; 40 | import org.openjdk.jmh.annotations.State; 41 | import org.openjdk.jmh.annotations.Warmup; 42 | import org.openjdk.jmh.runner.Runner; 43 | import org.openjdk.jmh.runner.RunnerException; 44 | import org.openjdk.jmh.runner.options.Options; 45 | import org.openjdk.jmh.runner.options.OptionsBuilder; 46 | 47 | import java.util.LinkedList; 48 | import java.util.List; 49 | 50 | @State(Scope.Thread) 51 | public class JMHSample_26_BatchSize { 52 | 53 | /* 54 | * Sometimes you need to evaluate operation which doesn't have 55 | * the steady state. The cost of a benchmarked operation may 56 | * significantly vary from invocation to invocation. 57 | * 58 | * In this case, using the timed measurements is not a good idea, 59 | * and the only acceptable benchmark mode is a single shot. On the 60 | * other hand, the operation may be too small for reliable single 61 | * shot measurement. 62 | * 63 | * We can use "batch size" parameter to describe the number of 64 | * benchmark calls to do per one invocation without looping the method 65 | * manually and protect from problems described in JMHSample_11_Loops. 66 | */ 67 | 68 | /* 69 | * Suppose we want to measure insertion in the middle of the list. 70 | */ 71 | 72 | List list = new LinkedList<>(); 73 | 74 | @Benchmark 75 | @Warmup(iterations = 5, time = 1) 76 | @Measurement(iterations = 5, time = 1) 77 | @BenchmarkMode(Mode.AverageTime) 78 | public List measureWrong_1() { 79 | list.add(list.size() / 2, "something"); 80 | return list; 81 | } 82 | 83 | @Benchmark 84 | @Warmup(iterations = 5, time = 5) 85 | @Measurement(iterations = 5, time = 5) 86 | @BenchmarkMode(Mode.AverageTime) 87 | public List measureWrong_5() { 88 | list.add(list.size() / 2, "something"); 89 | return list; 90 | } 91 | 92 | /* 93 | * This is what you do with JMH. 94 | */ 95 | @Benchmark 96 | @Warmup(iterations = 5, batchSize = 5000) 97 | @Measurement(iterations = 5, batchSize = 5000) 98 | @BenchmarkMode(Mode.SingleShotTime) 99 | public List measureRight() { 100 | list.add(list.size() / 2, "something"); 101 | return list; 102 | } 103 | 104 | @Setup(Level.Iteration) 105 | public void setup(){ 106 | list.clear(); 107 | } 108 | 109 | /* 110 | * ============================== HOW TO RUN THIS TEST: ==================================== 111 | * 112 | * You can see completely different results for measureWrong_1 and measureWrong_5; this 113 | * is because the workload has no steady state. The result of the workload is dependent 114 | * on the measurement time. measureRight does not have this drawback, because it measures 115 | * the N invocations of the test method and measures it's time. 116 | * 117 | * We measure batch of 5000 invocations and consider the batch as the single operation. 118 | * 119 | * You can run this test: 120 | * 121 | * a) Via the command line: 122 | * $ mvn clean install 123 | * $ java -jar target/benchmarks.jar JMHSample_26 -f 1 124 | * 125 | * b) Via the Java API: 126 | * (see the JMH homepage for possible caveats when running from IDE: 127 | * http://openjdk.java.net/projects/code-tools/jmh/) 128 | */ 129 | 130 | public static void main(String[] args) throws RunnerException { 131 | Options opt = new OptionsBuilder() 132 | .include(JMHSample_26_BatchSize.class.getSimpleName()) 133 | .forks(1) 134 | .build(); 135 | 136 | new Runner(opt).run(); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_15_Asymmetric.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Group; 36 | import org.openjdk.jmh.annotations.GroupThreads; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.OutputTimeUnit; 39 | import org.openjdk.jmh.annotations.Scope; 40 | import org.openjdk.jmh.annotations.Setup; 41 | import org.openjdk.jmh.annotations.State; 42 | import org.openjdk.jmh.runner.Runner; 43 | import org.openjdk.jmh.runner.RunnerException; 44 | import org.openjdk.jmh.runner.options.Options; 45 | import org.openjdk.jmh.runner.options.OptionsBuilder; 46 | 47 | import java.util.concurrent.TimeUnit; 48 | import java.util.concurrent.atomic.AtomicInteger; 49 | 50 | @State(Scope.Group) 51 | @BenchmarkMode(Mode.AverageTime) 52 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 | public class JMHSample_15_Asymmetric { 54 | 55 | /* 56 | * So far all the tests were symmetric: the same code was executed in all the threads. 57 | * At times, you need the asymmetric test. JMH provides this with the notion of @Group, 58 | * which can bind several methods together, and all the threads are distributed among 59 | * the test methods. 60 | * 61 | * Each execution group contains of one or more threads. Each thread within a particular 62 | * execution group executes one of @Group-annotated @Benchmark methods. Multiple execution 63 | * groups may participate in the run. The total thread count in the run is rounded to the 64 | * execution group size, which will only allow the full execution groups. 65 | * 66 | * Note that two state scopes: Scope.Benchmark and Scope.Thread are not covering all 67 | * the use cases here -- you either share everything in the state, or share nothing. 68 | * To break this, we have the middle ground Scope.Group, which marks the state to be 69 | * shared within the execution group, but not among the execution groups. 70 | * 71 | * Putting this all together, the example below means: 72 | * a) define the execution group "g", with 3 threads executing inc(), and 1 thread 73 | * executing get(), 4 threads per group in total; 74 | * b) if we run this test case with 4 threads, then we will have a single execution 75 | * group. Generally, running with 4*N threads will create N execution groups, etc.; 76 | * c) each execution group has one @State instance to share: that is, execution groups 77 | * share the counter within the group, but not across the groups. 78 | */ 79 | 80 | private AtomicInteger counter; 81 | 82 | @Setup 83 | public void up() { 84 | counter = new AtomicInteger(); 85 | } 86 | 87 | @Benchmark 88 | @Group("g") 89 | @GroupThreads(3) 90 | public int inc() { 91 | return counter.incrementAndGet(); 92 | } 93 | 94 | @Benchmark 95 | @Group("g") 96 | @GroupThreads(1) 97 | public int get() { 98 | return counter.get(); 99 | } 100 | 101 | /* 102 | * ============================== HOW TO RUN THIS TEST: ==================================== 103 | * 104 | * You will have the distinct metrics for inc() and get() from this run. 105 | * 106 | * You can run this test: 107 | * 108 | * a) Via the command line: 109 | * $ mvn clean install 110 | * $ java -jar target/benchmarks.jar JMHSample_15 -wi 5 -i 5 -f 1 111 | * (we requested 5 warmup/measurement iterations, single fork) 112 | * 113 | * b) Via the Java API: 114 | * (see the JMH homepage for possible caveats when running from IDE: 115 | * http://openjdk.java.net/projects/code-tools/jmh/) 116 | */ 117 | 118 | public static void main(String[] args) throws RunnerException { 119 | Options opt = new OptionsBuilder() 120 | .include(JMHSample_15_Asymmetric.class.getSimpleName()) 121 | .warmupIterations(5) 122 | .measurementIterations(5) 123 | .forks(1) 124 | .build(); 125 | 126 | new Runner(opt).run(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/LambdaOverhead.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import org.openjdk.jmh.annotations.Benchmark; 5 | import org.openjdk.jmh.annotations.BenchmarkMode; 6 | import org.openjdk.jmh.annotations.Fork; 7 | import org.openjdk.jmh.annotations.Measurement; 8 | import org.openjdk.jmh.annotations.Mode; 9 | import org.openjdk.jmh.annotations.OutputTimeUnit; 10 | import org.openjdk.jmh.annotations.Param; 11 | import org.openjdk.jmh.annotations.Scope; 12 | import org.openjdk.jmh.annotations.State; 13 | import org.openjdk.jmh.annotations.Warmup; 14 | import org.openjdk.jmh.infra.Blackhole; 15 | 16 | // You MUST run this with `-prof GC` on the command line to see GC 17 | // statistics; without those, the results are useless (we don't care about 18 | // time in these benchmarks, only GC utilization). 19 | @BenchmarkMode(Mode.AverageTime) 20 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 21 | @State(Scope.Thread) 22 | @Warmup(iterations = 5) 23 | @Measurement(iterations = 5) 24 | @Fork(2) 25 | public class LambdaOverhead { 26 | private static final int TOKENS_TO_CONSUME = 10; 27 | 28 | @Param({"10"}) 29 | public int _tokens; 30 | 31 | public static void workDirectly(int tokens) { 32 | Blackhole.consumeCPU(tokens); 33 | } 34 | 35 | public static void workWithRunnable(Runnable runnable) { 36 | runnable.run(); 37 | } 38 | 39 | public static void runAndBlackhole(Runnable runnable, Blackhole blackhole) { 40 | runnable.run(); 41 | blackhole.consume(runnable); 42 | } 43 | 44 | // No lambda is used at all; this shows baseline GC perf because nothing 45 | // gets allocated. 46 | @Benchmark 47 | public void noLambda() { 48 | workDirectly(TOKENS_TO_CONSUME); 49 | } 50 | 51 | // Does this allocate? It shouldn't because the lambda doesn't capture any 52 | // vars from its environment. It should compile down to just a simple 53 | // function that can easily be inlined, right? 54 | @Benchmark 55 | public void withNoCaptureLambda() { 56 | workWithRunnable(() -> Blackhole.consumeCPU(TOKENS_TO_CONSUME)); 57 | } 58 | 59 | // Now this version *definitely* creates an object; it must, because the 60 | // lambda we provides captures a var and that needs to be stored somewhere. 61 | // But the question is does the JVM's escape analysis kick in to ensure the 62 | // object is allocated on the stack (instead of the heap), thus removing 63 | // all GC impact? 64 | @Benchmark 65 | public void withCaptureLambda() { 66 | workWithRunnable(() -> Blackhole.consumeCPU(_tokens)); 67 | } 68 | 69 | // This both creates an object *and* it ensures no escape-analysis happens 70 | // because runAndBlackhole sends the runnable into a Blackhole, ensuring the 71 | // object "escapes" the stack frame (and thus, the object MUST be allocated 72 | // on the heap). 73 | @Benchmark 74 | public void control(Blackhole blackhole) { 75 | runAndBlackhole(() -> Blackhole.consumeCPU(_tokens), blackhole); 76 | } 77 | 78 | // RESULTS! (When run with `-prof gc`) 79 | // 80 | // Benchmark (_tokens) Mode Cnt Score Error Units 81 | // LambdaOverhead.control 10 avgt 10 17.068 ± 0.559 ns/op 82 | // LambdaOverhead.control:·gc.alloc.rate 10 avgt 10 595.805 ± 19.603 MB/sec 83 | // LambdaOverhead.control:·gc.alloc.rate.norm 10 avgt 10 16.000 ± 0.001 B/op 84 | // LambdaOverhead.control:·gc.churn.PS_Eden_Space 10 avgt 10 603.161 ± 28.736 MB/sec 85 | // LambdaOverhead.control:·gc.churn.PS_Eden_Space.norm 10 avgt 10 16.197 ± 0.552 B/op 86 | // LambdaOverhead.control:·gc.churn.PS_Survivor_Space 10 avgt 10 0.094 ± 0.037 MB/sec 87 | // LambdaOverhead.control:·gc.churn.PS_Survivor_Space.norm 10 avgt 10 0.003 ± 0.001 B/op 88 | // LambdaOverhead.control:·gc.count 10 avgt 10 149.000 counts 89 | // LambdaOverhead.control:·gc.time 10 avgt 10 74.000 ms 90 | // LambdaOverhead.noLambda 10 avgt 10 12.585 ± 0.441 ns/op 91 | // LambdaOverhead.noLambda:·gc.alloc.rate 10 avgt 10 ≈ 10⁻⁴ MB/sec 92 | // LambdaOverhead.noLambda:·gc.alloc.rate.norm 10 avgt 10 ≈ 10⁻⁵ B/op 93 | // LambdaOverhead.noLambda:·gc.count 10 avgt 10 ≈ 0 counts 94 | // LambdaOverhead.withCaptureLambda 10 avgt 10 12.766 ± 0.542 ns/op 95 | // LambdaOverhead.withCaptureLambda:·gc.alloc.rate 10 avgt 10 ≈ 10⁻⁴ MB/sec 96 | // LambdaOverhead.withCaptureLambda:·gc.alloc.rate.norm 10 avgt 10 ≈ 10⁻⁵ B/op 97 | // LambdaOverhead.withCaptureLambda:·gc.count 10 avgt 10 ≈ 0 counts 98 | // LambdaOverhead.withNoCaptureLambda 10 avgt 10 12.711 ± 0.417 ns/op 99 | // LambdaOverhead.withNoCaptureLambda:·gc.alloc.rate 10 avgt 10 ≈ 10⁻⁴ MB/sec 100 | // LambdaOverhead.withNoCaptureLambda:·gc.alloc.rate.norm 10 avgt 10 ≈ 10⁻⁵ B/op 101 | // LambdaOverhead.withNoCaptureLambda:·gc.count 10 avgt 10 ≈ 0 counts 102 | // 103 | // The interesting line to look at for each benchmark is gc.alloc.rate.norm 104 | // which shows the heap allocation rate per benchmark iteration. 105 | // You can see that ONLY the "control" ends up allocating anything. In all 106 | // other benchmarks, nothing gets allocated. This is either because no 107 | // objects are being created in the first place, or because they're being 108 | // created on the stack instead of the heap (escape analysis FTW). 109 | } 110 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_11_Loops.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | import java.util.concurrent.TimeUnit; 40 | 41 | @State(Scope.Thread) 42 | @BenchmarkMode(Mode.AverageTime) 43 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 | public class JMHSample_11_Loops { 45 | 46 | /* 47 | * It would be tempting for users to do loops within the benchmarked method. 48 | * (This is the bad thing Caliper taught everyone). These tests explain why 49 | * this is a bad idea. 50 | * 51 | * Looping is done in the hope of minimizing the overhead of calling the 52 | * test method, by doing the operations inside the loop instead of inside 53 | * the method call. Don't buy this argument; you will see there is more 54 | * magic happening when we allow optimizers to merge the loop iterations. 55 | */ 56 | 57 | /* 58 | * Suppose we want to measure how much it takes to sum two integers: 59 | */ 60 | 61 | int x = 1; 62 | int y = 2; 63 | 64 | /* 65 | * This is what you do with JMH. 66 | */ 67 | 68 | @Benchmark 69 | public int measureRight() { 70 | return (x + y); 71 | } 72 | 73 | /* 74 | * The following tests emulate the naive looping. 75 | * This is the Caliper-style benchmark. 76 | */ 77 | private int reps(int reps) { 78 | int s = 0; 79 | for (int i = 0; i < reps; i++) { 80 | s += (x + y); 81 | } 82 | return s; 83 | } 84 | 85 | /* 86 | * We would like to measure this with different repetitions count. 87 | * Special annotation is used to get the individual operation cost. 88 | */ 89 | 90 | @Benchmark 91 | @OperationsPerInvocation(1) 92 | public int measureWrong_1() { 93 | return reps(1); 94 | } 95 | 96 | @Benchmark 97 | @OperationsPerInvocation(10) 98 | public int measureWrong_10() { 99 | return reps(10); 100 | } 101 | 102 | @Benchmark 103 | @OperationsPerInvocation(100) 104 | public int measureWrong_100() { 105 | return reps(100); 106 | } 107 | 108 | @Benchmark 109 | @OperationsPerInvocation(1000) 110 | public int measureWrong_1000() { 111 | return reps(1000); 112 | } 113 | 114 | @Benchmark 115 | @OperationsPerInvocation(10000) 116 | public int measureWrong_10000() { 117 | return reps(10000); 118 | } 119 | 120 | @Benchmark 121 | @OperationsPerInvocation(100000) 122 | public int measureWrong_100000() { 123 | return reps(100000); 124 | } 125 | 126 | /* 127 | * ============================== HOW TO RUN THIS TEST: ==================================== 128 | * 129 | * You might notice the larger the repetitions count, the lower the "perceived" 130 | * cost of the operation being measured. Up to the point we do each addition with 1/20 ns, 131 | * well beyond what hardware can actually do. 132 | * 133 | * This happens because the loop is heavily unrolled/pipelined, and the operation 134 | * to be measured is hoisted from the loop. Morale: don't overuse loops, rely on JMH 135 | * to get the measurement right. 136 | * 137 | * You can run this test: 138 | * 139 | * a) Via the command line: 140 | * $ mvn clean install 141 | * $ java -jar target/benchmarks.jar JMHSample_11 -wi 5 -i 5 -f 1 142 | * (we requested 5 warmup/measurement iterations, single fork) 143 | * 144 | * b) Via the Java API: 145 | * (see the JMH homepage for possible caveats when running from IDE: 146 | * http://openjdk.java.net/projects/code-tools/jmh/) 147 | */ 148 | 149 | public static void main(String[] args) throws RunnerException { 150 | Options opt = new OptionsBuilder() 151 | .include(JMHSample_11_Loops.class.getSimpleName()) 152 | .warmupIterations(5) 153 | .measurementIterations(5) 154 | .forks(1) 155 | .build(); 156 | 157 | new Runner(opt).run(); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_31_InfraParams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Mode; 36 | import org.openjdk.jmh.annotations.OutputTimeUnit; 37 | import org.openjdk.jmh.annotations.Scope; 38 | import org.openjdk.jmh.annotations.Setup; 39 | import org.openjdk.jmh.annotations.State; 40 | import org.openjdk.jmh.infra.BenchmarkParams; 41 | import org.openjdk.jmh.infra.ThreadParams; 42 | import org.openjdk.jmh.runner.Runner; 43 | import org.openjdk.jmh.runner.RunnerException; 44 | import org.openjdk.jmh.runner.options.Options; 45 | import org.openjdk.jmh.runner.options.OptionsBuilder; 46 | 47 | import java.util.ArrayList; 48 | import java.util.List; 49 | import java.util.concurrent.ConcurrentHashMap; 50 | import java.util.concurrent.TimeUnit; 51 | 52 | @BenchmarkMode(Mode.Throughput) 53 | @OutputTimeUnit(TimeUnit.SECONDS) 54 | @State(Scope.Benchmark) 55 | public class JMHSample_31_InfraParams { 56 | 57 | /* 58 | * There is a way to query JMH about the current running mode. This is 59 | * possible with three infrastructure objects we can request to be injected: 60 | * - BenchmarkParams: covers the benchmark-global configuration 61 | * - IterationParams: covers the current iteration configuration 62 | * - ThreadParams: covers the specifics about threading 63 | * 64 | * Suppose we want to check how the ConcurrentHashMap scales under different 65 | * parallelism levels. We can put concurrencyLevel in @Param, but it sometimes 66 | * inconvenient if, say, we want it to follow the @Threads count. Here is 67 | * how we can query JMH about how many threads was requested for the current run, 68 | * and put that into concurrencyLevel argument for CHM constructor. 69 | */ 70 | 71 | static final int THREAD_SLICE = 1000; 72 | 73 | private ConcurrentHashMap mapSingle; 74 | private ConcurrentHashMap mapFollowThreads; 75 | 76 | @Setup 77 | public void setup(BenchmarkParams params) { 78 | int capacity = 16 * THREAD_SLICE * params.getThreads(); 79 | mapSingle = new ConcurrentHashMap<>(capacity, 0.75f, 1); 80 | mapFollowThreads = new ConcurrentHashMap<>(capacity, 0.75f, params.getThreads()); 81 | } 82 | 83 | /* 84 | * Here is another neat trick. Generate the distinct set of keys for all threads: 85 | */ 86 | 87 | @State(Scope.Thread) 88 | public static class Ids { 89 | private List ids; 90 | 91 | @Setup 92 | public void setup(ThreadParams threads) { 93 | ids = new ArrayList<>(); 94 | for (int c = 0; c < THREAD_SLICE; c++) { 95 | ids.add("ID" + (THREAD_SLICE * threads.getThreadIndex() + c)); 96 | } 97 | } 98 | } 99 | 100 | @Benchmark 101 | public void measureDefault(Ids ids) { 102 | for (String s : ids.ids) { 103 | mapSingle.remove(s); 104 | mapSingle.put(s, s); 105 | } 106 | } 107 | 108 | @Benchmark 109 | public void measureFollowThreads(Ids ids) { 110 | for (String s : ids.ids) { 111 | mapFollowThreads.remove(s); 112 | mapFollowThreads.put(s, s); 113 | } 114 | } 115 | 116 | /* 117 | * ============================== HOW TO RUN THIS TEST: ==================================== 118 | * 119 | * You can run this test: 120 | * 121 | * a) Via the command line: 122 | * $ mvn clean install 123 | * $ java -jar target/benchmarks.jar JMHSample_31 -wi 5 -i 5 -t 4 -f 5 124 | * (we requested 5 warmup iterations, 5 iterations, 2 threads, and 5 forks) 125 | * 126 | * b) Via the Java API: 127 | * (see the JMH homepage for possible caveats when running from IDE: 128 | * http://openjdk.java.net/projects/code-tools/jmh/) 129 | */ 130 | 131 | public static void main(String[] args) throws RunnerException { 132 | Options opt = new OptionsBuilder() 133 | .include(JMHSample_31_InfraParams.class.getSimpleName()) 134 | .warmupIterations(5) 135 | .measurementIterations(5) 136 | .threads(4) 137 | .forks(5) 138 | .build(); 139 | 140 | new Runner(opt).run(); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/Time4JMonoticClock.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.time.Clock; 4 | import java.time.Instant; 5 | import java.util.concurrent.TimeUnit; 6 | import net.time4j.SystemClock; 7 | import net.time4j.TemporalType; 8 | import org.openjdk.jmh.annotations.Benchmark; 9 | import org.openjdk.jmh.annotations.BenchmarkMode; 10 | import org.openjdk.jmh.annotations.Fork; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.State; 16 | import org.openjdk.jmh.annotations.Warmup; 17 | 18 | @BenchmarkMode(Mode.AverageTime) 19 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 20 | @Warmup(iterations = 5) 21 | @Measurement(iterations = 5) 22 | @State(Scope.Thread) 23 | @Fork(2) 24 | public class Time4JMonoticClock { 25 | // Yes, the time4j clock is monotonic while the system one isn't. It's not 26 | // an apples-to-apples comparison, but it's exactly the comparison we want 27 | // to make! (That is, what's the cost of using the monotonic clock?) 28 | private Clock time4JClock = TemporalType.CLOCK.from(SystemClock.MONOTONIC); 29 | private Clock systemClock = Clock.systemUTC(); 30 | 31 | @Benchmark 32 | public Instant time4jClockWithInstant() { 33 | return time4JClock.instant(); 34 | } 35 | 36 | @Benchmark 37 | public Instant systemClockWithInstant() { 38 | return systemClock.instant(); 39 | } 40 | 41 | @Benchmark 42 | public long time4jClockRawMillis() { 43 | return time4JClock.millis(); 44 | } 45 | 46 | @Benchmark 47 | public long systemClockRawMillis() { 48 | return systemClock.millis(); 49 | } 50 | 51 | // GC RESULTS! (When run with `-prof gc`) 52 | // 53 | // Benchmark Mode Cnt Score Error Units 54 | // Time4JMonoticClock.systemClockRawMillis avgt 10 34.354 ± 1.621 ns/op 55 | // Time4JMonoticClock.systemClockRawMillis:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec 56 | // Time4JMonoticClock.systemClockRawMillis:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁵ B/op 57 | // Time4JMonoticClock.systemClockRawMillis:·gc.count avgt 10 ≈ 0 counts 58 | // Time4JMonoticClock.systemClockWithInstant avgt 10 45.255 ± 5.272 ns/op 59 | // Time4JMonoticClock.systemClockWithInstant:·gc.alloc.rate avgt 10 338.761 ± 36.662 MB/sec 60 | // Time4JMonoticClock.systemClockWithInstant:·gc.alloc.rate.norm avgt 10 24.000 ± 0.001 B/op 61 | // Time4JMonoticClock.systemClockWithInstant:·gc.churn.PS_Eden_Space avgt 10 356.308 ± 87.294 MB/sec 62 | // Time4JMonoticClock.systemClockWithInstant:·gc.churn.PS_Eden_Space.norm avgt 10 25.110 ± 4.133 B/op 63 | // Time4JMonoticClock.systemClockWithInstant:·gc.churn.PS_Survivor_Space avgt 10 0.212 ± 0.071 MB/sec 64 | // Time4JMonoticClock.systemClockWithInstant:·gc.churn.PS_Survivor_Space.norm avgt 10 0.015 ± 0.006 B/op 65 | // Time4JMonoticClock.systemClockWithInstant:·gc.count avgt 10 28.000 counts 66 | // Time4JMonoticClock.systemClockWithInstant:·gc.time avgt 10 42.000 ms 67 | // Time4JMonoticClock.time4jClockRawMillis avgt 10 58.347 ± 8.978 ns/op 68 | // Time4JMonoticClock.time4jClockRawMillis:·gc.alloc.rate avgt 10 263.534 ± 34.303 MB/sec 69 | // Time4JMonoticClock.time4jClockRawMillis:·gc.alloc.rate.norm avgt 10 24.000 ± 0.001 B/op 70 | // Time4JMonoticClock.time4jClockRawMillis:·gc.churn.PS_Eden_Space avgt 10 269.663 ± 75.456 MB/sec 71 | // Time4JMonoticClock.time4jClockRawMillis:·gc.churn.PS_Eden_Space.norm avgt 10 24.608 ± 7.150 B/op 72 | // Time4JMonoticClock.time4jClockRawMillis:·gc.churn.PS_Survivor_Space avgt 10 0.077 ± 0.136 MB/sec 73 | // Time4JMonoticClock.time4jClockRawMillis:·gc.churn.PS_Survivor_Space.norm avgt 10 0.007 ± 0.014 B/op 74 | // Time4JMonoticClock.time4jClockRawMillis:·gc.count avgt 10 19.000 counts 75 | // Time4JMonoticClock.time4jClockRawMillis:·gc.time avgt 10 30.000 ms 76 | // Time4JMonoticClock.time4jClockWithInstant avgt 10 59.870 ± 6.797 ns/op 77 | // Time4JMonoticClock.time4jClockWithInstant:·gc.alloc.rate avgt 10 512.263 ± 59.488 MB/sec 78 | // Time4JMonoticClock.time4jClockWithInstant:·gc.alloc.rate.norm avgt 10 48.000 ± 0.001 B/op 79 | // Time4JMonoticClock.time4jClockWithInstant:·gc.churn.PS_Eden_Space avgt 10 514.041 ± 86.099 MB/sec 80 | // Time4JMonoticClock.time4jClockWithInstant:·gc.churn.PS_Eden_Space.norm avgt 10 48.180 ± 6.101 B/op 81 | // Time4JMonoticClock.time4jClockWithInstant:·gc.churn.PS_Survivor_Space avgt 10 0.115 ± 0.112 MB/sec 82 | // Time4JMonoticClock.time4jClockWithInstant:·gc.churn.PS_Survivor_Space.norm avgt 10 0.011 ± 0.011 B/op 83 | // Time4JMonoticClock.time4jClockWithInstant:·gc.count avgt 10 51.000 counts 84 | // Time4JMonoticClock.time4jClockWithInstant:·gc.time avgt 10 72.000 ms 85 | // 86 | // We can see that speed-wise, the system clock and the time4j clock are 87 | // pretty much the same. Time4j is a bit slower, but not appreciably. 88 | // 89 | // GC-wise, the interesting line to look at for each benchmark is 90 | // gc.alloc.rate.norm which shows the heap allocation rate per benchmark 91 | // iteration. 92 | // Time4j allocates 48 bytes per iteration while the system clock allocates 93 | // 24 bytes per iteration. That's twice as much, but probably not an amount 94 | // you'd care about. 95 | // Interestingly, reading raw millis from the time4j clock still allocates 96 | // (unlike doing the same with the system clock). 97 | } 98 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_32_BulkWarmup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.CompilerControl; 36 | import org.openjdk.jmh.annotations.Mode; 37 | import org.openjdk.jmh.annotations.OutputTimeUnit; 38 | import org.openjdk.jmh.annotations.Scope; 39 | import org.openjdk.jmh.annotations.State; 40 | import org.openjdk.jmh.runner.Runner; 41 | import org.openjdk.jmh.runner.RunnerException; 42 | import org.openjdk.jmh.runner.options.Options; 43 | import org.openjdk.jmh.runner.options.OptionsBuilder; 44 | import org.openjdk.jmh.runner.options.WarmupMode; 45 | 46 | import java.util.concurrent.TimeUnit; 47 | 48 | @State(Scope.Thread) 49 | @BenchmarkMode(Mode.AverageTime) 50 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 51 | public class JMHSample_32_BulkWarmup { 52 | 53 | /* 54 | * This is an addendum to JMHSample_12_Forking test. 55 | * 56 | * Sometimes you want an opposite configuration: instead of separating the profiles 57 | * for different benchmarks, you want to mix them together to test the worst-case 58 | * scenario. 59 | * 60 | * JMH has a bulk warmup feature for that: it does the warmups for all the tests 61 | * first, and then measures them. JMH still forks the JVM for each test, but once the 62 | * new JVM has started, all the warmups are being run there, before running the 63 | * measurement. This helps to dodge the type profile skews, as each test is still 64 | * executed in a different JVM, and we only "mix" the warmup code we want. 65 | */ 66 | 67 | /* 68 | * These test classes are borrowed verbatim from JMHSample_12_Forking. 69 | */ 70 | 71 | public interface Counter { 72 | int inc(); 73 | } 74 | 75 | public class Counter1 implements Counter { 76 | private int x; 77 | 78 | @Override 79 | public int inc() { 80 | return x++; 81 | } 82 | } 83 | 84 | public class Counter2 implements Counter { 85 | private int x; 86 | 87 | @Override 88 | public int inc() { 89 | return x++; 90 | } 91 | } 92 | 93 | Counter c1 = new Counter1(); 94 | Counter c2 = new Counter2(); 95 | 96 | /* 97 | * And this is our test payload. Notice we have to break the inlining of the payload, 98 | * so that in could not be inlined in either measure_c1() or measure_c2() below, and 99 | * specialized for that only call. 100 | */ 101 | 102 | @CompilerControl(CompilerControl.Mode.DONT_INLINE) 103 | public int measure(Counter c) { 104 | int s = 0; 105 | for (int i = 0; i < 10; i++) { 106 | s += c.inc(); 107 | } 108 | return s; 109 | } 110 | 111 | @Benchmark 112 | public int measure_c1() { 113 | return measure(c1); 114 | } 115 | 116 | @Benchmark 117 | public int measure_c2() { 118 | return measure(c2); 119 | } 120 | 121 | /* 122 | * ============================== HOW TO RUN THIS TEST: ==================================== 123 | * 124 | * Note how JMH runs the warmups first, and only then a given test. Note how JMH re-warmups 125 | * the JVM for each test. The scores for C1 and C2 cases are equally bad, compare them to 126 | * the scores from JMHSample_12_Forking. 127 | * 128 | * You can run this test: 129 | * 130 | * a) Via the command line: 131 | * $ mvn clean install 132 | * $ java -jar target/benchmarks.jar JMHSample_32 -f 1 -wi 5 -i 5 -wm BULK 133 | * (we requested a single fork, 5 warmup/measurement iterations, and bulk warmup mode) 134 | * 135 | * b) Via the Java API: 136 | * (see the JMH homepage for possible caveats when running from IDE: 137 | * http://openjdk.java.net/projects/code-tools/jmh/) 138 | */ 139 | 140 | public static void main(String[] args) throws RunnerException { 141 | Options opt = new OptionsBuilder() 142 | .include(JMHSample_32_BulkWarmup.class.getSimpleName()) 143 | // .includeWarmup(...) <-- this may include other benchmarks into warmup 144 | .warmupMode(WarmupMode.BULK) // see other WarmupMode.* as well 145 | .warmupIterations(5) 146 | .measurementIterations(5) 147 | .forks(1) 148 | .build(); 149 | 150 | new Runner(opt).run(); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_33_SecurityManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Mode; 36 | import org.openjdk.jmh.annotations.OutputTimeUnit; 37 | import org.openjdk.jmh.annotations.Scope; 38 | import org.openjdk.jmh.annotations.Setup; 39 | import org.openjdk.jmh.annotations.State; 40 | import org.openjdk.jmh.annotations.TearDown; 41 | import org.openjdk.jmh.runner.Runner; 42 | import org.openjdk.jmh.runner.RunnerException; 43 | import org.openjdk.jmh.runner.options.Options; 44 | import org.openjdk.jmh.runner.options.OptionsBuilder; 45 | 46 | import java.io.IOException; 47 | import java.net.URI; 48 | import java.net.URISyntaxException; 49 | import java.security.NoSuchAlgorithmException; 50 | import java.security.Policy; 51 | import java.security.URIParameter; 52 | import java.util.concurrent.TimeUnit; 53 | 54 | @BenchmarkMode(Mode.AverageTime) 55 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 56 | public class JMHSample_33_SecurityManager { 57 | 58 | /* 59 | * Some targeted tests may care about SecurityManager being installed. 60 | * Since JMH itself needs to do privileged actions, it is not enough 61 | * to blindly install the SecurityManager, as JMH infrastructure will fail. 62 | */ 63 | 64 | /* 65 | * In this example, we want to measure the performance of System.getProperty 66 | * with SecurityManager installed or not. To do this, we have two state classes 67 | * with helper methods. One that reads the default JMH security policy (we ship one 68 | * with JMH), and installs the security manager; another one that makes sure 69 | * the SecurityManager is not installed. 70 | * 71 | * If you need a restricted security policy for the tests, you are advised to 72 | * get /jmh-security-minimal.policy, that contains the minimal permissions 73 | * required for JMH benchmark to run, merge the new permissions there, produce new 74 | * policy file in a temporary location, and load that policy file instead. 75 | * There is also /jmh-security-minimal-runner.policy, that contains the minimal 76 | * permissions for the JMH harness to run, if you want to use JVM args to arm 77 | * the SecurityManager. 78 | */ 79 | 80 | @State(Scope.Benchmark) 81 | public static class SecurityManagerInstalled { 82 | @Setup 83 | public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { 84 | URI policyFile = JMHSample_33_SecurityManager.class.getResource("/jmh-security.policy").toURI(); 85 | Policy.setPolicy(Policy.getInstance("JavaPolicy", new URIParameter(policyFile))); 86 | System.setSecurityManager(new SecurityManager()); 87 | } 88 | 89 | @TearDown 90 | public void tearDown() { 91 | System.setSecurityManager(null); 92 | } 93 | } 94 | 95 | @State(Scope.Benchmark) 96 | public static class SecurityManagerEmpty { 97 | @Setup 98 | public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { 99 | System.setSecurityManager(null); 100 | } 101 | } 102 | 103 | @Benchmark 104 | public String testWithSM(SecurityManagerInstalled s) throws InterruptedException { 105 | return System.getProperty("java.home"); 106 | } 107 | 108 | @Benchmark 109 | public String testWithoutSM(SecurityManagerEmpty s) throws InterruptedException { 110 | return System.getProperty("java.home"); 111 | } 112 | 113 | /* 114 | * ============================== HOW TO RUN THIS TEST: ==================================== 115 | * 116 | * You can run this test: 117 | * 118 | * a) Via the command line: 119 | * $ mvn clean install 120 | * $ java -jar target/benchmarks.jar JMHSample_33 -wi 5 -i 5 -f 1 121 | * (we requested 5 warmup iterations, 5 iterations, 2 threads, 5 forks) 122 | * 123 | * b) Via the Java API: 124 | * (see the JMH homepage for possible caveats when running from IDE: 125 | * http://openjdk.java.net/projects/code-tools/jmh/) 126 | */ 127 | 128 | public static void main(String[] args) throws RunnerException { 129 | Options opt = new OptionsBuilder() 130 | .include(JMHSample_33_SecurityManager.class.getSimpleName()) 131 | .warmupIterations(5) 132 | .measurementIterations(5) 133 | .forks(1) 134 | .build(); 135 | 136 | new Runner(opt).run(); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_12_Forking.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | import java.util.concurrent.TimeUnit; 40 | 41 | @State(Scope.Thread) 42 | @BenchmarkMode(Mode.AverageTime) 43 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 44 | public class JMHSample_12_Forking { 45 | 46 | /* 47 | * JVMs are notoriously good at profile-guided optimizations. This is bad 48 | * for benchmarks, because different tests can mix their profiles together, 49 | * and then render the "uniformly bad" code for every test. Forking (running 50 | * in a separate process) each test can help to evade this issue. 51 | * 52 | * JMH will fork the tests by default. 53 | */ 54 | 55 | /* 56 | * Suppose we have this simple counter interface, and two implementations. 57 | * Even though those are semantically the same, from the JVM standpoint, 58 | * those are distinct classes. 59 | */ 60 | 61 | public interface Counter { 62 | int inc(); 63 | } 64 | 65 | public class Counter1 implements Counter { 66 | private int x; 67 | 68 | @Override 69 | public int inc() { 70 | return x++; 71 | } 72 | } 73 | 74 | public class Counter2 implements Counter { 75 | private int x; 76 | 77 | @Override 78 | public int inc() { 79 | return x++; 80 | } 81 | } 82 | 83 | /* 84 | * And this is how we measure it. 85 | * Note this is susceptible for same issue with loops we mention in previous examples. 86 | */ 87 | 88 | public int measure(Counter c) { 89 | int s = 0; 90 | for (int i = 0; i < 10; i++) { 91 | s += c.inc(); 92 | } 93 | return s; 94 | } 95 | 96 | /* 97 | * These are two counters. 98 | */ 99 | Counter c1 = new Counter1(); 100 | Counter c2 = new Counter2(); 101 | 102 | /* 103 | * We first measure the Counter1 alone... 104 | * Fork(0) helps to run in the same JVM. 105 | */ 106 | 107 | @Benchmark 108 | @Fork(0) 109 | public int measure_1_c1() { 110 | return measure(c1); 111 | } 112 | 113 | /* 114 | * Then Counter2... 115 | */ 116 | 117 | @Benchmark 118 | @Fork(0) 119 | public int measure_2_c2() { 120 | return measure(c2); 121 | } 122 | 123 | /* 124 | * Then Counter1 again... 125 | */ 126 | 127 | @Benchmark 128 | @Fork(0) 129 | public int measure_3_c1_again() { 130 | return measure(c1); 131 | } 132 | 133 | /* 134 | * These two tests have explicit @Fork annotation. 135 | * JMH takes this annotation as the request to run the test in the forked JVM. 136 | * It's even simpler to force this behavior for all the tests via the command 137 | * line option "-f". The forking is default, but we still use the annotation 138 | * for the consistency. 139 | * 140 | * This is the test for Counter1. 141 | */ 142 | 143 | @Benchmark 144 | @Fork(1) 145 | public int measure_4_forked_c1() { 146 | return measure(c1); 147 | } 148 | 149 | /* 150 | * ...and this is the test for Counter2. 151 | */ 152 | 153 | @Benchmark 154 | @Fork(1) 155 | public int measure_5_forked_c2() { 156 | return measure(c2); 157 | } 158 | 159 | /* 160 | * ============================== HOW TO RUN THIS TEST: ==================================== 161 | * 162 | * Note that C1 is faster, C2 is slower, but the C1 is slow again! This is because 163 | * the profiles for C1 and C2 had merged together. Notice how flawless the measurement 164 | * is for forked runs. 165 | * 166 | * You can run this test: 167 | * 168 | * a) Via the command line: 169 | * $ mvn clean install 170 | * $ java -jar target/benchmarks.jar JMHSample_12 -wi 5 -i 5 171 | * (we requested 5 warmup/measurement iterations) 172 | * 173 | * b) Via the Java API: 174 | * (see the JMH homepage for possible caveats when running from IDE: 175 | * http://openjdk.java.net/projects/code-tools/jmh/) 176 | */ 177 | 178 | public static void main(String[] args) throws RunnerException { 179 | Options opt = new OptionsBuilder() 180 | .include(JMHSample_12_Forking.class.getSimpleName()) 181 | .warmupIterations(5) 182 | .measurementIterations(5) 183 | .build(); 184 | 185 | new Runner(opt).run(); 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/Streams.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import java.util.concurrent.TimeUnit; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.openjdk.jmh.annotations.Benchmark; 11 | import org.openjdk.jmh.annotations.BenchmarkMode; 12 | import org.openjdk.jmh.annotations.Fork; 13 | import org.openjdk.jmh.annotations.Measurement; 14 | import org.openjdk.jmh.annotations.Mode; 15 | import org.openjdk.jmh.annotations.OutputTimeUnit; 16 | import org.openjdk.jmh.annotations.Param; 17 | import org.openjdk.jmh.annotations.Scope; 18 | import org.openjdk.jmh.annotations.Setup; 19 | import org.openjdk.jmh.annotations.State; 20 | import org.openjdk.jmh.annotations.Warmup; 21 | 22 | @BenchmarkMode(Mode.AverageTime) 23 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 24 | @Warmup(iterations = 5) 25 | @Measurement(iterations = 5) 26 | @State(Scope.Thread) 27 | @Fork(2) 28 | public class Streams { 29 | @Param({"4", "20", "1000"}) 30 | public int numKeys; 31 | 32 | private Map> data = new HashMap<>(numKeys); 33 | 34 | @Setup 35 | public void setup() { 36 | ThreadLocalRandom random = ThreadLocalRandom.current(); 37 | for (int i = 0; i < numKeys; i++) { 38 | List list = new ArrayList<>(20); 39 | for (int j = 0; j < random.nextInt(20, 50); j++) { 40 | list.add(RandomStringUtils.random(random.nextInt(5, 10))); 41 | } 42 | data.put(RandomStringUtils.random(10), list); 43 | } 44 | } 45 | 46 | @Benchmark 47 | public int withStreams() { 48 | return data.values() 49 | .stream() 50 | .mapToInt(List::size) 51 | .sum(); 52 | } 53 | 54 | @Benchmark 55 | public int withIterator() { 56 | int sum = 0; 57 | for (List list : data.values()) { 58 | sum += list.size(); 59 | } 60 | return sum; 61 | } 62 | 63 | // RESULTS! (When run with `-prof gc`) 64 | // 65 | // Benchmark (numKeys) Mode Cnt Score Error Units 66 | // Streams.withIterator 4 avgt 10 21.863 ± 2.012 ns/op 67 | // Streams.withIterator:·gc.alloc.rate 4 avgt 10 ≈ 10⁻⁴ MB/sec 68 | // Streams.withIterator:·gc.alloc.rate.norm 4 avgt 10 ≈ 10⁻⁵ B/op 69 | // Streams.withIterator:·gc.count 4 avgt 10 ≈ 0 counts 70 | // Streams.withIterator 20 avgt 10 66.355 ± 6.878 ns/op 71 | // Streams.withIterator:·gc.alloc.rate 20 avgt 10 ≈ 10⁻⁴ MB/sec 72 | // Streams.withIterator:·gc.alloc.rate.norm 20 avgt 10 ≈ 10⁻⁵ B/op 73 | // Streams.withIterator:·gc.count 20 avgt 10 ≈ 0 counts 74 | // Streams.withIterator 1000 avgt 10 4220.107 ± 485.298 ns/op 75 | // Streams.withIterator:·gc.alloc.rate 1000 avgt 10 ≈ 10⁻⁴ MB/sec 76 | // Streams.withIterator:·gc.alloc.rate.norm 1000 avgt 10 0.002 ± 0.001 B/op 77 | // Streams.withIterator:·gc.count 1000 avgt 10 ≈ 0 counts 78 | // Streams.withStreams 4 avgt 10 66.328 ± 1.318 ns/op 79 | // Streams.withStreams:·gc.alloc.rate 4 avgt 10 2223.591 ± 44.065 MB/sec 80 | // Streams.withStreams:·gc.alloc.rate.norm 4 avgt 10 232.000 ± 0.001 B/op 81 | // Streams.withStreams:·gc.churn.PS_Eden_Space 4 avgt 10 2298.034 ± 395.648 MB/sec 82 | // Streams.withStreams:·gc.churn.PS_Eden_Space.norm 4 avgt 10 239.878 ± 42.587 B/op 83 | // Streams.withStreams:·gc.churn.PS_Survivor_Space 4 avgt 10 0.108 ± 0.127 MB/sec 84 | // Streams.withStreams:·gc.churn.PS_Survivor_Space.norm 4 avgt 10 0.011 ± 0.013 B/op 85 | // Streams.withStreams:·gc.count 4 avgt 10 28.000 counts 86 | // Streams.withStreams:·gc.time 4 avgt 10 38.000 ms 87 | // Streams.withStreams 20 avgt 10 179.163 ± 29.395 ns/op 88 | // Streams.withStreams:·gc.alloc.rate 20 avgt 10 888.173 ± 129.228 MB/sec 89 | // Streams.withStreams:·gc.alloc.rate.norm 20 avgt 10 248.000 ± 0.001 B/op 90 | // Streams.withStreams:·gc.churn.PS_Eden_Space 20 avgt 10 911.828 ± 216.359 MB/sec 91 | // Streams.withStreams:·gc.churn.PS_Eden_Space.norm 20 avgt 10 254.250 ± 42.265 B/op 92 | // Streams.withStreams:·gc.churn.PS_Survivor_Space 20 avgt 10 0.127 ± 0.091 MB/sec 93 | // Streams.withStreams:·gc.churn.PS_Survivor_Space.norm 20 avgt 10 0.036 ± 0.024 B/op 94 | // Streams.withStreams:·gc.count 20 avgt 10 55.000 counts 95 | // Streams.withStreams:·gc.time 20 avgt 10 71.000 ms 96 | // Streams.withStreams 1000 avgt 10 12090.225 ± 1128.039 ns/op 97 | // Streams.withStreams:·gc.alloc.rate 1000 avgt 10 13.079 ± 1.087 MB/sec 98 | // Streams.withStreams:·gc.alloc.rate.norm 1000 avgt 10 248.005 ± 0.001 B/op 99 | // Streams.withStreams:·gc.count 1000 avgt 10 ≈ 0 counts 100 | // 101 | // The interesting line to look at for each benchmark is gc.alloc.rate.norm 102 | // which shows the heap allocation rate per benchmark iteration. 103 | // Streams does allocate, but at 248 bytes, it's not a huge amount. Don't 104 | // call it in a loop though. 105 | // Perf-wise, the raw for-each loop is clearly ~3x faster, and consistently 106 | // so. Even as the number of elements increase, the perf advantage remains. 107 | } 108 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_07_FixtureLevelInvocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.runner.Runner; 35 | import org.openjdk.jmh.runner.RunnerException; 36 | import org.openjdk.jmh.runner.options.Options; 37 | import org.openjdk.jmh.runner.options.OptionsBuilder; 38 | 39 | import java.util.concurrent.*; 40 | 41 | /** 42 | * Fixtures have different Levels to control when they are about to run. 43 | * Level.Invocation is useful sometimes to do some per-invocation work 44 | * which should not count as payload (e.g. sleep for some time to emulate 45 | * think time) 46 | */ 47 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 48 | public class JMHSample_07_FixtureLevelInvocation { 49 | 50 | /* 51 | * Fixtures have different Levels to control when they are about to run. 52 | * Level.Invocation is useful sometimes to do some per-invocation work, 53 | * which should not count as payload. PLEASE NOTE the timestamping and 54 | * synchronization for Level.Invocation helpers might significantly offset 55 | * the measurement, use with care. See Level.Invocation javadoc for further 56 | * discussion. 57 | * 58 | * Consider this sample: 59 | */ 60 | 61 | /* 62 | * This state handles the executor. 63 | * Note we create and shutdown executor with Level.Trial, so 64 | * it is kept around the same across all iterations. 65 | */ 66 | 67 | @State(Scope.Benchmark) 68 | public static class NormalState { 69 | ExecutorService service; 70 | 71 | @Setup(Level.Trial) 72 | public void up() { 73 | service = Executors.newCachedThreadPool(); 74 | } 75 | 76 | @TearDown(Level.Trial) 77 | public void down() { 78 | service.shutdown(); 79 | } 80 | 81 | } 82 | 83 | /* 84 | * This is the *extension* of the basic state, which also 85 | * has the Level.Invocation fixture method, sleeping for some time. 86 | */ 87 | 88 | public static class LaggingState extends NormalState { 89 | public static final int SLEEP_TIME = Integer.getInteger("sleepTime", 10); 90 | 91 | @Setup(Level.Invocation) 92 | public void lag() throws InterruptedException { 93 | TimeUnit.MILLISECONDS.sleep(SLEEP_TIME); 94 | } 95 | } 96 | 97 | /* 98 | * This allows us to formulate the task: measure the task turnaround in 99 | * "hot" mode when we are not sleeping between the submits, and "cold" mode, 100 | * when we are sleeping. 101 | */ 102 | 103 | @Benchmark 104 | @BenchmarkMode(Mode.AverageTime) 105 | public double measureHot(NormalState e, final Scratch s) throws ExecutionException, InterruptedException { 106 | return e.service.submit(new Task(s)).get(); 107 | } 108 | 109 | @Benchmark 110 | @BenchmarkMode(Mode.AverageTime) 111 | public double measureCold(LaggingState e, final Scratch s) throws ExecutionException, InterruptedException { 112 | return e.service.submit(new Task(s)).get(); 113 | } 114 | 115 | /* 116 | * This is our scratch state which will handle the work. 117 | */ 118 | 119 | @State(Scope.Thread) 120 | public static class Scratch { 121 | private double p; 122 | public double doWork() { 123 | p = Math.log(p); 124 | return p; 125 | } 126 | } 127 | 128 | public static class Task implements Callable { 129 | private Scratch s; 130 | 131 | public Task(Scratch s) { 132 | this.s = s; 133 | } 134 | 135 | @Override 136 | public Double call() { 137 | return s.doWork(); 138 | } 139 | } 140 | 141 | /* 142 | * ============================== HOW TO RUN THIS TEST: ==================================== 143 | * 144 | * You can see the cold scenario is running longer, because we pay for 145 | * thread wakeups. 146 | * 147 | * You can run this test: 148 | * 149 | * a) Via the command line: 150 | * $ mvn clean install 151 | * $ java -jar target/benchmarks.jar JMHSample_07 -wi 5 -i 5 -f 1 152 | * (we requested 5 warmup/measurement iterations, single fork) 153 | * 154 | * b) Via the Java API: 155 | * (see the JMH homepage for possible caveats when running from IDE: 156 | * http://openjdk.java.net/projects/code-tools/jmh/) 157 | */ 158 | 159 | public static void main(String[] args) throws RunnerException { 160 | Options opt = new OptionsBuilder() 161 | .include(JMHSample_07_FixtureLevelInvocation.class.getSimpleName()) 162 | .warmupIterations(5) 163 | .measurementIterations(5) 164 | .forks(1) 165 | .build(); 166 | 167 | new Runner(opt).run(); 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_29_StatesDAG.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.Benchmark; 34 | import org.openjdk.jmh.annotations.BenchmarkMode; 35 | import org.openjdk.jmh.annotations.Fork; 36 | import org.openjdk.jmh.annotations.Measurement; 37 | import org.openjdk.jmh.annotations.Mode; 38 | import org.openjdk.jmh.annotations.OutputTimeUnit; 39 | import org.openjdk.jmh.annotations.Scope; 40 | import org.openjdk.jmh.annotations.Setup; 41 | import org.openjdk.jmh.annotations.State; 42 | import org.openjdk.jmh.annotations.TearDown; 43 | import org.openjdk.jmh.annotations.Warmup; 44 | import org.openjdk.jmh.runner.Runner; 45 | import org.openjdk.jmh.runner.RunnerException; 46 | import org.openjdk.jmh.runner.options.Options; 47 | import org.openjdk.jmh.runner.options.OptionsBuilder; 48 | 49 | import java.util.ArrayList; 50 | import java.util.LinkedList; 51 | import java.util.List; 52 | import java.util.Queue; 53 | import java.util.concurrent.TimeUnit; 54 | 55 | @BenchmarkMode(Mode.AverageTime) 56 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 57 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 58 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 59 | @Fork(1) 60 | @State(Scope.Thread) 61 | public class JMHSample_29_StatesDAG { 62 | 63 | /** 64 | * WARNING: 65 | * THIS IS AN EXPERIMENTAL FEATURE, BE READY FOR IT BECOME REMOVED WITHOUT NOTICE! 66 | */ 67 | 68 | /** 69 | * There are weird cases when the benchmark state is more cleanly described 70 | * by the set of @States, and those @States reference each other. JMH allows 71 | * linking @States in directed acyclic graphs (DAGs) by referencing @States 72 | * in helper method signatures. (Note that {@link org.openjdk.jmh.samples.JMHSample_28_BlackholeHelpers} 73 | * is just a special case of that. 74 | * 75 | * Following the interface for @Benchmark calls, all @Setups for 76 | * referenced @State-s are fired before it becomes accessible to current @State. 77 | * Similarly, no @TearDown methods are fired for referenced @State before 78 | * current @State is done with it. 79 | */ 80 | 81 | /* 82 | * This is a model case, and it might not be a good benchmark. 83 | * // TODO: Replace it with the benchmark which does something useful. 84 | */ 85 | 86 | public static class Counter { 87 | int x; 88 | 89 | public int inc() { 90 | return x++; 91 | } 92 | 93 | public void dispose() { 94 | // pretend this is something really useful 95 | } 96 | } 97 | 98 | /* 99 | * Shared state maintains the set of Counters, and worker threads should 100 | * poll their own instances of Counter to work with. However, it should only 101 | * be done once, and therefore, Local state caches it after requesting the 102 | * counter from Shared state. 103 | */ 104 | 105 | @State(Scope.Benchmark) 106 | public static class Shared { 107 | List all; 108 | Queue available; 109 | 110 | @Setup 111 | public synchronized void setup() { 112 | all = new ArrayList<>(); 113 | for (int c = 0; c < 10; c++) { 114 | all.add(new Counter()); 115 | } 116 | 117 | available = new LinkedList<>(); 118 | available.addAll(all); 119 | } 120 | 121 | @TearDown 122 | public synchronized void tearDown() { 123 | for (Counter c : all) { 124 | c.dispose(); 125 | } 126 | } 127 | 128 | public synchronized Counter getMine() { 129 | return available.poll(); 130 | } 131 | } 132 | 133 | @State(Scope.Thread) 134 | public static class Local { 135 | Counter cnt; 136 | 137 | @Setup 138 | public void setup(Shared shared) { 139 | cnt = shared.getMine(); 140 | } 141 | } 142 | 143 | @Benchmark 144 | public int test(Local local) { 145 | return local.cnt.inc(); 146 | } 147 | 148 | /* 149 | * ============================== HOW TO RUN THIS TEST: ==================================== 150 | * 151 | * You can run this test: 152 | * 153 | * a) Via the command line: 154 | * $ mvn clean install 155 | * $ java -jar target/benchmarks.jar JMHSample_29 156 | * 157 | * b) Via the Java API: 158 | * (see the JMH homepage for possible caveats when running from IDE: 159 | * http://openjdk.java.net/projects/code-tools/jmh/) 160 | */ 161 | 162 | public static void main(String[] args) throws RunnerException { 163 | Options opt = new OptionsBuilder() 164 | .include(JMHSample_29_StatesDAG.class.getSimpleName()) 165 | .build(); 166 | 167 | new Runner(opt).run(); 168 | } 169 | 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/jmh/java/io/markovic/jmh/experiments/IteratorGC.java: -------------------------------------------------------------------------------- 1 | package io.markovic.jmh.experiments; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.TimeUnit; 6 | import org.apache.commons.lang3.RandomStringUtils; 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Measurement; 11 | import org.openjdk.jmh.annotations.Mode; 12 | import org.openjdk.jmh.annotations.OutputTimeUnit; 13 | import org.openjdk.jmh.annotations.Param; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.Setup; 16 | import org.openjdk.jmh.annotations.State; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | import org.openjdk.jmh.infra.Blackhole; 19 | 20 | @BenchmarkMode(Mode.AverageTime) 21 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 22 | @Warmup(iterations = 5) 23 | @Measurement(iterations = 5) 24 | @State(Scope.Benchmark) 25 | @Fork(2) 26 | public class IteratorGC { 27 | @Param({"4", "10", "20", "50", "1000"}) 28 | public int numStrings; 29 | 30 | List strings; 31 | 32 | @Setup 33 | public void setup() { 34 | strings = new ArrayList<>(numStrings); 35 | for (int i = 0; i < numStrings; i++) { 36 | strings.add(RandomStringUtils.random(10)); 37 | } 38 | } 39 | 40 | @Benchmark 41 | public void rawForLoop(Blackhole blackhole) { 42 | for (int i = 0; i < strings.size(); i++) { 43 | blackhole.consume(strings.get(i)); 44 | } 45 | } 46 | 47 | @Benchmark 48 | public void forEachLoop(Blackhole blackhole) { 49 | for (String s : strings) { 50 | blackhole.consume(s); 51 | } 52 | } 53 | 54 | // RESULTS! (When run with `-prof gc`) 55 | // 56 | // Benchmark (numItems) Mode Cnt Score Error Units 57 | // IteratorGC.forEachLoop 4 avgt 10 19.030 ± 0.386 ns/op 58 | // IteratorGC.forEachLoop:·gc.alloc.rate 4 avgt 10 ≈ 10⁻⁴ MB/sec 59 | // IteratorGC.forEachLoop:·gc.alloc.rate.norm 4 avgt 10 ≈ 10⁻⁵ B/op 60 | // IteratorGC.forEachLoop:·gc.count 4 avgt 10 ≈ 0 counts 61 | // IteratorGC.forEachLoop 10 avgt 10 50.100 ± 1.497 ns/op 62 | // IteratorGC.forEachLoop:·gc.alloc.rate 10 avgt 10 ≈ 10⁻⁴ MB/sec 63 | // IteratorGC.forEachLoop:·gc.alloc.rate.norm 10 avgt 10 ≈ 10⁻⁵ B/op 64 | // IteratorGC.forEachLoop:·gc.count 10 avgt 10 ≈ 0 counts 65 | // IteratorGC.forEachLoop 20 avgt 10 97.925 ± 2.449 ns/op 66 | // IteratorGC.forEachLoop:·gc.alloc.rate 20 avgt 10 ≈ 10⁻⁴ MB/sec 67 | // IteratorGC.forEachLoop:·gc.alloc.rate.norm 20 avgt 10 ≈ 10⁻⁴ B/op 68 | // IteratorGC.forEachLoop:·gc.count 20 avgt 10 ≈ 0 counts 69 | // IteratorGC.forEachLoop 50 avgt 10 253.550 ± 13.140 ns/op 70 | // IteratorGC.forEachLoop:·gc.alloc.rate 50 avgt 10 ≈ 10⁻⁴ MB/sec 71 | // IteratorGC.forEachLoop:·gc.alloc.rate.norm 50 avgt 10 ≈ 10⁻⁴ B/op 72 | // IteratorGC.forEachLoop:·gc.count 50 avgt 10 ≈ 0 counts 73 | // IteratorGC.forEachLoop 1000 avgt 10 5065.450 ± 149.429 ns/op 74 | // IteratorGC.forEachLoop:·gc.alloc.rate 1000 avgt 10 ≈ 10⁻⁴ MB/sec 75 | // IteratorGC.forEachLoop:·gc.alloc.rate.norm 1000 avgt 10 0.002 ± 0.001 B/op 76 | // IteratorGC.forEachLoop:·gc.count 1000 avgt 10 ≈ 0 counts 77 | // IteratorGC.rawForLoop 4 avgt 10 17.050 ± 0.407 ns/op 78 | // IteratorGC.rawForLoop:·gc.alloc.rate 4 avgt 10 ≈ 10⁻⁴ MB/sec 79 | // IteratorGC.rawForLoop:·gc.alloc.rate.norm 4 avgt 10 ≈ 10⁻⁵ B/op 80 | // IteratorGC.rawForLoop:·gc.count 4 avgt 10 ≈ 0 counts 81 | // IteratorGC.rawForLoop 10 avgt 10 41.231 ± 1.307 ns/op 82 | // IteratorGC.rawForLoop:·gc.alloc.rate 10 avgt 10 ≈ 10⁻⁴ MB/sec 83 | // IteratorGC.rawForLoop:·gc.alloc.rate.norm 10 avgt 10 ≈ 10⁻⁵ B/op 84 | // IteratorGC.rawForLoop:·gc.count 10 avgt 10 ≈ 0 counts 85 | // IteratorGC.rawForLoop 20 avgt 10 82.340 ± 3.602 ns/op 86 | // IteratorGC.rawForLoop:·gc.alloc.rate 20 avgt 10 ≈ 10⁻⁴ MB/sec 87 | // IteratorGC.rawForLoop:·gc.alloc.rate.norm 20 avgt 10 ≈ 10⁻⁴ B/op 88 | // IteratorGC.rawForLoop:·gc.count 20 avgt 10 ≈ 0 counts 89 | // IteratorGC.rawForLoop 50 avgt 10 212.377 ± 4.300 ns/op 90 | // IteratorGC.rawForLoop:·gc.alloc.rate 50 avgt 10 ≈ 10⁻⁴ MB/sec 91 | // IteratorGC.rawForLoop:·gc.alloc.rate.norm 50 avgt 10 ≈ 10⁻⁴ B/op 92 | // IteratorGC.rawForLoop:·gc.count 50 avgt 10 ≈ 0 counts 93 | // IteratorGC.rawForLoop 1000 avgt 10 3960.977 ± 44.419 ns/op 94 | // IteratorGC.rawForLoop:·gc.alloc.rate 1000 avgt 10 ≈ 10⁻⁴ MB/sec 95 | // IteratorGC.rawForLoop:·gc.alloc.rate.norm 1000 avgt 10 0.002 ± 0.001 B/op 96 | // IteratorGC.rawForLoop:·gc.count 1000 avgt 10 ≈ 0 counts 97 | // 98 | // The interesting line to look at for each benchmark is gc.alloc.rate.norm 99 | // which shows the heap allocation rate per benchmark iteration. 100 | // NONE OF THE BENCHMARK RUNS ALLOCATE! None whatsoever. The iterator 101 | // _always_ gets escape-analyzed away by the JVM; that's very unsurprising 102 | // gives that removing iterator GC overhead was one of the primary reasons 103 | // why escape analysis was added to the JVM! 104 | // DON'T BE FOOLED BY THE TIMING RESULTS! Using blackhole.consume prevents 105 | // clever loop optimizations the JVM can perform, like iteration fusing, 106 | // loop unrolling etc. 107 | // We use this benchmark strictly to test GC overhead. To see perf overhead 108 | // of iterators, head over to IteratorPerf.java. 109 | } 110 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_37_CacheAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.infra.Blackhole; 35 | import org.openjdk.jmh.runner.Runner; 36 | import org.openjdk.jmh.runner.RunnerException; 37 | import org.openjdk.jmh.runner.options.Options; 38 | import org.openjdk.jmh.runner.options.OptionsBuilder; 39 | 40 | import java.util.Random; 41 | import java.util.concurrent.TimeUnit; 42 | 43 | @BenchmarkMode(Mode.AverageTime) 44 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 45 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 46 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 47 | @Fork(5) 48 | @State(Scope.Benchmark) 49 | public class JMHSample_37_CacheAccess { 50 | 51 | /* 52 | * This sample serves as a warning against subtle differences in cache access patterns. 53 | * 54 | * Many performance differences may be explained by the way tests are accessing memory. 55 | * In the example below, we walk the matrix either row-first, or col-first: 56 | */ 57 | 58 | private final static int COUNT = 4096; 59 | private final static int MATRIX_SIZE = COUNT * COUNT; 60 | 61 | private int[][] matrix; 62 | 63 | @Setup 64 | public void setup() { 65 | matrix = new int[COUNT][COUNT]; 66 | Random random = new Random(1234); 67 | for (int i = 0; i < COUNT; i++) { 68 | for (int j = 0; j < COUNT; j++) { 69 | matrix[i][j] = random.nextInt(); 70 | } 71 | } 72 | } 73 | 74 | @Benchmark 75 | @OperationsPerInvocation(MATRIX_SIZE) 76 | public void colFirst(Blackhole bh) { 77 | for (int c = 0; c < COUNT; c++) { 78 | for (int r = 0; r < COUNT; r++) { 79 | bh.consume(matrix[r][c]); 80 | } 81 | } 82 | } 83 | 84 | @Benchmark 85 | @OperationsPerInvocation(MATRIX_SIZE) 86 | public void rowFirst(Blackhole bh) { 87 | for (int r = 0; r < COUNT; r++) { 88 | for (int c = 0; c < COUNT; c++) { 89 | bh.consume(matrix[r][c]); 90 | } 91 | } 92 | } 93 | 94 | /* 95 | Notably, colFirst accesses are much slower, and that's not a surprise: Java's multidimensional 96 | arrays are actually rigged, being one-dimensional arrays of one-dimensional arrays. Therefore, 97 | pulling n-th element from each of the inner array induces more cache misses, when matrix is large. 98 | -prof perfnorm conveniently highlights that, with >2 cache misses per one benchmark op: 99 | 100 | Benchmark Mode Cnt Score Error Units 101 | JMHSample_37_MatrixCopy.colFirst avgt 25 5.306 ± 0.020 ns/op 102 | JMHSample_37_MatrixCopy.colFirst:·CPI avgt 5 0.621 ± 0.011 #/op 103 | JMHSample_37_MatrixCopy.colFirst:·L1-dcache-load-misses avgt 5 2.177 ± 0.044 #/op <-- OOPS 104 | JMHSample_37_MatrixCopy.colFirst:·L1-dcache-loads avgt 5 14.804 ± 0.261 #/op 105 | JMHSample_37_MatrixCopy.colFirst:·LLC-loads avgt 5 2.165 ± 0.091 #/op 106 | JMHSample_37_MatrixCopy.colFirst:·cycles avgt 5 22.272 ± 0.372 #/op 107 | JMHSample_37_MatrixCopy.colFirst:·instructions avgt 5 35.888 ± 1.215 #/op 108 | 109 | JMHSample_37_MatrixCopy.rowFirst avgt 25 2.662 ± 0.003 ns/op 110 | JMHSample_37_MatrixCopy.rowFirst:·CPI avgt 5 0.312 ± 0.003 #/op 111 | JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-load-misses avgt 5 0.066 ± 0.001 #/op 112 | JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-loads avgt 5 14.570 ± 0.400 #/op 113 | JMHSample_37_MatrixCopy.rowFirst:·LLC-loads avgt 5 0.002 ± 0.001 #/op 114 | JMHSample_37_MatrixCopy.rowFirst:·cycles avgt 5 11.046 ± 0.343 #/op 115 | JMHSample_37_MatrixCopy.rowFirst:·instructions avgt 5 35.416 ± 1.248 #/op 116 | 117 | So, when comparing two different benchmarks, you have to follow up if the difference is caused 118 | by the memory locality issues. 119 | */ 120 | 121 | /* 122 | * ============================== HOW TO RUN THIS TEST: ==================================== 123 | * 124 | * You can run this test: 125 | * 126 | * a) Via the command line: 127 | * $ mvn clean install 128 | * $ java -jar target/benchmarks.jar JMHSample_37 129 | * 130 | * b) Via the Java API: 131 | * (see the JMH homepage for possible caveats when running from IDE: 132 | * http://openjdk.java.net/projects/code-tools/jmh/) 133 | */ 134 | public static void main(String[] args) throws RunnerException { 135 | Options opt = new OptionsBuilder() 136 | .include(".*" + JMHSample_37_CacheAccess.class.getSimpleName() + ".*") 137 | .build(); 138 | 139 | new Runner(opt).run(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/jmh/java/org/openjdk/jmh/samples/JMHSample_36_BranchPrediction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Oracle America, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * * Neither the name of Oracle nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | * THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | package org.openjdk.jmh.samples; 32 | 33 | import org.openjdk.jmh.annotations.*; 34 | import org.openjdk.jmh.infra.Blackhole; 35 | import org.openjdk.jmh.runner.Runner; 36 | import org.openjdk.jmh.runner.RunnerException; 37 | import org.openjdk.jmh.runner.options.Options; 38 | import org.openjdk.jmh.runner.options.OptionsBuilder; 39 | 40 | import java.util.Arrays; 41 | import java.util.Random; 42 | import java.util.concurrent.TimeUnit; 43 | 44 | @BenchmarkMode(Mode.AverageTime) 45 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 46 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 47 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 48 | @Fork(5) 49 | @State(Scope.Benchmark) 50 | public class JMHSample_36_BranchPrediction { 51 | 52 | /* 53 | * This sample serves as a warning against regular data sets. 54 | * 55 | * It is very tempting to present a regular data set to benchmark, either due to 56 | * naive generation strategy, or just from feeling better about regular data sets. 57 | * Unfortunately, it frequently backfires: the regular datasets are known to be 58 | * optimized well by software and hardware. This example exploits one of these 59 | * optimizations: branch prediction. 60 | * 61 | * Imagine our benchmark selects the branch based on the array contents, as 62 | * we are streaming through it: 63 | */ 64 | 65 | private static final int COUNT = 1024 * 1024; 66 | 67 | private byte[] sorted; 68 | private byte[] unsorted; 69 | 70 | @Setup 71 | public void setup() { 72 | sorted = new byte[COUNT]; 73 | unsorted = new byte[COUNT]; 74 | Random random = new Random(1234); 75 | random.nextBytes(sorted); 76 | random.nextBytes(unsorted); 77 | Arrays.sort(sorted); 78 | } 79 | 80 | @Benchmark 81 | @OperationsPerInvocation(COUNT) 82 | public void sorted(Blackhole bh1, Blackhole bh2) { 83 | for (byte v : sorted) { 84 | if (v > 0) { 85 | bh1.consume(v); 86 | } else { 87 | bh2.consume(v); 88 | } 89 | } 90 | } 91 | 92 | @Benchmark 93 | @OperationsPerInvocation(COUNT) 94 | public void unsorted(Blackhole bh1, Blackhole bh2) { 95 | for (byte v : unsorted) { 96 | if (v > 0) { 97 | bh1.consume(v); 98 | } else { 99 | bh2.consume(v); 100 | } 101 | } 102 | } 103 | 104 | /* 105 | There is a substantial difference in performance for these benchmarks! 106 | 107 | It is explained by good branch prediction in "sorted" case, and branch mispredicts in "unsorted" 108 | case. -prof perfnorm conveniently highlights that, with larger "branch-misses", and larger "CPI" 109 | for "unsorted" case: 110 | 111 | Benchmark Mode Cnt Score Error Units 112 | JMHSample_36_BranchPrediction.sorted avgt 25 2.160 ± 0.049 ns/op 113 | JMHSample_36_BranchPrediction.sorted:·CPI avgt 5 0.286 ± 0.025 #/op 114 | JMHSample_36_BranchPrediction.sorted:·branch-misses avgt 5 ≈ 10⁻⁴ #/op 115 | JMHSample_36_BranchPrediction.sorted:·branches avgt 5 7.606 ± 1.742 #/op 116 | JMHSample_36_BranchPrediction.sorted:·cycles avgt 5 8.998 ± 1.081 #/op 117 | JMHSample_36_BranchPrediction.sorted:·instructions avgt 5 31.442 ± 4.899 #/op 118 | 119 | JMHSample_36_BranchPrediction.unsorted avgt 25 5.943 ± 0.018 ns/op 120 | JMHSample_36_BranchPrediction.unsorted:·CPI avgt 5 0.775 ± 0.052 #/op 121 | JMHSample_36_BranchPrediction.unsorted:·branch-misses avgt 5 0.529 ± 0.026 #/op <--- OOPS 122 | JMHSample_36_BranchPrediction.unsorted:·branches avgt 5 7.841 ± 0.046 #/op 123 | JMHSample_36_BranchPrediction.unsorted:·cycles avgt 5 24.793 ± 0.434 #/op 124 | JMHSample_36_BranchPrediction.unsorted:·instructions avgt 5 31.994 ± 2.342 #/op 125 | 126 | It is an open question if you want to measure only one of these tests. In many cases, you have to measure 127 | both to get the proper best-case and worst-case estimate! 128 | */ 129 | 130 | 131 | /* 132 | * ============================== HOW TO RUN THIS TEST: ==================================== 133 | * 134 | * You can run this test: 135 | * 136 | * a) Via the command line: 137 | * $ mvn clean install 138 | * $ java -jar target/benchmarks.jar JMHSample_36 139 | * 140 | * b) Via the Java API: 141 | * (see the JMH homepage for possible caveats when running from IDE: 142 | * http://openjdk.java.net/projects/code-tools/jmh/) 143 | */ 144 | public static void main(String[] args) throws RunnerException { 145 | Options opt = new OptionsBuilder() 146 | .include(".*" + JMHSample_36_BranchPrediction.class.getSimpleName() + ".*") 147 | .build(); 148 | 149 | new Runner(opt).run(); 150 | } 151 | 152 | } 153 | --------------------------------------------------------------------------------