Java--JMH--性能测试--测试软件运行效率/时间--StopWatch

news2024/9/24 7:19:55
写在前面:
很多时候想要测试代码运行时间,或者比较2个运行的效率。 最简单的方法就是Sytem.currentTimeMillis记录2开始和结束时间来算
但是Java 代码越执行越快,放在后面的方法会有优势,这个原因受留个眼,以后研究。大概有受类加载,缓存预热, jit 编译优化等原因。

简单点的StopWatch

//创建对象
StopWatch s = new StopWatch();
//计时
s.start("这一次的名字");
....程序
//结束
s.stop();
//生成一个字符串,其中包含描述所有已执行任务的表。
System.out.println(s.prettyPrint());

多个对象

需要有参构造

//使用给定的 ID 构造一个新 StopWatch 。
//当我们有来自多个秒表的输出并需要区分它们时,该 ID 很方便。
public StopWatch(String id) 

其他的应该不太用的着

JMH

一款一款官方的微基准测试工具 - JMH.
JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。使用 JMH 可以让你方便快速的进行一次严格的代码基准测试,并且有多种测试模式,多种测试维度可供选择;而且使用简单、增加注解便可启动测试。

加入依赖

他们说高版本jdk自带,我用的jdk17不知道为啥没有,也没有找到有教程。

        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.35</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.35</version>
        </dependency>

插件

这里可以使用JMH Java Microbenchmark Harness插件快速生成。而且可以像 JUnit 一样,运行单独的 Benchmark 方法\运行类中所有的 Benchmark 方法.
生成代码
在这里插入图片描述

右边可以直接运行
在这里插入图片描述

第一个案列

将要测试的方法添加@Benchmark注解即可。
然后用main方法启动就可以了。

public class JMHSample_01_HelloWorld {

    @Benchmark
    public void wellHelloThere() {
        // this method was intentionally left blank.
    }
    

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

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

最后的结果,前面还有一大堆,在结果进行分析
在这里插入图片描述

结果分析(逐行分析)

结果输出有这样几个部分
版本和参数

// JMH版本号
# JMH version: 1.35  
//jdk的版本和路径参数
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\study\app\jdk\bin\java.exe
# VM options: -javaagent:D:\idea\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=9272:D:\idea\IntelliJ IDEA 2021.3.3\bin -Dfile.encoding=UTF-8

//黑洞模式
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)

//预热的次数和每一次的时间
# Warmup: 5 iterations, 10 s each //5次每次10s
// 测试的次数和时间
# Measurement: 5 iterations, 10 s each
// 超时,每次迭代10分钟
# Timeout: 10 min per iteration
//线程
# Threads: 1 thread, will synchronize iterations
//输出结果打印方式
# Benchmark mode: Throughput, ops/time//吞吐量
//执行的类
# Benchmark: org.openjdk.jmh.samples.JMHSample_01_HelloWorld.wellHelloThere

每次的统计

# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration   1: 1895950720.598 ops/s
# Warmup Iteration   2: 1906625977.172 ops/s
# Warmup Iteration   3: 2580159163.977 ops/s
# Warmup Iteration   4: 2564641382.865 ops/s
# Warmup Iteration   5: 2561493559.473 ops/s
Iteration   1: 2529900034.146 ops/s
Iteration   2: 2497119600.056 ops/s
Iteration   3: 2512703440.984 ops/s
Iteration   4: 2568341912.143 ops/s
Iteration   5: 2574424415.895 ops/s

这一部分是总体的统计
有最小值平均值和最大值 、标准差、置信区间


Result "org.openjdk.jmh.samples.JMHSample_01_HelloWorld.wellHelloThere":
  2536497880.645 ±(99.9%) 130763529.779 ops/s [Average]

  (min, avg, max) = (2497119600.056, 2536497880.645, 2574424415.895), stdev = 33958873.426
  CI (99.9%): [2405734350.865, 2667261410.424] (assumes normal distribution)

一些介绍,没啥用

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

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

NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.

这里比较重要,一般都会看

属性介绍
benchmark执行的
mod执行模式
cnt轮次
score执行的结果平均值
error误差
units单位
Benchmark                                Mode  Cnt           Score           Error  Units
JMHSample_01_HelloWorld.wellHelloThere  thrpt    5  2536497880.645 ± 130763529.779  ops/s

进程已结束,退出代码0

使用介绍

只打算简单介绍对象的配置,注解肯定用的更舒服。

对象配置

创建对象后在后面逐个添加属性,然后在使用Runner类启动。
在这里插入图片描述

Benchmark

为该方法生成基准代码,在基准列表中将该方法注册为基准,从注释中读出默认值,并大致为基准测试运行准备环境。

适用:ElementType.METHOD,后面用注解表示了

BenchmarkMode

基准测试模式声明运行此基准测试的默认模式。有关可用的基准测试模式

@Target({ElementType.METHOD, ElementType.TYPE})

Mode[] value:模式,必须的
	AverageTime - 调用的平均时间
	SampleTime - 随机取样,最后输出取样结果的分布,输出会比较多,但是不会全部统计
	SingleShotTime - 只会执行一次,通常用来测试冷启动时候的性能。
	All - 所有的benchmark modes。

Fork

执行次数,除了value默认就好了

@Target({ElementType.METHOD,ElementType.TYPE})

int BLANK_FORKS = -1;

String BLANK_ARGS = "blank_blank_blank_2014";

/** @return 表示该benchMark执行多少次 */
int value() default BLANK_FORKS;

/** @return 线束应分叉并忽略结果的次数 */
int warmups() default BLANK_FORKS;

/** @return 要运行的 JVM 可执行文件 */
String jvm() default BLANK_ARGS;

/** @return 要在命令行中替换的 JVM 参数 */
String[] jvmArgs() default { BLANK_ARGS };

/** @return 要在命令行中预置的 JVM 参数*/
String[] jvmArgsPrepend() default { BLANK_ARGS };

/** @return :要在命令行中追加的 JVM 参数*/
String[] jvmArgsAppend() default { BLANK_ARGS };

fork(0)同一个进程执行一次
fork(1)新建一个进程执行一次
fork(n)新建n个进程
默认的-1等同于传5

Warmup

允许为基准设置默认预热参数

@Target({ElementType.METHOD,ElementType.TYPE})

/** @return 预热迭代次数 */
int iterations() default BLANK_ITERATIONS;

/** @return每次预热迭代的时间 */
int time() default BLANK_TIME;

/** @return 预热迭代持续时间的时间单位 */
TimeUnit timeUnit() default TimeUnit.SECONDS;

/** @return 批大小:每个操作的基准方法调用数*/
int batchSize() default BLANK_BATCHSIZE;

Measurement

设置默认测量参数。
这个和上面一样,就换成翻译了

@Target({ElementType.METHOD,ElementType.TYPE})

/** @return Number of measurement iterations */
int iterations() default BLANK_ITERATIONS;

/** @return Time of each measurement iteration */
int time() default BLANK_TIME;

/** @return Time unit for measurement iteration duration */
TimeUnit timeUnit() default TimeUnit.SECONDS;

/** @return Batch size: number of benchmark method calls per operation */
int batchSize() default BLANK_BATCHSIZE;

OutputTimeUnit

为统计结果的时间单位

@Target({ElementType.METHOD,ElementType.TYPE})

value: 时间单位

State

状态对象通常作为参数注入到方法中Benchmar。
@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试,不同的选项含义如下:

@Target(ElementType.TYPE)

Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
Scope.Group:同一个线程在同一个 group 里共享实例
Scope.Thread:默认的 State,每个测试线程分配一个实例

感觉这个不太好理解,这里看官方示例
这个代码中measureShared多个线程会同时操作这个x。
如2线程轮流那么x是1 2 3 4 5 6
measureUnshared同时操作那么就是
1 1 2 2 3 3

	@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
    public void measureUnshared(ThreadState state) {
        // 所有基准测试线程都将调用此方法。
        //
        // 但是,由于 ThreadState 是 Scope.Thread,因此每个线程将拥有自己的状态副本,此基准将衡量未共享的情况。
        state.x++;
    }

    @Benchmark
    public void measureShared(BenchmarkState state) {
        // 由于 BenchmarkState 是 Scope.Benchmark,所有线程都将共享状态实例,我们最终将测量共享案例。
        state.x++;
    }

如果标记到类,我们看第四个代码.
那么我们相当于可以调用这个类的属性,而不用注入了。

@State(Scope.Thread)
public class JMHSample_04_DefaultState {

    double x = Math.PI;

    @Benchmark
    public void measure() {
        x++;
    }

Setup and TearDown

会在执行 benchmark 之前/后被执行,主要用于初始化/资源处理。

@Target(ElementType.METHOD)

此方法的级别。
Level value() default Level.Trial;
参数
	Level.Trial:Benchmark级别,benchmark执行完执行,最大的。
	Level.Iteration:执行迭代级别,每次执行完都会
	Level.Invocation:每次方法调用级别,每次方法调用就都执行
类型结果
TrialIteration 2:x = 1.059139639E9
Iteration# Warmup Iteration x = 3.54500352E8
Iteration 1: x = 7.0472257E8
Iteration 2:x = 1.059033135E9
Invocation每次都调用一下在这里插入图片描述
    @TearDown(Level.Iteration)
    public void check() {
        System.out.println("---------------------------------");
        System.out.println("x = " + x);
    }

    @Benchmark
    public void measureRight() {
        x++;
    }

死码消除问题

介绍

在第8给示例中主要介绍了死码优化的问题。
如果一个方法调用后没有什么用,就在编译器被消除了,但这给我做基准测试带了一些麻烦

我们看第八个:
讲道理来演示的意思是measureWrong比rigth应该慢超级多,但是我怎么测试都测试不出来,不知道为什么。而且第九个也测试不出来。

    private double x = Math.PI;

    @Benchmark
    public void baseline() {
        // do nothing, this is a baseline
    }

    @Benchmark
    public void measureWrong() {
        // This is wrong: result is not used and the entire computation is optimized away.
        Math.log(x);
    }

    @Benchmark
    public double measureRight() {
        // This is correct: the result is being used.
        return Math.log(x);
    }

在这里插入图片描述

解决

第九个示例告诉我们解决。
不过我测不出来。

  1. 使用他
  2. 注入Blackhole bh 对象,使用consume方法调用
  @Benchmark
    public void measureRight_2(Blackhole bh) {
        bh.consume(Math.log(x1));
        bh.consume(Math.log(x2));
    }

同一个进程会互相影响

众所周知,JVM擅长按配置文件优化。这对基准测试不利,因为不同的测试可以将它们的配置文件混合在一起,然后为每个测试呈现“统一错误”的代码。分叉(在单独的进程中运行)每个测试都有助于避免此问题。默认情况下,JMH 将分叉测试。

    @Benchmark
    @Fork(0)
    public int measure_1_c1() {
        return measure(c1);
    }

    /*
     * Then Counter2...
     */

    @Benchmark
    @Fork(0)
    public int measure_2_c2() {
        return measure(c2);
    }

    /*
     * Then Counter1 again...
     */

    @Benchmark
    @Fork(0)
    public int measure_3_c1_again() {
        return measure(c1);
    }

请注意,C1 更快,C2 更慢,但 C1 又慢了!这是因为 C1 和 C2 的配置文件已合并在一起。请注意分叉运行的测量是多么完美。
在这里插入图片描述

还有些案例,不过不想学了,这些够现在的用了,以后需要在学

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/400382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RedisCluster集群模式下master宕机主从切换期间Lettuce连接Redis无法使用报错Redis command timed out的问题

背景springboot使用redisTemplate访问redis cluster&#xff08;三主三从&#xff09;&#xff0c;底层是Lettuce&#xff0c;当其中一个master挂掉后&#xff0c;slave正常升为master&#xff0c;程序报错 Redis commond timed out after 6 seconds。解决手动连接集群&#xf…

【三维几何学习】网格上低分辨率的分割结果到高分辨率的投影与可视化

网格上低分辨率的分割结果到高分辨率的投影与可视化引言一、到高分辨率的投影1.1 准确率1.2 主要代码1.3 投影核心代码二、可视化代码引言 三角网格的结构特性决定了其仅用少量三角形即可表示一个完整的3D模型。增加其分辨率可以展示更多模型的形状细节。对于网格分割来说&…

可复用测试用例描述要素

测试用例的输入、操作、预期结果和评估标准、前提条件是测试用例不可少的要素&#xff0c;但对于可复用测试用例而言&#xff0c;这是不够的。本文在文献规定的测试用例要素基础上&#xff0c;增加了新的内容。从而从多个角度完整地对可复用测试用例进行了描述&#xff0c;为可…

从0开始学IntelliJ Plugin开发:一、配置环境

前言 作为一个javaer&#xff0c;相信大家平时开发都多多少少使用了一些idea的插件&#xff0c;那么在享受插件便利的同时&#xff0c;有没有好奇插件是如何开发的 笔者怀着这份好奇开始了idea插件开发学习之路&#xff0c;同时把学习的心得体会整理成系列文章作为学习笔记供…

探访人工智能领跑者:纷享销客携手30+TOP高科技企业走进旷视科技

拥有全球规模领先的计算机视觉研究院&#xff1b; 揽获28项世界顶级AI竞赛冠军&#xff1b; 世界级人工智能公司&#xff1b; 没错&#xff0c;它就是人工智能行业的务实者和领跑者&#xff0c;旷视科技。 3月3日&#xff0c;北京软件和信息服务业协会联合纷享销客&#xff0c;…

macOS 13.3 Beta 3 (22E5236f)With OpenCore 0.9.1开发版 and winPE双引导分区原版镜像

原文地址&#xff1a;http://www.imacosx.cn/112494.html&#xff08;转载请注明出处&#xff09;镜像特点完全由黑果魏叔官方制作&#xff0c;针对各种机型进行默认配置&#xff0c;让黑苹果安装不再困难。系统镜像设置为双引导分区&#xff0c;全面去除clover引导分区&#x…

ViT:Transformer在CV领域的开山之作

ViT引发的变革 Transformer最开始是作为自然语言处理&#xff08;英语&#xff1a; Natural Language Processing &#xff0c;缩写作 NLP&#xff09;领域的模型框架&#xff0c;在该领域其可谓大放异彩&#xff0c;然而自始至终都有人在不断尝试将Transformer应用到视觉领域…

快速入门深度学习1(用时1h)

速通《动手学深度学习》1写在最前面0.内容与结构1.深度学习简介1.1 问题引入1.2 思路&#xff1a;逆向思考1.3 跳过1.4 特点1.5 小结2.预备知识&#xff08;MXNet版本&#xff0c;学错了。。。。&#xff09;2.1 获取和运行本书的代码2.2 数据操作2.2.1 略过2.2.2 小结2.3 自动…

c/c++开发,无可避免的模板编程实践(篇九)-c++11的新顺序容器

一、std::array数组容器 1.1 数组的适配器-std::array std::array 是封装固定大小数组的容器&#xff0c;是c11标准库新引入的顺序容器&#xff0c;定义于头文件 <array>。 template <class T,std::size_t N > struct array; 此容器是一个聚合类型&#xff0c;其…

ChatPDF解放双手帮你解读PDF文档

一、先介绍一下吧 chatPDF是一个解读pdf文档的AI模型&#xff0c;然后封装出来的工具。如论文、合同、文书、书籍等&#xff0c;只要是PDF都能搞定&#xff0c;可支持120页【2023.3.9】的文件。据说之前支持200页&#xff0c;反正在变 最新爆火的ChatPDF&#xff0c;短短5天就…

nginx 主动健康检查搭建详解(nginx_upstream_check_module)

版本信息 nginx: 1.21 1.下载nginx_upstream_check_module模块 nginx_upstream_check_module-master.zip wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master 解压到 2. 安装nginx 略 3. 补丁安装 由于我这边安装nginx版本为nginx1.21…

你是使用什么工具调试 golang 程序的?

写过 C/C 的都是到&#xff0c;调试程序的时候通常使用 gdb 工具来进行调试&#xff0c;用起来可爽了&#xff0c;那么 gdb 是否也适合 golang 程序的调试的 我个人到是通常使用 dlv 来进行 golang 程序的调试&#xff0c;分享一波 dlv 是什么&#xff0c;全称 Delve Delve …

KiCad 编译

KiCad 编译 因为最新项目需要&#xff0c;所以看了一下KiCad的编译&#xff0c;这里介绍的是64位电脑的编译&#xff0c;32位小伙伴请绕道官网看教程呦。 您可以在KiCad内查看基本的编译教程。 我这里也是参考的官网编译教程进行的编译&#xff0c;接下来让我们一起看看吧。…

论文 | 期刊 | 专业名词解释

文章目录1. EI2. IEEE Xplore3. CN期刊3.2 CN期刊后面的数字代表什么3. SCI3.1 影响因子先立个帖子&#xff0c;后续用到的话随时更新1. EI 工程索引(EI)是由美国工程信息公司(Engineering information Inc.)编辑出版&#xff0c;历史上最悠久的一部大型综合性检索工具。 《工…

03 SWMM快速入门案例的设施参数设置与批量设置

文章目录1 雨量计1.1 雨量计基础设置1.2 雨量计数据来源2 汇水区2.1 参数讲解2.2 设置结果3 检查井3.1 参数讲解3.2 批量设置4 管道4.1 参数讲解4.2 设置结果5 出水口上一篇博客中我们已经完成了各类设施的绘制&#xff0c;本节对他们的参数进行设置1 雨量计 1.1 雨量计基础设…

第一章 C语言:数据存储

一、大小端存储大端存储&#xff1a;数据的低位字节存储在高地址小端存储&#xff1a;数据的低位字节存储在低地址不同编译器有不同的存储方式int a 10; char* p (char*)&a; printf("%x\n", *p); // a ---> 0000000a //0000 0000 0000 0000 0000 0…

教学场景应用视频试看预览功能

html5播放器视频预览功能效果 - 视频预览代码示例预播放一小段时间的视频内容&#xff0c;比如3分钟&#xff0c;然后引导用户付费观看或注册会员观看完整视频。原理&#xff1a;视频播放结束&#xff0c;执行s2j_onPlayOver()函数&#xff0c;显示提示信息或对话框&#xff0c…

Altium Designer(AD)软件使用记录03-AD软件中各层定义

Altium Designer(AD)软件使用记录03-AD软件中各层定义 重点&#xff1a; 1、常用的信号层&#xff1a;顶层&#xff0c;底层层&#xff0c;中间正片层&#xff0c;中间负片层 2、机械1层作为板框层&#xff0c;机械13层作为3D防止层&#xff0c;其他的机械层很少用 3、顶层阻焊…

AVL树详解+模拟实现

1&#xff1a;概念当数据有序&#xff0c;二叉搜索树将趋近于单叉树&#xff0c;查找元素相当于在顺序表中查找元素&#xff0c;效率低下&#xff0c;两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis创建了AVL树。特性如下&#xff1a; 左右子树高度差的绝对值不超过1左右子树…

Django/Vue实现在线考试系统-06-开发环境搭建-Visual Studio Code安装

1.0 VS Code下载和安装 Visual Studio Code,简称 VS Code,是由微软公司开发的 IDE 工具。与微软其他 IDE(如 Visual Studio)不同的是,Visual Studio Code 是跨平台的,可以安装在 Windows、Linux 和 macOS平台上运行。不仅如此, Visual Studio Code 没有限定只能开发特定…