当前位置: 移动技术网 > IT编程>开发语言>Java > JMH使用说明

JMH使用说明

2018年11月08日  | 移动技术网IT编程  | 我要评论

一、

  性能往往是特定情景下的评价,泛泛地说性能“好”或者“快”,往是具有误导性的。通过引入基准测试,我们可以定义性能对比的明确条件、具体的指标,进而保证得到定量的、可重复的对比数据,这是工程中的实际需要。

  不同的基准测试其具体内容和范围也存在很大的不同。如果是专业的性能工程师,更加熟悉的可能是类似spec提供的工业标准的系统级测试;而对于大多数 java 开发者,更熟悉的则是范围相对较小、关注点更加细节的微基准测试(micro-benchmark)。

 

  目前应用最为广泛的框架之一就是jmh,openjdk 自身也大量地使用 jmh 进行性能对比,如果你是做 java api 级别的性能对比,jmh 往往是你的首选。

 

二、如果要在现有maven项目中使用jmh,只需要把生成出来的两个依赖以及shade插件拷贝到项目的pom中即可:

        <dependency>
            <groupid>org.openjdk.jmh</groupid>
            <artifactid>jmh-core</artifactid>
            <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
            <version>1.19</version>
        </dependency>
        <dependency>
            <groupid>org.openjdk.jmh</groupid>
            <artifactid>jmh-generator-annprocess</artifactid>
            <version>1.19</version>
            <scope>provided</scope>
        </dependency>


    <build>
        <plugins>

            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-shade-plugin</artifactid>
                <version>2.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <finalname>microbenchmarks</finalname>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.manifestresourcetransformer">
                                    <mainclass>org.openjdk.jmh.main</mainclass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

 

testjmh.java

package com.jmh;

import java.util.concurrent.timeunit;
import org.openjdk.jmh.annotations.benchmark;
import org.openjdk.jmh.annotations.benchmarkmode;
import org.openjdk.jmh.annotations.mode;
import org.openjdk.jmh.annotations.outputtimeunit;
import org.openjdk.jmh.annotations.scope;
import org.openjdk.jmh.annotations.state;
import org.openjdk.jmh.runner.runner;
import org.openjdk.jmh.runner.runnerexception;
import org.openjdk.jmh.runner.options.options;
import org.openjdk.jmh.runner.options.optionsbuilder;

@benchmarkmode(mode.throughput) // 测试方法平均执行时间
@outputtimeunit(timeunit.microseconds) // 输出结果的时间粒度为微秒
@state(scope.thread) // 每个测试线程一个实例
public class testjmh {

    @benchmark
        public string stringconcat() {
            string a = "a";
            string b = "b";
            string c = "c";
            string s = a + b + c;
            system.out.println(s);
            return s;
        }
        
        
        public static void main(string[] args) throws runnerexception {
            options opt = new optionsbuilder().include(testjmh.class.getsimplename()).forks(1).warmupiterations(5)
                    .measurementiterations(5).build();
            new runner(opt).run();
        }
}

 

 

三、详细说明

3.1 基本概念

首先看看jmh的几个基本概念:

 

mode 

mode 表示 jmh 进行 benchmark 时所使用的模式。通常是测量的维度不同,或是测量的方式不同。目前 jmh 共有四种模式:

 

throughput: 整体吞吐量,例如“1秒内可以执行多少次调用”。

 

averagetime: 调用的平均时间,例如“每次调用平均耗时xxx毫秒”。

 

sampletime: 随机取样,最后输出取样结果的分布,例如“99%的调用在xxx毫秒以内,99.99%的调用在xxx毫秒以内”

 

singleshottime: 以上模式都是默认一次 iteration 是 1s,唯有 singleshottime 是只运行一次。往往同时把 warmup 次数设为0,用于测试冷启动时的性能。

 

iteration 

iteration 是 jmh 进行测试的最小单位。在大部分模式下,一次 iteration 代表的是一秒,jmh 会在这一秒内不断调用需要 benchmark 的方法,然后根据模式对其采样,计算吞吐量,计算平均执行时间等。

 

warmup

 

warmup 是指在实际进行 benchmark 前先进行预热的行为。为什么需要预热?因为 jvm 的 jit 机制的存在,如果某个函数被调用多次之后,jvm 会尝试将其编译成为机器码从而提高执行速度。为了让 benchmark 的结果更加接近真实情况就需要进行预热。

 

3.2 注解与选项

3.2.1 常用注解说明

@benchmarkmode 

对应mode选项,可用于类或者方法上, 需要注意的是,这个注解的value是一个数组,可以把几种mode集合在一起执行,还可以设置为mode.all,即全部执行一遍。

 

@state 

类注解,jmh测试类必须使用@state注解,state定义了一个类实例的生命周期,可以类比spring bean的scope。由于jmh允许多线程同时执行测试,不同的选项含义如下:

 

scope.thread:默认的state,每个测试线程分配一个实例;

 

scope.benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;

 

scope.group:每个线程组共享一个实例;

 

@outputtimeunit 

benchmark 结果所使用的时间单位,可用于类或者方法注解,使用java.util.concurrent.timeunit中的标准时间单位。

 

@benchmark 

方法注解,表示该方法是需要进行 benchmark 的对象。

 

@setup 

方法注解,会在执行 benchmark 之前被执行,正如其名,主要用于初始化。

 

@teardown 

方法注解,与@setup 相对的,会在所有 benchmark 执行结束以后执行,主要用于资源的回收等。

 

@param 

成员注解,可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。@param注解接收一个string数组,在@setup方法执行前转化为为对应的数据类型。多个@param注解的成员之间是乘积关系,譬如有两个用@param注解的字段,第一个有5个值,第二个字段有2个值,那么每个测试方法会跑5*2=10次。

 

原文:https://blog.csdn.net/lxbjkben/article/details/79410740 

jmh使用说明一、概述jmh,即java microbenchmark harness,是专门用于代码微基准测试的工具套件。何谓micro benchmark呢?简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用jmh对优化的结果进行量化的分析。和其他竞品相比——如果有的话,jmh最有特色的地方就是,它是由oracle内部实现jit的那拨人开发的,对于jit以及jvm所谓的“profile guided optimization”对基准测试准确性的影响可谓心知肚明(smile)
jmh比较典型的应用场景有:
想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性;对比接口不同实现在给定条件下的吞吐量;查看多少百分比的请求在多长时间内完成;二、第一个例子接下来,我们看看如何使用jmh。
要使用jmh,首先需要准备好maven环境,jmh的源代码以及官方提供的sample就是使用maven进行项目管理的,github上也有使用gradle的例子可自行搜索参考。使用mvn命令行创建一个jmh工程:
mvn archetype:generate \          -dinteractivemode=false \          -darchetypegroupid=org.openjdk.jmh \          -darchetypeartifactid=jmh-java-benchmark-archetype \          -dgroupid=co.speedar.infra \          -dartifactid=jmh-test \          -dversion=1.01234567如果要在现有maven项目中使用jmh,只需要把生成出来的两个依赖以及shade插件拷贝到项目的pom中即可:
    <dependency>        <groupid>org.openjdk.jmh</groupid>        <artifactid>jmh-core</artifactid>        <version>0.7.1</version>    </dependency>    <dependency>        <groupid>org.openjdk.jmh</groupid>        <artifactid>jmh-generator-annprocess</artifactid>        <version>0.7.1</version>        <scope>provided</scope>    </dependency>...    <plugin>        <groupid>org.apache.maven.plugins</groupid>        <artifactid>maven-shade-plugin</artifactid>        <version>2.0</version>        <executions>            <execution>                <phase>package</phase>                <goals>                    <goal>shade</goal>                </goals>                <configuration>                    <finalname>microbenchmarks</finalname>                    <transformers>                        <transformer implementation="org.apache.maven.plugins.shade.resource.manifestresourcetransformer">                            <mainclass>org.openjdk.jmh.main</mainclass>                        </transformer>                    </transformers>                </configuration>            </execution>        </executions>    </plugin>123456789101112131415161718192021222324252627282930313233然后,就可以着手写第一个jmh例子了:
package co.speedar.infra.test;import java.util.concurrent.timeunit;import org.openjdk.jmh.annotations.benchmark;import org.openjdk.jmh.annotations.benchmarkmode;import org.openjdk.jmh.annotations.mode;import org.openjdk.jmh.annotations.outputtimeunit;import org.openjdk.jmh.annotations.scope;import org.openjdk.jmh.annotations.state;import org.openjdk.jmh.runner.runner;import org.openjdk.jmh.runner.runnerexception;import org.openjdk.jmh.runner.options.options;import org.openjdk.jmh.runner.options.optionsbuilder;import org.slf4j.logger;import org.slf4j.loggerfactory;@benchmarkmode(mode.averagetime) // 测试方法平均执行时间@outputtimeunit(timeunit.microseconds) // 输出结果的时间粒度为微秒@state(scope.thread) // 每个测试线程一个实例public class firstbenchmark {    private static logger log = loggerfactory.getlogger(firstbenchmark.class);    @benchmark    public string stringconcat() {        string a = "a";        string b = "b";        string c = "c";        string s = a + b + c;        log.debug(s);        return s;    }    public static void main(string[] args) throws runnerexception {        // 使用一个单独进程执行测试,执行5遍warmup,然后执行5遍测试        options opt = new optionsbuilder().include(firstbenchmark.class.getsimplename()).forks(1).warmupiterations(5)                .measurementiterations(5).build();        new runner(opt).run();    }}1234567891011121314151617181920212223242526272829303132333435在上面的测试代码中,加了几个类注解以及一个方法注解,在main方法中指明了测试的一些选项,然后使用jmh提供的runner执行测试。在注释中提供了大致的讲解,具体的选项说明后边再详述。接下来我们直接跑起来这个测试看看结果如何。执行测试,可能会遇到报错: exception in thread "main" java.lang.runtimeexception: error: unable to find the resource: /meta-inf/benchmarklist 解决方法:
先执行mvn clean install然后再在ide中执行main方法;或者在eclipse中安装m2e-apt插件,然后启用automatically configure jdt apt选项;  

然后,就可以愉快地看到测试结果如下:
# jmh 1.14.1 (released 525 days ago, please consider updating!)# vm version: jdk 1.8.0_91, vm 25.91-b14# vm invoker: /library/java/javavirtualmachines/jdk1.8.0_91.jdk/contents/home/jre/bin/java# vm options: -dfile.encoding=utf-8# warmup: 5 iterations, 1 s each# measurement: 5 iterations, 1 s each# timeout: 10 min per iteration# threads: 1 thread, will synchronize iterations# benchmark mode: average time, time/op# benchmark: co.speedar.infra.test.firstbenchmark.stringconcat# run progress: 0.00% complete, eta 00:00:10# fork: 1 of 1# warmup iteration   1: 0.009 us/op# warmup iteration   2: 0.011 us/op# warmup iteration   3: 0.007 us/op# warmup iteration   4: 0.006 us/op# warmup iteration   5: 0.006 us/opiteration   1: 0.006 us/opiteration   2: 0.005 us/opiteration   3: 0.005 us/opiteration   4: 0.006 us/opiteration   5: 0.006 us/op
result "stringconcat":  0.006 ±(99.9%) 0.001 us/op [average]  (min, avg, max) = (0.005, 0.006, 0.006), stdev = 0.001  ci (99.9%): [0.005, 0.006] (assumes normal distribution)
# run complete. total time: 00:00:10benchmark                    mode  cnt  score    error  unitsfirstbenchmark.stringconcat  avgt    5  0.006 ±  0.001  us/op12345678910111213141516171819202122232425262728293031测试结果表明,被测试方法平均耗时为0.006微秒,误差为±0.001微秒。
三、详细说明3.1 基本概念首先看看jmh的几个基本概念:
mode mode 表示 jmh 进行 benchmark 时所使用的模式。通常是测量的维度不同,或是测量的方式不同。目前 jmh 共有四种模式:
throughput: 整体吞吐量,例如“1秒内可以执行多少次调用”。
averagetime: 调用的平均时间,例如“每次调用平均耗时xxx毫秒”。
sampletime: 随机取样,最后输出取样结果的分布,例如“99%的调用在xxx毫秒以内,99.99%的调用在xxx毫秒以内”
singleshottime: 以上模式都是默认一次 iteration 是 1s,唯有 singleshottime 是只运行一次。往往同时把 warmup 次数设为0,用于测试冷启动时的性能。
iteration iteration 是 jmh 进行测试的最小单位。在大部分模式下,一次 iteration 代表的是一秒,jmh 会在这一秒内不断调用需要 benchmark 的方法,然后根据模式对其采样,计算吞吐量,计算平均执行时间等。
warmup
warmup 是指在实际进行 benchmark 前先进行预热的行为。为什么需要预热?因为 jvm 的 jit 机制的存在,如果某个函数被调用多次之后,jvm 会尝试将其编译成为机器码从而提高执行速度。为了让 benchmark 的结果更加接近真实情况就需要进行预热。
3.2 注解与选项3.2.1 常用注解说明@benchmarkmode 对应mode选项,可用于类或者方法上, 需要注意的是,这个注解的value是一个数组,可以把几种mode集合在一起执行,还可以设置为mode.all,即全部执行一遍。
@state 类注解,jmh测试类必须使用@state注解,state定义了一个类实例的生命周期,可以类比spring bean的scope。由于jmh允许多线程同时执行测试,不同的选项含义如下:
scope.thread:默认的state,每个测试线程分配一个实例;
scope.benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;
scope.group:每个线程组共享一个实例;
@outputtimeunit benchmark 结果所使用的时间单位,可用于类或者方法注解,使用java.util.concurrent.timeunit中的标准时间单位。
@benchmark 方法注解,表示该方法是需要进行 benchmark 的对象。
@setup 方法注解,会在执行 benchmark 之前被执行,正如其名,主要用于初始化。
@teardown 方法注解,与@setup 相对的,会在所有 benchmark 执行结束以后执行,主要用于资源的回收等。
@param 成员注解,可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。@param注解接收一个string数组,在@setup方法执行前转化为为对应的数据类型。多个@param注解的成员之间是乘积关系,譬如有两个用@param注解的字段,第一个有5个值,第二个字段有2个值,那么每个测试方法会跑5*2=10次。
3.2.2 注解使用例子以下示例代码来自jmh官方例子,为了节省篇幅删除了头部的license声明和重复的注释。
@benchmarkmode和@outputtimeunitpublic class jmhsample_02_benchmarkmodes {    @benchmark    @benchmarkmode(mode.throughput)    @outputtimeunit(timeunit.seconds)    public void measurethroughput() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * mode.averagetime measures the average execution time, and it does it     * in the way similar to mode.throughput.     *     * some might say it is the reciprocal throughput, and it really is.     * there are workloads where measuring times is more convenient though.     */    @benchmark    @benchmarkmode(mode.averagetime)    @outputtimeunit(timeunit.microseconds)    public void measureavgtime() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * mode.sampletime samples the execution time. with this mode, we are     * still running the method in a time-bound iteration, but instead of     * measuring the total time, we measure the time spent in *some* of     * the benchmark method calls.     *     * this allows us to infer the distributions, percentiles, etc.     *     * jmh also tries to auto-adjust sampling frequency: if the method     * is long enough, you will end up capturing all the samples.     */    @benchmark    @benchmarkmode(mode.sampletime)    @outputtimeunit(timeunit.microseconds)    public void measuresamples() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * mode.singleshottime measures the single method invocation time. as the javadoc     * suggests, we do only the single benchmark method invocation. the iteration     * time is meaningless in this mode: as soon as benchmark method stops, the     * iteration is over.     *     * this mode is useful to do cold startup tests, when you specifically     * do not want to call the benchmark method continuously.     */    @benchmark    @benchmarkmode(mode.singleshottime)    @outputtimeunit(timeunit.microseconds)    public void measuresingleshot() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * we can also ask for multiple benchmark modes at once. all the tests     * above can be replaced with just a single test like this:     */    @benchmark    @benchmarkmode({mode.throughput, mode.averagetime, mode.sampletime, mode.singleshottime})    @outputtimeunit(timeunit.microseconds)    public void measuremultiple() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * or even...     */    @benchmark    @benchmarkmode(mode.all)    @outputtimeunit(timeunit.microseconds)    public void measureall() throws interruptedexception {        timeunit.milliseconds.sleep(100);    }    /*     * ============================== how to run this test: ====================================     *     * you are expected to see the different run modes for the same benchmark.     * note the units are different, scores are consistent with each other.     *     * you can run this test:     *     * a) via the command line:     *    $ mvn clean install     *    $ java -jar target/benchmarks.jar jmhsample_02 -wi 5 -i 5 -f 1     *    (we requested 5 warmup/measurement iterations, single fork)     *     * b) via the java api:     *    (see the jmh homepage for possible caveats when running from ide:     *      http://openjdk.java.net/projects/code-tools/jmh/)     */    public static void main(string[] args) throws runnerexception {        options opt = new optionsbuilder()                .include(jmhsample_02_benchmarkmodes.class.getsimplename())                .warmupiterations(5)                .measurementiterations(5)                .forks(1)                .build();        new runner(opt).run();    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798@statepublic class jmhsample_03_states {    @state(scope.benchmark)    public static class benchmarkstate {        volatile double x = math.pi;    }    @state(scope.thread)    public static class threadstate {        volatile double x = math.pi;    }    /*     * benchmark methods can reference the states, and jmh will inject the     * appropriate states while calling these methods. you can have no states at     * all, or have only one state, or have multiple states referenced. this     * makes building multi-threaded benchmark a breeze.     *     * for this exercise, we have two methods.     */    @benchmark    public void measureunshared(threadstate state) {        // all benchmark threads will call in this method.        //        // however, since threadstate is the scope.thread, each thread        // will have it's own copy of the state, and this benchmark        // will measure unshared case.        state.x++;    }    @benchmark    public void measureshared(benchmarkstate state) {        // all benchmark threads will call in this method.        //        // since benchmarkstate is the scope.benchmark, all threads        // will share the state instance, and we will end up measuring        // shared case.        state.x++;    }
    public static void main(string[] args) throws runnerexception {        options opt = new optionsbuilder()                .include(jmhsample_03_states.class.getsimplename())                .warmupiterations(5)                .measurementiterations(5)                .threads(4)                .forks(1)                .build();        new runner(opt).run();    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@param@benchmarkmode(mode.averagetime)@outputtimeunit(timeunit.nanoseconds)@warmup(iterations = 5, time = 1, timeunit = timeunit.seconds)@measurement(iterations = 5, time = 1, timeunit = timeunit.seconds)@fork(1)@state(scope.benchmark)public class jmhsample_27_params {    /**     * in many cases, the experiments require walking the configuration space     * for a benchmark. this is needed for additional control, or investigating     * how the workload performance changes with different settings.     */    @param({"1", "31", "65", "101", "103"})    public int arg;    @param({"0", "1", "2", "4", "8", "16", "32"})    public int certainty;    @benchmark    public boolean bench() {        return biginteger.valueof(arg).isprobableprime(certainty);    }    public static void main(string[] args) throws runnerexception {        options opt = new optionsbuilder()                .include(jmhsample_27_params.class.getsimplename())//                .param("arg", "41", "42") // use this to selectively constrain/override parameters                .build();        new runner(opt).run();    }}12345678910111213141516171819202122232425262728293.2.3 常用选项说明include benchmark 所在的类的名字,这里可以使用正则表达式对所有类进行匹配。
fork jvm因为使用了profile-guided optimization而“臭名昭著”,这对于微基准测试来说十分不友好,因为不同测试方法的profile混杂在一起,“互相伤害”彼此的测试结果。对于每个@benchmark方法使用一个独立的进程可以解决这个问题,这也是jmh的默认选项。注意不要设置为0,设置为n则会启动n个进程执行测试(似乎也没有太大意义)。fork选项也可以通过方法注解以及启动参数来设置。
warmupiterations 预热的迭代次数,默认1秒。
measurementiterations 实际测量的迭代次数,默认1秒。
compilercontrol 可以在@benchmark注解中指定编译器行为。
compilercontrol.mode.dont_inline:this method should not be inlined. useful to measure the method call cost and to evaluate if it worth to increase the inline threshold for the jvm.compilercontrol.mode.inline:ask the compiler to inline this method. usually should be used in conjunction with mode.dont_inline to check pros and cons of inlining.compilercontrol.mode.exclude:do not compile this method – interpret it instead. useful in holy wars as an argument how good is the jit.group 方法注解,可以把多个 benchmark 定义为同一个 group,则它们会被同时执行,譬如用来模拟生产者-消费者读写速度不一致情况下的表现。可以参考如下例子: counterbenchmark.java
level 用于控制 @setup,@teardown 的调用时机,默认是 level.trial。
trial:每个benchmark方法前后;
iteration:每个benchmark方法每次迭代前后;
invocation:每个benchmark方法每次调用前后,谨慎使用,需留意javadoc注释;
threads 每个fork进程使用多少条线程去执行你的测试方法,默认值是runtime.getruntime().availableprocessors()。
四、一些值得注意的地方4.1 无用代码消除(dead code elimination)现代编译器是十分聪明的,它们会对你的代码进行推导分析,判定哪些代码是无用的然后进行去除,这种行为对微基准测试是致命的,它会使你无法准确测试出你的方法性能。jmh本身已经对这种情况做了处理,你只要记住:1.永远不要写void方法;2.在方法结束返回你的计算结果。有时候如果需要返回多于一个结果,可以考虑自行合并计算结果,或者使用jmh提供的blackhole对象:
/* * this demonstrates option a: * * merge multiple results into one and return it. * this is ok when is computation is relatively heavyweight, and merging * the results does not offset the results much. */@benchmarkpublic double measureright_1() {    return math.log(x1) + math.log(x2);}/* * this demonstrates option b: * * use explicit blackhole objects, and sink the values there. * (background: blackhole is just another @state object, bundled with jmh). */@benchmarkpublic void measureright_2(blackhole bh) {    bh.consume(math.log(x1));    bh.consume(math.log(x2));}123456789101112131415161718192021224.2 常量折叠(constant folding)常量折叠是一种现代编译器优化策略,例如,i = 320 * 200 * 32,多数的现代编译器不会真的产生两个乘法的指令再将结果储存下来,取而代之的,他们会辨识出语句的结构,并在编译时期将数值计算出来(i = 2,048,000)。
在微基准测试中,如果你的计算输入是可预测的,也不是一个@state实例变量,那么很可能会被jit给优化掉。对此,jmh的建议是:1.永远从@state实例中读取你的方法输入;2.返回你的计算结果;3.或者考虑使用blackhole对象;
见如下官方例子:
@state(scope.thread)@benchmarkmode(mode.averagetime)@outputtimeunit(timeunit.nanoseconds)public class jmhsample_10_constantfold {    private double x = math.pi;    private final double wrongx = math.pi;    @benchmark    public double baseline() {        // simply return the value, this is a baseline        return math.pi;    }    @benchmark    public double measurewrong_1() {        // this is wrong: the source is predictable, and computation is foldable.        return math.log(math.pi);    }    @benchmark    public double measurewrong_2() {        // this is wrong: the source is predictable, and computation is foldable.        return math.log(wrongx);    }    @benchmark    public double measureright() {        // this is correct: the source is not predictable.        return math.log(x);    }    public static void main(string[] args) throws runnerexception {        options opt = new optionsbuilder()                .include(jmhsample_10_constantfold.class.getsimplename())                .warmupiterations(5)                .measurementiterations(5)                .forks(1)                .build();        new runner(opt).run();    }}1234567891011121314151617181920212223242526272829303132333435364.3 循环展开(loop unwinding)循环展开最常用来降低循环开销,为具有多个功能单元的处理器提供指令级并行。也有利于指令流水线的调度。例如:
for (i = 1; i <= 60; i++)    a[i] = a[i] * b + c;12可以展开成:
for (i = 1; i <= 60; i+=3){  a[i] = a[i] * b + c;  a[i+1] = a[i+1] * b + c;  a[i+2] = a[i+2] * b + c;}123456由于编译器可能会对你的代码进行循环展开,因此jmh建议不要在你的测试方法中写任何循环。如果确实需要执行循环计算,可以结合@benchmarkmode(mode.singleshottime)和@measurement(batchsize = n)来达到同样的效果。参考如下例子:
/* * suppose we want to measure how much it takes to sum two integers: */int x = 1;int y = 2;/* * this is what you do with jmh. */@benchmark@operationsperinvocation(100)public int measureright() {    return (x + y);}12345678910111213还有这个例子:
@state(scope.thread)@warmup(iterations = 5, time = 1, timeunit = timeunit.seconds)@measurement(iterations = 5, time = 1, timeunit = timeunit.seconds)@fork(3)@benchmarkmode(mode.averagetime)@outputtimeunit(timeunit.nanoseconds)public class jmhsample_34_safelooping {    /*     * jmhsample_11_loops warns about the dangers of using loops in @benchmark methods.     * sometimes, however, one needs to traverse through several elements in a dataset.     * this is hard to do without loops, and therefore we need to devise a scheme for     * safe looping.     */    /*     * suppose we want to measure how much it takes to execute work() with different     * arguments. this mimics a frequent use case when multiple instances with the same     * implementation, but different data, is measured.     */    static final int base = 42;    static int work(int x) {        return base + x;    }    /*     * every benchmark requires control. we do a trivial control for our benchmarks     * by checking the benchmark costs are growing linearly with increased task size.     * if it doesn't, then something wrong is happening.     */    @param({"1", "10", "100", "1000"})    int size;    int[] xs;    @setup    public void setup() {        xs = new int[size];        for (int c = 0; c < size; c++) {            xs[c] = c;        }    }    /*     * first, the obviously wrong way: "saving" the result into a local variable would not     * work. a sufficiently smart compiler will inline work(), and figure out only the last     * work() call needs to be evaluated. indeed, if you run it with varying $size, the score     * will stay the same!     */    @benchmark    public int measurewrong_1() {        int acc = 0;        for (int x : xs) {            acc = work(x);        }        return acc;    }    /*     * second, another wrong way: "accumulating" the result into a local variable. while     * it would force the computation of each work() method, there are software pipelining     * effects in action, that can merge the operations between two otherwise distinct work()     * bodies. this will obliterate the benchmark setup.     *     * in this example, hotspot does the unrolled loop, merges the $base operands into a single     * addition to $acc, and then does a bunch of very tight stores of $x-s. the final performance     * depends on how much of the loop unrolling happened *and* how much data is available to make     * the large strides.     */    @benchmark    public int measurewrong_2() {        int acc = 0;        for (int x : xs) {            acc += work(x);        }        return acc;    }    /*     * now, let's see how to measure these things properly. a very straight-forward way to     * break the merging is to sink each result to blackhole. this will force runtime to compute     * every work() call in full. (we would normally like to care about several concurrent work()     * computations at once, but the memory effects from blackhole.consume() prevent those optimization     * on most runtimes).     */    @benchmark    public void measureright_1(blackhole bh) {        for (int x : xs) {            bh.consume(work(x));        }    }    /*     * dangerous area, please read the description below.     *     * sometimes, the cost of sinking the value into a blackhole is dominating the nano-benchmark score.     * in these cases, one may try to do a make-shift "sinker" with non-inlineable method. this trick is     * *very* vm-specific, and can only be used if you are verifying the generated code (that's a good     * strategy when dealing with nano-benchmarks anyway).     *     * you should not use this trick in most cases. apply only where needed.     */    @benchmark    public void measureright_2() {        for (int x : xs) {            sink(work(x));        }    }    @compilercontrol(compilercontrol.mode.dont_inline)    public static void sink(int v) {        // it is very important to match the signature to avoid autoboxing.        // the method intentionally does nothing.    }
    public static void main(string[] args) throws runnerexception {        options opt = new optionsbuilder()                .include(jmhsample_34_safelooping.class.getsimplename())                .warmupiterations(5)                .measurementiterations(5)                .forks(3)                .build();        new runner(opt).run();    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115五、license声明文中大部分例子来自jmh官方的实例工程:jmh-samples,基于节省篇幅考虑去掉了头部的license声明,现补充如下:
/* * copyright (c) 2014, oracle america, inc. * all rights reserved. * * redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * *  * redistributions of source code must retain the above copyright notice, *    this list of conditions and the following disclaimer. * *  * redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * *  * neither the name of oracle nor the names of its contributors may be used *    to endorse or promote products derived from this software without *    specific prior written permission. * * this software is provided by the copyright holders and contributors "as is" * and any express or implied warranties, including, but not limited to, the * implied warranties of merchantability and fitness for a particular purpose * are disclaimed. in no event shall the copyright holder or contributors be * liable for any direct, indirect, incidental, special, exemplary, or * consequential damages (including, but not limited to, procurement of * substitute goods or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, whether in * contract, strict liability, or tort (including negligence or otherwise) * arising in any way out of the use of this software, even if advised of * the possibility of such damage. */--------------------- 作者:秦沙 来源:csdn 原文:https://blog.csdn.net/lxbjkben/article/details/79410740 版权声明:本文为博主原创文章,转载请附上博文链接!

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网