性能调优之JMH必知必会4:JMH的高级用法

2023-05-16

性能调优之JMH必知必会4:JMH的高级用法

    • JMH必知必会系列文章(持续更新)
    • 一、前言
    • 二、JMH的高级用法
      • 1、添加JMH依赖包
      • 2、Asymmetric Benchmark
      • 3、Interrupts Benchmark
      • 4、几大线程安全Map的性能对比

 

JMH必知必会系列文章(持续更新)

  • 性能调优之JMH必知必会1:什么是JMH
  • 性能调优之JMH必知必会2:JMH的基本用法
  • 性能调优之JMH必知必会3:编写正确的微基准测试用例
  • 性能调优之JMH必知必会5:JMH的Profiler

 

一、前言

 
  在前面三篇文章中分别介绍了什么是JMH、JMH的基本法和编写正确的微基准测试用例。现在来介绍JMH的一些高级用法。【单位换算:1秒(s)=1000000微秒(us)=1000000000纳秒(ns)
 
  官方JMH源码(包含样例,在jmh-samples包里)下载地址:https://github.com/openjdk/jmh/tags。
 
  官方JMH样例在线浏览地址:http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/。
 
  本文内容参考书籍《Java高并发编程详解:深入理解并发核心库》,作者为 汪文君 ,读者有需要可以去购买正版书籍。
 
  本文由 @大白有点菜 原创,请勿盗用,转载请说明出处!如果觉得文章还不错,请点点赞,加关注,谢谢!
 

二、JMH的高级用法

1、添加JMH依赖包

  在Maven仓库中搜索依赖包jmh-corejmh-generator-annprocess ,版本为 1.36 。需要注释 jmh-generator-annprocess 包中的“<scope>test</scope>”,不然项目运行会报错。

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.36</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.36</version>
<!--            <scope>test</scope>-->
</dependency>

2、Asymmetric Benchmark

  我们编写的所有基准测试都会被JMH框架根据方法名的字典顺序排序之后串行执行,然而有些时候我们会想要对某个类的读写方法并行执行,比如,我们想要在修改某个原子变量的时候又有其他线程对其进行读取操作。
 
【Asymmetric Benchmark样例代码 - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * JMH测试15:Asymmetric Benchmark样例
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Group)
public class JmhTestApp15_AsymmetricBenchmark {

    private AtomicInteger counter;

    @Setup
    public void init()
    {
        this.counter = new AtomicInteger();
    }

    @GroupThreads(5)
    @Group("q")
    @Benchmark
    public void inc()
    {
        this.counter.incrementAndGet();
    }

    @GroupThreads(5)
    @Group("q")
    @Benchmark
    public int get()
    {
        return this.counter.get();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp15_AsymmetricBenchmark.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

 
【Asymmetric Benchmark样例代码 - 代码运行结果】

Benchmark                               Mode  Cnt  Score   Error  Units
JmhTestApp15_AsymmetricBenchmark.q      avgt    5  0.083 ± 0.002  us/op
JmhTestApp15_AsymmetricBenchmark.q:get  avgt    5  0.034 ± 0.002  us/op
JmhTestApp15_AsymmetricBenchmark.q:inc  avgt    5  0.132 ± 0.006  us/op

  我们在对AtomicInteger进行自增操作的同时又会对其进行读取操作,这就是我们经常见到的高并发环境中某些API的操作方式,同样也是线程安全存在隐患的地方。5个线程对AtomicInteger执行自增操作,5个线程对AtomicInteger执行读取时的性能输出说明如下:

  • group q(5个读线程,5个写线程)的平均响应时间为0.083us,误差为0.002。
  • group q(5个读线程)同时读取AtomicInteger变量的速度为0.034us,误差为0.002。
  • group q(5个写线程)同时修改AtomicInteger变量的速度为0.132us,误差为0.006。

 
【附上官方Asymmetric样例(JMHSample_15_Asymmetric) - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * JMH测试15:官方Asymmetric样例
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Group)
public class JmhTestApp15_Asymmetric {

    /**
     * So far all the tests were symmetric: the same code was executed in all the threads.
     * At times, you need the asymmetric test. JMH provides this with the notion of @Group,
     * which can bind several methods together, and all the threads are distributed among
     * the test methods.
     *
     * 到目前为止,所有测试都是对称的:在所有线程中执行相同的代码。有时,您需要非对称测试。
     * JMH为此提供了@Group的概念,它可以将多个方法绑定在一起,所有线程都分布在测试方法中。
     *
     * Each execution group contains of one or more threads. Each thread within a particular
     * execution group executes one of @Group-annotated @Benchmark methods. Multiple execution
     * groups may participate in the run. The total thread count in the run is rounded to the
     * execution group size, which will only allow the full execution groups.
     *
     * 每个执行组包含一个或多个线程。 特定执行组中的每个线程执行@Group 注释的@Benchmark 方法之一。
     * 多个执行组可能参与运行。 运行中的总线程数四舍五入为执行组大小,这将只允许完整的执行组。
     *
     * Note that two state scopes: Scope.Benchmark and Scope.Thread are not covering all
     * the use cases here -- you either share everything in the state, or share nothing.
     * To break this, we have the middle ground Scope.Group, which marks the state to be
     * shared within the execution group, but not among the execution groups.
     *
     * 请注意,两个状态范围:Scope.Benchmark 和 Scope.Thread 并未涵盖此处的所有用例——您要么共享状态中的所有内容,要么不共享任何内容。
     * 为了打破这一点,我们有中间地带 Scope.Group,它标记要在执行组内共享的状态,但不在执行组之间共享。
     *
     * Putting this all together, the example below means:
     *  a) define the execution group "g", with 3 threads executing inc(), and 1 thread
     *     executing get(), 4 threads per group in total;
     *  b) if we run this test case with 4 threads, then we will have a single execution
     *     group. Generally, running with 4*N threads will create N execution groups, etc.;
     *  c) each execution group has one @State instance to share: that is, execution groups
     *     share the counter within the group, but not across the groups.
     *
     *  将所有这些放在一起,下面的示例意味着:
     *   a) 定义执行组"g",3个线程执行 inc(),1个线程执行 get(),每组4个线程;
     *   b) 如果我们用 4 个线程运行这个测试用例,那么我们将有一个单独的执行组。 一般4*N个线程运行会创建N个执行组等;
     *   c) 每个执行组共享一个 @State 实例:即执行组在组内共享计数器,但不跨组。
     */

    private AtomicInteger counter;

    @Setup
    public void up() {
        counter = new AtomicInteger();
    }

    @Benchmark
    @Group("g")
    @GroupThreads(3)
    public int inc() {
        return counter.incrementAndGet();
    }

    @Benchmark
    @Group("g")
    @GroupThreads(1)
    public int get() {
        return counter.get();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp15_Asymmetric.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

 
【附上官方Asymmetric样例(JMHSample_15_Asymmetric) - 代码运行结果】

Benchmark                               Mode  Cnt   Score   Error  Units
JmhTestApp15_Asymmetric.g               avgt    5  49.144 ± 0.270  ns/op
JmhTestApp15_Asymmetric.g:get           avgt    5  27.448 ± 2.089  ns/op
JmhTestApp15_Asymmetric.g:inc           avgt    5  56.375 ± 0.678  ns/op
JmhTestApp15_AsymmetricBenchmark.q      avgt    5   0.082 ± 0.003  us/op
JmhTestApp15_AsymmetricBenchmark.q:get  avgt    5   0.049 ± 0.002  us/op
JmhTestApp15_AsymmetricBenchmark.q:inc  avgt    5   0.115 ± 0.007  us/op

 
【官方Asymmetric样例(JMHSample_15_Asymmetric)注解 - 谷歌和百度翻译互补】

  到目前为止,所有测试都是对称的:在所有线程中执行相同的代码。有时,您需要非对称测试。JMH为此提供了@Group的概念,它可以将多个方法绑定在一起,所有线程都分布在测试方法中。
 
  每个执行组包含一个或多个线程。 特定执行组中的每个线程执行@Group 注释的@Benchmark 方法之一。多个执行组可能参与运行。 运行中的总线程数四舍五入为执行组大小,这将只允许完整的执行组。
 
  请注意,两个状态范围:Scope.Benchmark 和 Scope.Thread 并未涵盖此处的所有用例——您要么共享状态中的所有内容,要么不共享任何内容。为了打破这一点,我们有中间地带 Scope.Group,它标记要在执行组内共享的状态,但不在执行组之间共享。
 
  将所有这些放在一起,下面的示例意味着:
   a) 定义执行组"g",3个线程执行 inc(),1个线程执行 get(),每组4个线程;
   b) 如果我们用 4 个线程运行这个测试用例,那么我们将有一个单独的执行组。 一般4*N个线程运行会创建N个执行组等;
   c) 每个执行组共享一个 @State 实例:即执行组在组内共享计数器,但不跨组。

 

3、Interrupts Benchmark

  前面的例子中为大家演示了多线程情况下同时对 AtomicInteger 执行读写操作的情况,虽然基准测试能够顺利地运行,但是有些时候我们想要执行某些容器的读写操作时可能会引起阻塞,这种阻塞并不是容器无法保证线程安全问题引起的,而是由JMH框架的机制引起的。
 
【Interrupts Benchmark样例 - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * JMH测试16:Interrupts Benchmark样例
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Group)
public class JmhTestApp16_InterruptsBenchmark {

    private BlockingQueue<Integer> queue;

    private final static int VALUE = Integer.MAX_VALUE;

    @Setup
    public void init()
    {
        this.queue = new ArrayBlockingQueue<>(10);
    }

    @GroupThreads(5)
    @Group("blockingQueue")
    @Benchmark
    public void put()
            throws InterruptedException
    {
        this.queue.put(VALUE);
    }

    @GroupThreads(5)
    @Group("blockingQueue")
    @Benchmark
    public int take()
            throws InterruptedException
    {
        return this.queue.take();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp16_InterruptsBenchmark.class.getSimpleName())
                .build();

        new Runner(opt).run();
    }
}

 
  在 JmhTestApp16_InterruptsBenchmark 中我们针对BlockingQueue同时进行读(take)和写(put)的操作,但是很遗憾,在某些情况下(或许是第一次运行时)程序会出现长时间的阻塞,对于每一批次的Measurement,当然也包括Warmup中,put和take方法都会同时被多线程执行。想象一下,假设put方法最先执行结束,take方法无法再次从blocking queue中获取元素的时候将会一直阻塞下去,同样,take方法最先执行结束后,put方法在放满10个元素后再也无法存入新的元素,进而进入了阻塞状态,这两种情况都会等到每一次iteration(批次)超时(默认是10分钟)后才能继续往下执行。
 
  难道我们就没有办法测试高并发容器在线程挂起时的性能了吗?事实上,JMH的设计者们早就为我们想好了对应的解决方案,我们可以通过设置Options的timeout来强制让每一个批次的度量超时,超时的基准测试数据将不会被纳入统计之中,这也是JMH的另外一个严谨之处。
 
【Interrupts Benchmark增加超时参数样例 - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * JMH测试16:Interrupts Benchmark样例
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Group)
public class JmhTestApp16_InterruptsBenchmark {

    private BlockingQueue<Integer> queue;

    private final static int VALUE = Integer.MAX_VALUE;

    @Setup
    public void init()
    {
        this.queue = new ArrayBlockingQueue<>(10);
    }

    @GroupThreads(5)
    @Group("blockingQueue")
    @Benchmark
    public void put()
            throws InterruptedException
    {
        this.queue.put(VALUE);
    }

    @GroupThreads(5)
    @Group("blockingQueue")
    @Benchmark
    public int take()
            throws InterruptedException
    {
        return this.queue.take();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp16_InterruptsBenchmark.class.getSimpleName())
                // 将每个批次的超时时间设置为10秒
                .timeout(TimeValue.seconds(10))
                .build();

        new Runner(opt).run();
    }
}

 
【Interrupts Benchmark增加超时参数样例 - 代码运行结果】

# JMH version: 1.36
# VM version: JDK 1.8.0_281, Java HotSpot(TM) 64-Bit Server VM, 25.281-b09
# VM invoker: D:\Develop\JDK\jdk1.8.0_281\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=13710:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
##########设置超时时间为10秒
# Timeout: 10 s per iteration, ***WARNING: The timeout might be too low!***
# Threads: 10 threads (1 group; 5x "put", 5x "take" in each group), will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: cn.zhuangyt.javabase.jmh.JmhTestApp16_InterruptsBenchmark.blockingQueue

# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration   1: (benchmark timed out, interrupted 1 times) 16.056 ±(99.9%) 13.176 us/op
# Warmup Iteration   2: 22.701 ±(99.9%) 28.283 us/op
# Warmup Iteration   3: 13.663 ±(99.9%) 7.399 us/op
# Warmup Iteration   4: (benchmark timed out, interrupted 1 times) 13.507 ±(99.9%) 8.866 us/op
# Warmup Iteration   5: (benchmark timed out, interrupted 1 times) 15.758 ±(99.9%) 14.186 us/op
##########第一个批次的执行由于阻塞超时被中断,但是阻塞所耗费的CPU时间并未纳入统计
Iteration   1: 12.763 ±(99.9%) 5.298 us/op
                 put:  12.131 ±(99.9%) 9.317 us/op
                 take: 13.395 ±(99.9%) 17.552 us/op

Iteration   2: 17.265 ±(99.9%) 16.655 us/op
                 put:  17.173 ±(99.9%) 50.839 us/op
                 take: 17.357 ±(99.9%) 38.260 us/op

Iteration   3: 19.929 ±(99.9%) 21.201 us/op
                 put:  18.791 ±(99.9%) 55.449 us/op
                 take: 21.067 ±(99.9%) 58.632 us/op

Iteration   4: 19.870 ±(99.9%) 21.256 us/op
                 put:  21.154 ±(99.9%) 71.631 us/op
                 take: 18.586 ±(99.9%) 37.452 us/op

Iteration   5: 13.857 ±(99.9%) 8.801 us/op
                 put:  14.860 ±(99.9%) 29.325 us/op
                 take: 12.854 ±(99.9%) 15.272 us/op



Result "cn.zhuangyt.javabase.jmh.JmhTestApp16_InterruptsBenchmark.blockingQueue":
  16.737 ±(99.9%) 12.825 us/op [Average]
  (min, avg, max) = (12.763, 16.737, 19.929), stdev = 3.331
  CI (99.9%): [3.912, 29.562] (assumes normal distribution)

Secondary result "cn.zhuangyt.javabase.jmh.JmhTestApp16_InterruptsBenchmark.blockingQueue:put":
  16.822 ±(99.9%) 13.425 us/op [Average]
  (min, avg, max) = (12.131, 16.822, 21.154), stdev = 3.487
  CI (99.9%): [3.396, 30.247] (assumes normal distribution)

Secondary result "cn.zhuangyt.javabase.jmh.JmhTestApp16_InterruptsBenchmark.blockingQueue:take":
  16.652 ±(99.9%) 13.445 us/op [Average]
  (min, avg, max) = (12.854, 16.652, 21.067), stdev = 3.492
  CI (99.9%): [3.207, 30.097] (assumes normal distribution)


# Run complete. Total time: 00:01:49

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                            Mode  Cnt   Score    Error  Units
JmhTestApp16_InterruptsBenchmark.blockingQueue       avgt    5  16.737 ± 12.825  us/op
JmhTestApp16_InterruptsBenchmark.blockingQueue:put   avgt    5  16.822 ± 13.425  us/op
JmhTestApp16_InterruptsBenchmark.blockingQueue:take  avgt    5  16.652 ± 13.445  us/op

  观察输出结果会发现当出现阻塞时,JMH最多等待指定的超时时间会继续执行而不是像之前那样陷入长时间的阻塞。第一个批次(Iteration 1)的执行由于阻塞超时被中断,但是阻塞所耗费的CPU时间并未纳入统计。
 
【附上官方Interrupts样例(JMHSample_30_Interrupts) - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * JMH测试16:官方Interrupts样例
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Group)
public class JmhTestApp16_Interrupts {

    /**
     * JMH can also detect when threads are stuck in the benchmarks, and try
     * to forcefully interrupt the benchmark thread. JMH tries to do that
     * when it is arguably sure it would not affect the measurement.
     *
     * JMH 还可以检测线程何时卡在基准测试中,并尝试强制中断基准线程。当 JMH 可以确定它不会影响测量时,它会尝试这样做。
     */

    /**
     * In this example, we want to measure the simple performance characteristics
     * of the ArrayBlockingQueue. Unfortunately, doing that without a harness
     * support will deadlock one of the threads, because the executions of
     * take/put are not paired perfectly. Fortunately for us, both methods react
     * to interrupts well, and therefore we can rely on JMH to terminate the
     * measurement for us. JMH will notify users about the interrupt actions
     * nevertheless, so users can see if those interrupts affected the measurement.
     * JMH will start issuing interrupts after the default or user-specified timeout
     * had been reached.
     *
     * 在此示例中,我们要测量 ArrayBlockingQueue 的简单性能特征。
     * 不幸的是,在没有 harness 支持的情况下这样做会导致其中一个线程死锁,因为 take/put 的执行没有完美配对。
     * 对我们来说幸运的是,这两种方法都能很好地响应中断,因此我们可以依靠 JMH 为我们终止测量。
     * 尽管如此,JMH 仍会通知用户有关中断操作的信息,因此用户可以查看这些中断是否影响了测量。JMH 将在达到默认或用户指定的超时后开始发出中断。
     *
     * This is a variant of org.openjdk.jmh.samples.JMHSample_18_Control, but without
     * the explicit control objects. This example is suitable for the methods which
     * react to interrupts gracefully.
     *
     * 这是 org.openjdk.jmh.samples.JMHSample_18_Control 的变体,但没有显式控制对象。 此示例适用于优雅地响应中断的方法。
     */

    private BlockingQueue<Integer> q;

    @Setup
    public void setup() {
        q = new ArrayBlockingQueue<>(1);
    }

    @Group("Q")
    @Benchmark
    public Integer take() throws InterruptedException {
        return q.take();
    }

    @Group("Q")
    @Benchmark
    public void put() throws InterruptedException {
        q.put(42);
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp16_Interrupts.class.getSimpleName())
                .threads(2)
                .forks(5)
                .timeout(TimeValue.seconds(10))
                .build();

        new Runner(opt).run();
    }
}

 
【附上官方Interrupts样例(JMHSample_30_Interrupts) - 代码运行结果】

Benchmark                                            Mode  Cnt     Score     Error  Units
JmhTestApp16_Interrupts.Q                            avgt   25  6773.746 ± 753.955  ns/op
JmhTestApp16_Interrupts.Q:put                        avgt   25  6773.746 ± 753.955  ns/op
JmhTestApp16_Interrupts.Q:take                       avgt   25  6773.745 ± 753.954  ns/op
JmhTestApp16_InterruptsBenchmark.blockingQueue       avgt   25    15.970 ±   3.405  us/op
JmhTestApp16_InterruptsBenchmark.blockingQueue:put   avgt   25    16.128 ±   4.533  us/op
JmhTestApp16_InterruptsBenchmark.blockingQueue:take  avgt   25    15.813 ±   2.921  us/op

 
【官方Interrupts样例(JMHSample_30_Interrupts)注解 - 谷歌和百度翻译互补】

  JMH 还可以检测线程何时卡在基准测试中,并尝试强制中断基准线程。当 JMH 可以确定它不会影响测量时,它会尝试这样做。
 
  在此示例中,我们要测量 ArrayBlockingQueue 的简单性能特征。不幸的是,在没有 harness 支持的情况下这样做会导致其中一个线程死锁,因为 take/put 的执行没有完美配对。对我们来说幸运的是,这两种方法都能很好地响应中断,因此我们可以依靠 JMH 为我们终止测量。尽管如此,JMH 仍会通知用户有关中断操作的信息,因此用户可以查看这些中断是否影响了测量。JMH 将在达到默认或用户指定的超时后开始发出中断。
 
  这是 org.openjdk.jmh.samples.JMHSample_18_Control 的变体,但没有显式控制对象。 此示例适用于优雅地响应中断的方法。

 

4、几大线程安全Map的性能对比

  对比几大线程安全Map的多线程下的读写性能。
 
【线程安全Map读写性能对比样例 - 代码】

package cn.zhuangyt.javabase.jmh;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.*;

/**
 * JMH测试16:测试线程安全的几个Map的读写性能
 * @author 大白有点菜
 */
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Group)
public class JmhTestApp16_Measure_Map_Performance {

    @Param({"1", "2", "3", "4"})
    private int type;

    private Map<Integer, Integer> map;

    @Setup
    public void setUp()
    {
        switch (type)
        {
            case 1:
                this.map = new ConcurrentHashMap<>();
                break;
            case 2:
                this.map = new ConcurrentSkipListMap<>();
                break;
            case 3:
                this.map = new Hashtable<>();
                break;
            case 4:
                this.map = Collections.synchronizedMap(
                        new HashMap<>());
                break;
            default:
                throw new IllegalArgumentException("Illegal map type.");
        }
    }

    @Group("g")
    @GroupThreads(5)
    @Benchmark
    public void putMap()
    {
        int random = randomIntValue();
        this.map.put(random, random);
    }

    @Group("g")
    @GroupThreads(5)
    @Benchmark
    public Integer getMap()
    {
        return this.map.get(randomIntValue());
    }

    /**
     * 计算一个随机值用作Map中的Key和Value
     * @return
     */
    private int randomIntValue()
    {
        return (int) Math.ceil(Math.random() * 600000);
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhTestApp16_Measure_Map_Performance.class.getSimpleName())
                .build();

        new Runner(opt).run();
    }
}

 
【线程安全Map读写性能对比样例 - 代码运行结果】

Benchmark                                      (type)  Mode  Cnt  Score   Error  Units
JmhTestApp16_Measure_Map_Performance.g              1  avgt    5  2.402 ± 0.406  us/op
JmhTestApp16_Measure_Map_Performance.g:getMap       1  avgt    5  2.394 ± 0.484  us/op
JmhTestApp16_Measure_Map_Performance.g:putMap       1  avgt    5  2.409 ± 0.332  us/op
JmhTestApp16_Measure_Map_Performance.g              2  avgt    5  2.804 ± 0.571  us/op
JmhTestApp16_Measure_Map_Performance.g:getMap       2  avgt    5  2.951 ± 0.578  us/op
JmhTestApp16_Measure_Map_Performance.g:putMap       2  avgt    5  2.658 ± 0.565  us/op
JmhTestApp16_Measure_Map_Performance.g              3  avgt    5  3.745 ± 0.195  us/op
JmhTestApp16_Measure_Map_Performance.g:getMap       3  avgt    5  6.081 ± 0.436  us/op
JmhTestApp16_Measure_Map_Performance.g:putMap       3  avgt    5  1.408 ± 0.048  us/op
JmhTestApp16_Measure_Map_Performance.g              4  avgt    5  4.754 ± 2.204  us/op
JmhTestApp16_Measure_Map_Performance.g:getMap       4  avgt    5  8.171 ± 4.468  us/op
JmhTestApp16_Measure_Map_Performance.g:putMap       4  avgt    5  1.336 ± 0.148  us/op

  我们可以看到,在 putMap 和 getMap 方法中,通过随机值的方式将取值作为 key 和 value 存入 map 中,同样也是通过随机值的方式将取值作为 key 从 map 中进行数据读取(当然读取的值可能并不存在)。还有我们在基准方法中进行了随机值的运算,虽然随机值计算所耗费的CPU时间也会被纳入基准结果的统计中,但是每一个 map 都进行了相关的计算,因此,我们可以认为大家还是站在了同样的起跑线上,故而可以对其忽略不计。
 
  基准测试的数据可以表明,在5个线程同时进行 map 写操作,5个线程同时进行读操作时,参数 type=1 的性能是最佳的,也就是 ConcurrentHashMap 。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

性能调优之JMH必知必会4:JMH的高级用法 的相关文章

  • win10下安装kubernets

    win10下安装docker for windows后 xff0c 新版是有一个kubernets选项 xff0c 选择启动后 xff0c 一直报 kubernets is starting 的错误 xff0c 原因是 xff0c kube
  • 嵌入式工程师的自我修养

    文章目录 前言一 认知的四个阶段1 不知不知2 知道不知3 知道己知3 1 软硬件3 2 网络3 3 安全技术 xff08 换成你自己的领域 xff09 3 4 真正知道的三个阶段3 4 1 会用3 4 2 了解怎么实现3 4 3 明白为什
  • 利用uORB机制实现数据在不同进程中通信

    uORB实际上是一种设计模式中的观察者模式 xff0c 用于实现一种一对多的依赖关系 xff0c 让多个观察者 xff08 Observer xff09 同时监听某一主题对象 xff08 Subject xff09 当这个主题对象 xff0
  • Android-注解篇

    1 什么是注解 从JDK 5 开始 xff0c Java 增加了注解 xff0c 注解是代码里的特殊标记 xff0c 这些标记可以在编译 类加载 运 行时被读取 xff0c 并执行相应的处理 通过使用注解 xff0c 开发人员可以在不改变原
  • 新品BCM6755A1KFEBG/MT7921LE/MT7921AU WiFi芯片

    博通在WiFi市场具有相当的实力 在WiFi6上有下面这几个解决方案 xff1a 型号 xff1a BCM6755 BCM6755A1KFEBG 类型 xff1a 四核1 5GHz CPU 封装 xff1a BGA 批次 xff1a 新 B
  • Ubuntu : GPG签名验证错误 解决之道sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 6DFBCBAE

    Ubuntu GPG签名验证错误 解决之道 转载 sudo apt key adv keyserver keyserver ubuntu com recv keys Key Where key 61 61 the gpg key id Th
  • T265深度图像输出

    1 T265深度图像输出 1 1 环境依赖 T265摄像头python3pip3opencv pythonpyrealsense2 1 2 安装运行环境 安装秘钥 span class token function sudo span ap
  • Linux版本号串记录(ubuntu系列)

    Linux version 4 4 0 112 generic buildd 64 lgw01 amd64 010 gcc version 5 4 0 20160609 Ubuntu 5 4 0 6ubuntu1 16 04 5 135 U
  • 死锁的四个必要条件

    死锁 在高并发中是一个常见的名词 产生的四个必要条件如下 xff1a 互斥条件 xff1a 一个资源同一时间能且只能被一个线程访问 xff1b 不可掠夺 xff1a 当资源被一个线程占用时 xff0c 其他线程不可抢夺该资源 xff1b 请
  • Sphinx index.rst

    假设我们有两个文本file1 rst和file2 rst他们的内容如下 file1 rst span class hljs header file1 title1 61 61 61 61 61 61 61 61 61 61 61 61 sp
  • Git - 图形化界面操作

    目录 1 新建仓库 2 代码提交 3 代码回滚 4 新建分支 5 合并分支 6 重置合并 7 分支变基 8 分支优选 Git 的图形化界面操作 xff0c 使用 Idea 进行演示 1 新建仓库 对于一个代码仓库 Create Git re
  • CMakeLists

    1 指定 cmake 的最小版本 cmake minimum required VERSION 3 4 1 2 设置项目名称 xff0c 它会引入两个变量 demo BINARY DIR 和 demo SOURCE DIR xff0c 同时
  • 七步实现STM32MP157多核协同工作(Cortex-A7与Cortex-M4通信)

    写在前面 xff1a STM32MP157是ST进军Linux的首款微处理器 xff0c 采用MCU 43 MPU的组合 xff0c 集成两颗主频微800MHz的Cortex A7应用处理器内核 xff08 支持开源linux操作系统 xf
  • 【实战】STM32 FreeRTOS移植系列教程4:FreeRTOS 软件定时器

    写在前面 xff1a 本文章为 STM32MP157开发教程之FreeRTOS操作系统篇 系列中的一篇 xff0c 笔者使用的开发平台为华清远见FS MP1A开发板 xff08 STM32MP157开发板 xff09 stm32mp157是
  • 【实战】STM32 FreeRTOS移植系列教程5:FreeRTOS消息队列

    写在前面 xff1a 本文章为 STM32MP157开发教程之FreeRTOS操作系统篇 系列中的一篇 xff0c 笔者使用的开发平台为华清远见FS MP1A开发板 xff08 STM32MP157开发板 xff09 stm32mp157是
  • 学习嵌入式linux为什么推荐stm32mp157开发板?

    stm32mp157是ST推出的一款双A7 43 M4多核异构处理器 xff0c 既可以学习linux xff0c 又可以学习stm32单片机开发 xff0c 还可以拓展物联网 人工智能方向技术学习 xff0c 并极大丰富linux应用场景
  • STM32 Linux开发板——教程+视频+项目+硬件

    STM32 Linux开发板 适合入门进阶学习的Linux开发板 xff1a 华清远见FS MP1A开发板 xff08 STM32MP157开发板 xff09 开发板介绍 FS MP1A开发板是华清远见自主研发的一款高品质 高性价比的Lin
  • 编程语言对比 面向对象

    C 43 43 面向对象 java面向对象 python面向对象 java中是public int a 61 10 C 43 43 中是 public int a 61 10 C 43 43 中有拷贝构造
  • 嵌入式linux物联网毕业设计项目智能语音识别基于stm32mp157开发板

    stm32mp157开发板FS MP1A是华清远见自主研发的一款高品质 高性价比的Linux 43 单片机二合一的嵌入式教学级开发板 开发板搭载ST的STM32MP157高性能微处理器 xff0c 集成2个Cortex A7核和1个Cort
  • CMake(一)

    CMake xff08 一 xff09 简述 在之前的文章中介绍了 qmake的使用 相比qmake xff0c CMake稍微复杂一点 xff0c 它使用CMakeList txt文件来定制整个编译流程 同时 xff0c CMake会根据

随机推荐

  • LTE网元功能

    LTE 网元功能 2015 03 30 22 33 31 分类 xff1a NetworkProtocols 举报 字号 订阅 下载LOFTER 我的照片书 主要网元功能 eNodeB Radio Resou
  • [C++] 32位C++程序,计算sizeof的值

    sizeof str 61 6 字符串数组 xff0c 大小是六个字节 加上 39 0 39 共六个 sizeof p 61 4 指针的内容就是一个指向目标地址的整数 xff0c 所以不管指向char int还是其他 xff0c 32位机指
  • 串口打印printf

    串口打印printf 1 配置串口2 添加代码3 使用MDK勾选Mircro LIB 1 配置串口 打开STM32CubeMX xff0c 创建工程 xff0c 配置串口 2 添加代码 重写fputc函数 xff0c 需要包含头文件 inc
  • 22.Ubuntu出现“由于没有公钥,无法验证下列签名”

    由于没有公钥 xff0c 无法验证下列签名 1 无公钥错误2 输入命令导入公钥3 注意 1 无公钥错误 使用sudo apt update时出现以下错误 xff1a 我图中的公钥就是 xff1a 3B4FE6ACC0B21F32 xff08
  • nyist 27 水池数目(dfs搜索)

    xfeff xfeff 水池数目 时间限制 xff1a 3000 ms 内存限制 xff1a 65535 KB 难度 xff1a 4 描述 南阳理工学院校园里有一些小河和一些湖泊 xff0c 现在 xff0c 我们把它们通一看成水池 xff
  • XTUOJ 1176 I Love Military Chess(模拟)

    xfeff xfeff I Love Military Chess Accepted 45 Submit 141Time Limit 1000 MS Memory Limit 65536 KB 题目描述 陆军棋 xff0c 又称陆战棋 xf
  • 数据结构课程设计之一元多项式的计算

    数据结构不是听会的 xff0c 也不是看会的 xff0c 是练会的 xff0c 对于写这么长的代码还是心有余也力不足啊 xff0c 对于指针的一些操作 xff0c 也还是不熟练 xff0c 总出现一些异常错误 xff0c 对于数据结构掌握还
  • Unity官方文档(英文)

    地址 xff1a https docs unity3d com Manual UnityManual html
  • 数据结构课程设计之通讯录管理系统

    数据结构的第二个课程设计 xff0c 在c语言课程设计的基础上加以改进 xff0c xff08 加强版 xff09 xff0c 保存一下代码 xff0c 对文件的处理 xff0c 还是有一点一问题 xff0c 还有待改进 include l
  • 在网页中添加音乐

    最近在折腾一个网页 xff0c 对于一个有强迫症的人来说 xff0c 就想在网页中插入音乐 xff0c xff08 当做背景音乐 xff09 xff0c 然后自己百度了好多资料 xff1b 就在这里总结一下 xff1a 第一步 xff1a
  • nyist oj 214 单调递增子序列(二) (动态规划经典)

    单调递增子序列 二 时间限制 xff1a 1000 ms 内存限制 xff1a 65535 KB 难度 xff1a 4 描述 给定一整型数列 a1 a2 an xff08 0 lt n lt 61 100000 xff09 xff0c 找出
  • 思科CCNA第一学期期末考试答案

    1 第 3 层头部包含的哪一项信息可帮助数据传输 xff1f 端口号 设备物理地址 目的主机逻辑地址 虚拟连接标识符 2 IP 依靠 OSI 哪一层的协议来确定数据包是否已丢失并请求重传 xff1f 应用层 表示层 会话层 传输层 3 请参
  • hexo博客出现command not found解决方案

    由于前一段时间忙于考试 xff0c 也有好久没有去更新博客了 xff0c 今天去添加友链的时候 xff0c 突然发现用不了了 xff0c 出现了conmand not found的提示 xff1a 按照字面上的翻译就是 找不到所使用的命令
  • 思科CCNA第二学期期末考试答案

    1 关于数据包通过路由器传输时的封装和解封的叙述 xff0c 下列哪三项是正确的 xff1f xff08 选择三项 xff09 路由器修改 TTL 字段 xff0c 将其值减 1 路由器将源 IP 更改为送出接口的 IP 路由器保持相同的源
  • Hexo版本升级和Next主题升级之坑

    缘起 差不多用了一年hexo的3 2 0版本 xff0c next主题版本也用的5 0的 xff0c 本来用的好好的 xff0c 但是最近访问其他人的博客 xff0c 发现访问速度比我的提升了不止一点点 xff0c 遂决定折腾一番 过程 H
  • Python中JSON的基本使用

    JSON JavaScript Object Notation 是一种轻量级的数据交换格式 Python3 中可以使用 json 模块来对 JSON 数据进行编解码 xff0c 它主要提供了四个方法 xff1a dumps dump loa
  • 卷积和快速傅里叶变换(FFT)的实现

    卷积运算 卷积可以说是图像处理中最基本的操作 线性滤波通过不同的卷积核 xff0c 可以产生很多不同的效果 假如有一个要处理的二维图像 xff0c 通过二维的滤波矩阵 xff08 卷积核 xff09 xff0c 对于图像的每一个像素点 xf
  • 每个程序员和设计师可做的10项运动

    本文转载自 码农网 程序员 和设计师大部分时间都坐在电脑前 有效的锻炼有助于他们更好地工作 传统的 xff1a 当坐在电脑桌前的时候 脚触地 双手在肘部弯曲 打字时手应搁在桌子上 键盘和鼠标应在触手可及的地方 显示屏应在视线水平上 xff0
  • OKhttpUtils

    public class OkUtils static final int GET EXCUTE 61 111 static final int GET ENQUEUE 61 222 static final int POST EXCUTE
  • 性能调优之JMH必知必会4:JMH的高级用法

    性能调优之JMH必知必会4 xff1a JMH的高级用法 JMH必知必会系列文章 xff08 持续更新 xff09 一 前言二 JMH的高级用法1 添加JMH依赖包2 Asymmetric Benchmark3 Interrupts Ben