日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747


概述

JMH 是一個由 OpenJDK/Oracle 里面那群開發了 JAVA 編譯器的大牛們所開發的 Micro Benchmark Framework 。何謂 Micro Benchmark 呢?簡單地說就是在 method 層面上的 benchmark,精度可以精確到微秒級。可以看出 JMH 主要使用在當你已經找出了熱點函數,而需要對熱點函數進行進一步的優化時,就可以使用 JMH 對優化的效果進行定量的分析。

比較典型的使用場景還有:

•想定量地知道某個函數需要執行多長時間,以及執行時間和輸入 n 的相關性•一個函數有兩種不同實現(例如實現 A 使用了 FixedThreadPool,實現 B 使用了 ForkJoinPool),不知道哪種實現性能更好

盡管 JMH 是一個相當不錯的 Micro Benchmark Framework,但很無奈的是網上能夠找到的文檔比較少,而官方也沒有提供比較詳細的文檔,對使用造成了一定的障礙。但是有個好消息是官方的 Code Sample 寫得非常淺顯易懂,推薦在需要詳細了解 JMH 的用法時可以通讀一遍——本文則會介紹 JMH 最典型的用法和部分常用選項。

第一個例子

如果你使用 maven 來管理你的 Java 項目的話,引入 JMH 是一件很簡單的事情——只需要在 pom.xml 里增加 JMH 的依賴即可

<properties>    <jmh.version>1.14.1</jmh.version></properties>
<dependencies>    <dependency>        <groupId>org.openjdk.jmh</groupId>        <artifactId>jmh-core</artifactId>        <version>${jmh.version}</version>    </dependency>    <dependency>        <groupId>org.openjdk.jmh</groupId>        <artifactId>jmh-generator-annprocess</artifactId>        <version>${jmh.version}</version>        <scope>provided</scope>    </dependency></dependencies>

接下來再創建我們的第一個 Benchmark

package com.ckj.base.designPatternes.proxy.DynamicProxy;
import org.openjdk.jmh.annotations.*;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;import java.util.concurrent.TimeUnit;
/** * @author c.kj * @Description * @Date 2021-03-04 * @Time 21:55 **/@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.MICROSECONDS)@State(Scope.Thread)@Fork(1)public class CglibProxy {
    @Benchmark    @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MICROSECONDS)    @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MICROSECONDS)    public int measureName() throws InterruptedException {       Thread.sleep(1);        getProxyInstance();        return 0;    }
    public Object getProxyInstance() {        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(CglibTarget.class);        MethodInterceptor methodInterceptor = new MethodInterceptor() {            @Override            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)                    throws Throwable {
                System.out.println("intercept start....");                Object o1 = methodProxy.invokeSuper(o, objects);                System.out.println("intercept end....");
                return o1;            }        };        enhancer.setCallback(methodInterceptor);        Object o = enhancer.create();        return o;
    }
}

有不少你可能是第一次見到的注解,不過不著急,接下來會解釋這些注解的意義。我們先來跑一下這個 benchmark 吧 :)

# JMH 1.14.1 (released 1780 days ago, please consider updating!)# VM version: JDK 1.8.0_201, VM 25.201-b09# VM invoker: /Library/Java/JavaVirtualmachines/jdk1.8.0_201.jdk/Contents/Home/jre/bin/java# VM options: -Dvisualvm.id=94033462347806 -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=56278:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8# Warmup: 5 iterations, 100 us each# Measurement: 5 iterations, 100 us each# Timeout: 10 min per iteration# Threads: 1 thread, will synchronize iterations# Benchmark mode: Average time, time/op# Benchmark: com.ckj.base.designPatternes.proxy.DynamicProxy.CglibProxy.measureName
# Run progress: 0.00% complete, ETA 00:00:00# Fork: 1 of 1# Warmup Iteration   1: 68584.809 us/op# Warmup Iteration   2: 1312.436 us/op# Warmup Iteration   3: 1329.796 us/op# Warmup Iteration   4: 1329.558 us/op# Warmup Iteration   5: 1296.309 us/opIteration   1: 1296.129 us/opIteration   2: 1121.620 us/opIteration   3: 1603.035 us/opIteration   4: 1306.412 us/opIteration   5: 1194.444 us/op

Result "measureName":  1304.328 ±(99.9%) 706.764 us/op [Average]  (min, avg, max) = (1121.620, 1304.328, 1603.035), stdev = 183.544  CI (99.9%): [597.564, 2011.092] (assumes normal distribution)

# Run complete. Total time: 00:00:01
Benchmark                                                  Mode  Cnt     Score     Error  UnitsdesignPatternes.proxy.DynamicProxy.CglibProxy.measureName  avgt    5  1304.328 ± 706.764  us/op
Process finished with exit code 0

對 getProxyInstance() 的測試結果顯示執行時間平均約為1304.328微秒。因為我們的測試對象 1304.328正好就是睡眠1000微秒,所以 JMH 顯示的結果可以說很符合我們的預期。

那好,現在我們再來詳細地解釋代碼的意義。不過在這之前,需要先了解一下 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 的結果更加接近真實情況就需要進行預熱。

注解

現在來解釋一下上面例子中使用到的注解,其實很多注解的意義完全可以望文生義 :)

@Benchmark

表示該方法是需要進行 benchmark 的對象,用法和 JUnit 的 @Test 類似。

@Mode

Mode 如之前所說,表示 JMH 進行 Benchmark 時所使用的模式。

@State

State 用于聲明某個類是一個“狀態”,然后接受一個 Scope 參數用來表示該狀態的共享范圍。因為很多 benchmark 會需要一些表示狀態的類,JMH 允許你把這些類以依賴注入的方式注入到 benchmark 函數里。Scope 主要分為兩種。

Thread: 該狀態為每個線程獨享。•Benchmark: 該狀態在所有線程間共享。

關于State的用法,官方的 code sample 里有比較好的例子。

@OutputTimeUnit

benchmark 結果所使用的時間單位。

啟動選項

解釋完了注解,再來看看 JMH 在啟動前設置的參數。

Options opt = new OptionsBuilder()        .include(FirstBenchmark.class.getSimpleName())        .forks(1)        .warmupIterations(5)        .measurementIterations(5)        .build();
new Runner(opt).run();

include

benchmark 所在的類的名字,注意這里是使用正則表達式對所有類進行匹配的。

fork

進行 fork 的次數。如果 fork 數是2的話,則 JMH 會 fork 出兩個進程來進行測試。

warmupIterations

預熱的迭代次數。

measurementIterations

實際測量的迭代次數。

第二個例子

在看過第一個完全只為示范的例子之后,再來看一個有實際意義的例子。

問題:

計算 1 ~ n 之和,比較串行算法和并行算法的效率,看 n 在大約多少時并行算法開始超越串行算法

首先定義一個表示這兩種實現的接口

public interface Calculator {    /**     * calculate sum of an integer array     * @param numbers     * @return     */    public long sum(int[] numbers);
    /**     * shutdown pool or reclAIm any related resources     */    public void shutdown();}

由于這兩種算法的實現不是這篇文章的重點,而且本身并不困難,所以實際代碼就不贅述了。如果真的感興趣的話,可以看最后的附錄。以下僅說明一下我所指的串行算法和并行算法的含義。

•串行算法:使用 for-loop 來計算 n 個正整數之和。•并行算法:將所需要計算的 n 個正整數分成 m 份,交給 m 個線程分別計算出和以后,再把它們的結果相加。

進行 benchmark 的代碼如下

@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.MICROSECONDS)@State(Scope.Benchmark)public class SecondBenchmark {    @Param({"10000", "100000", "1000000"})    private int length;
    private int[] numbers;    private Calculator singleThreadCalc;    private Calculator multiThreadCalc;
    public static void main(String[] args) throws RunnerException {        Options opt = new OptionsBuilder()                .include(SecondBenchmark.class.getSimpleName())                .forks(2)                .warmupIterations(5)                .measurementIterations(5)                .build();
        new Runner(opt).run();    }
    @Benchmark    public long singleThreadBench() {        return singleThreadCalc.sum(numbers);    }
    @Benchmark    public long multiThreadBench() {        return multiThreadCalc.sum(numbers);    }
    @Setup    public void prepare() {        numbers = IntStream.rangeClosed(1, length).toArray();        singleThreadCalc = new SinglethreadCalculator();        multiThreadCalc = new MultithreadCalculator(Runtime.getRuntime().availableProcessors());    }
    @TearDown    public void shutdown() {        singleThreadCalc.shutdown();        multiThreadCalc.shutdown();    }}

注意到這里用到了3個之前沒有使用的注解。

@Param

@Param 可以用來指定某項參數的多種情況。特別適合用來測試一個函數在不同的參數輸入的情況下的性能。

@Setup

@Setup 會在執行 benchmark 之前被執行,正如其名,主要用于初始化。

@TearDown

@TearDown 和 @Setup 相對的,會在所有 benchmark 執行結束以后執行,主要用于資源的回收等。

最后來猜猜看實際結果如何?并行算法在哪個問題集下能夠超越串行算法?

我在自己的 mac 上跑下來的結果,總數在10000時并行算法不如串行算法,總數達到100000時并行算法開始和串行算法接近,總數達到1000000時并行算法所耗時間約是串行算法的一半左右。

常用選項

還有一些 JMH 的常用選項沒有提及的,簡單地在此介紹一下

CompilerControl

控制 compiler 的行為,例如強制 inline,不允許編譯等。

Group

可以把多個 benchmark 定義為同一個 group,則它們會被同時執行,主要用于測試多個相互之間存在影響的方法。

Level

用于控制 @Setup@TearDown 的調用時機,默認是 Level.Trial,即benchmark開始前和結束后。

Profiler

JMH 支持一些 profiler,可以顯示等待時間和運行時間比,熱點函數等。

延伸閱讀

IDE插件

IntelliJ 有 JMH 的插件,提供 benchmark 方法的自動生成等便利功能。

JMH 教程

Jenkov 的 JMH 教程,相比于這篇文章介紹得更為詳細,非常推薦。順便 Jenkov 的其他 Java 教程也非常值得一看。

附錄

代碼清單

public class SinglethreadCalculator implements Calculator {    public long sum(int[] numbers) {        long total = 0L;        for (int i : numbers) {            total += i;        }        return total;    }
    @Override    public void shutdown() {        // nothing to do    }}
public class MultithreadCalculator implements Calculator {    private final int nThreads;    private final ExecutorService pool;
    public MultithreadCalculator(int nThreads) {        this.nThreads = nThreads;        this.pool = Executors.newFixedThreadPool(nThreads);    }
    private class SumTask implements Callable<Long> {        private int[] numbers;        private int from;        private int to;
        public SumTask(int[] numbers, int from, int to) {            this.numbers = numbers;            this.from = from;            this.to = to;        }
        public Long call() throws Exception {            long total = 0L;            for (int i = from; i < to; i++) {                total += numbers[i];            }            return total;        }    }
    public long sum(int[] numbers) {        int chunk = numbers.length / nThreads;
        int from, to;        List<SumTask> tasks = new ArrayList<SumTask>();        for (int i = 1; i <= nThreads; i++) {            if (i == nThreads) {                from = (i - 1) * chunk;                to = numbers.length;            } else {                from = (i - 1) * chunk;                to = i * chunk;            }            tasks.add(new SumTask(numbers, from, to));        }
        try {            List<Future<Long>> futures = pool.invokeAll(tasks);
            long total = 0L;            for (Future<Long> future : futures) {                total += future.get();            }            return total;        } catch (Exception e) {            // ignore            return 0;        }    }
    @Override    public void shutdown() {        pool.shutdown();    }}

分享到:
標簽:JMH
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定