当前位置:   article > 正文

顶级Java才懂的,基准测试JMH_预热过程,逐步放量到相应的服务节点

预热过程,逐步放量到相应的服务节点

复制代码

这样的统计方式,用在业务代码里,哪怕是APM里,并不见得有什么问题。

可惜的是,这段代码的统计结果,并不见得一定准确。举个例子来说,JVM在执行时,会对一些代码块,或者一些频繁执行的逻辑,进行JIT编译和内联优化,在得到一个稳定的测试结果之前,需要先循环上上万次,进行预热。预热前和预热后的性能差别是非常大的。

另外,评估性能,有很多的指标。如果这些指标数据,每次都要手工去算的话,那肯定是枯燥乏味且低效的。

JMH(the Java Microbenchmark Harness) 就是这样一个能够做基准测试的工具。如果你通过我们一系列的工具,定位到了热点代码,要测试它的性能数据,评估改善情况,就可以交给JMH。它的测量精度非常高,最高可达到纳秒的级别。

JMH已经在JDK 12中被包含,其他版本的需要自行引入maven,坐标如下。

org.openjdk.jmh

jmh-core

1.23

org.openjdk.jmh

jmh-generator-annprocess

1.23

provided

复制代码

下面,我们介绍一下这个工具的使用。

2. 关键注解


JMH是一个jar包,它和单元测试框架JUnit非常的像,可以通过注解进行一些基础配置。这部分配置有很多是可以通过main方法的OptionsBuilder进行设置的。

image.png

上图是一个典型的JMH程序执行的内容。通过开启多个进程,多个线程,首先执行预热,然后执行迭代,最后汇总所有的测试数据进行分析。在执行前后,还可以根据粒度处理一些前置和后置操作。

一个简单的代码如下:

@BenchmarkMode(Mode.Throughput)

@OutputTimeUnit(TimeUnit.MILLISECONDS)

@State(Scope.Thread)

@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)

@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)

@Fork(1)

@Threads(2)

public class BenchmarkTest {

@Benchmark

public long shift() {

long t = 455565655225562L;

long a = 0;

for (int i = 0; i < 1000; i++) {

a = t >> 30;

}

return a;

}

@Benchmark

public long div() {

long t = 455565655225562L;

long a = 0;

for (int i = 0; i < 1000; i++) {

a = t / 1024 / 1024 / 1024;

}

return a;

}

public static void main(String[] args) throws Exception {

Options opts = new OptionsBuilder()

.include(BenchmarkTest.class.getSimpleName())

.resultFormat(ResultFormatType.JSON)

.build();

new Runner(opts).run();

}

}

复制代码

下面,我们逐一介绍一下比较关键的注解和参数。

@Warmup

样例。

@Warmup(

iterations = 5,

time = 1,

timeUnit = TimeUnit.SECONDS)

复制代码

我们不止一次提到预热,warmup这个注解,可以用在类或者方法上,进行预热配置。可以看到,它有几个配置参数。

  • timeUnit:时间的单位,默认的单位是秒。

  • iterations:预热阶段的迭代数。

  • time:每次预热的时间。

  • batchSize:批处理大小,指定了每次操作调用几次方法。

上面的注解,意思是对代码预热总计5秒(迭代5次,每次一秒) 。预热过程的测试数据,是不记录测量结果的。

我们可以看一下它执行的效果:

Warmup: 3 iterations, 1 s each

Warmup Iteration 1: 0.281 ops/ns

Warmup Iteration 2: 0.376 ops/ns

Warmup Iteration 3: 0.483 ops/ns

复制代码

一般来说,基准测试都是针对的比较小的、执行速度相对较快的代码块。这些代码有很大的可能被编译、内联,在编码的时候保持方法的精简,对JIT也是有好的。

说到预热,就不得不提一下在分布式环境下的服务预热。在对服务节点进行发布的时候,通常也会有预热过程,逐步放量到相应的服务节点,直到服务达到最优状态。如下图所示,负载均衡负责这个放量过程,一般是根据百分比进行放量。

image.png

@Measurement

样例如下。

@Measurement(

iterations = 5,

time = 1,

timeUnit = TimeUnit.SECONDS)

复制代码

MeasurementWarmup的参数是一样的。不同于预热,它指的是真正的迭代次数。

我们能够从日志中看到这个执行过程:

Measurement: 5 iterations, 1 s each

Iteration 1: 1646.000 ns/op

Iteration 2: 1243.000 ns/op

Iteration 3: 1273.000 ns/op

Iteration 4: 1395.000 ns/op

Iteration 5: 1423.000 ns/op

复制代码

虽然经过预热之后,代码都能表现出它的最优状态,但一般和实际应用场景还是有些出入的。如果你的测试机器性能很高,或者你的测试机资源利用已经达到了极限,都会影响测试结果的数值。通常情况下,我都会在测试的时候,给机器充足的资源,保持一个稳定的环境。在分析结果的时候,也更加关注不同实现方式的性能差异,而不是测试数据本身。

@BenchmarkMode

此注解用来指定基准测试类型,对应Mode选项,用来修饰类和方法都可以。这里的value,是一个数组,可以配置多个统计维度。比如:

@BenchmarkMode({Throughput,Mode.AverageTime})。统计的就是吞吐量和平均执行时间两个指标。

所谓的模式,在JMH中,可以分为以下几种:

  • Throughput: 整体吞吐量,比如QPS,单位时间内的调用量等。

  • AverageTime: 平均耗时,指的是每次执行的平均时间。如果这个值很小不好辨认,可以把统计的单位时间调小一点。

  • SampleTime: 随机取样

  • SingleShotTime: 如果你想要测试仅仅一次的性能,比如第一次初始化花了多长时间,就可以使用这个参数,其实和传统的main方法没有什么区别。

  • All: 所有的指标,都算一遍,你可以设置成这个参数看下效果。

我们拿平均时间,看一下一个大体的执行结果:

Result “com.github.xjjdog.tuning.BenchmarkTest.shift”:

2.068 ±(99.9%) 0.038 ns/op [Average]

(min, avg, max) = (2.059, 2.068, 2.083), stdev = 0.010

CI (99.9%): [2.030, 2.106] (assumes normal distribution)

复制代码

由于我们声明的时间单位是纳秒,本次shift方法的平均响应时间就是2.068纳秒。

我们也可以看下最终的耗时时间。

Benchmark Mode Cnt Score Error Units

BenchmarkTest.div avgt 5 2.072 ± 0.053 ns/op

BenchmarkTest.shift avgt 5 2.068 ± 0.038 ns/op

复制代码

由于是平均数,这里的Error值的是误差的意思(或者波动)。

可以看到,在衡量这些指标的时候,都有一个时间维度,它就是通过**@OutputTimeUnit**注解进行配置的。

这个就比较简单了,它指明了基准测试结果的时间类型。可用于类或者方法上。一般选择秒、毫秒、微秒,纳秒那是针对的速度非常快的方法。

举个例子,@BenchmarkMode(Mode.Throughput)@OutputTimeUnit(TimeUnit.MILLISECONDS)进行组合,代表的就是每毫秒的吞吐量。

如下面的关于吞吐量的结果,就是以毫秒计算的。

Benchmark Mode Cnt Score Error Units

BenchmarkTest.div thrpt 5 482999.685 ± 6415.832 ops/ms

BenchmarkTest.shift thrpt 5 480599.263 ± 20752.609 ops/ms

复制代码

OutputTimeUnit注解同样可以修饰类或者方法,通过更改时间级别,可以获取更加易读的结果。

@Fork

fork的值一般设置成1,表示只使用一个进程进行测试;如果这个数字大于1,表示会启用新的进程进行测试;但如果设置成0,程序依然会运行,不过这样是在用户的JVM进程上运行的,可以看下下面的提示,但不推荐这么做。

Fork: N/A, test runs in the host VM

*** WARNING: Non-forked runs may silently omit JVM options, mess up profilers, disable compiler hints, etc. ***

*** WARNING: Use non-forked runs only for debugging purposes, not for actual performance runs. ***

复制代码

那么fork到底是在进程还是线程环境里运行呢?我们追踪一下JMH的源码,发现每个fork进程是单独运行在Proccess进程里的,这样就可以做完全的环境隔离,避免交叉影响。它的输入输出流,通过Socket连接的模式,发送到我们的执行终端。

image.png

在这里分享一个小技巧。其实fork注解有一个参数叫做jvmArgsAppend,我们可以通过它传递一些JVM的参数。

@Fork(value = 3, jvmArgsAppend = {“-Xmx2048m”, “-server”, “-XX:+AggressiveOpts”})

复制代码

在平常的测试中,也可以适当增加fork数,来减少测试的误差。

@Threads

fork是面向进程的,而Threads是面向线程的。指定了这个注解以后,将会开启并行测试。

如果配置了 Threads.MAX ,则使用和处理机器核数相同的线程数。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

看完上述知识点如果你深感Java基础不够扎实,或者刷题刷的不够、知识不全面

小编专门为你量身定制了一套<Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>

image

针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺

image

全都是一丢一丢的收集整理纯手打出来的

更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

image

image
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>

[外链图片转存中…(img-QQIku6lN-1712536413891)]

针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺

[外链图片转存中…(img-gVXpYo7w-1712536413891)]

全都是一丢一丢的收集整理纯手打出来的

更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

[外链图片转存中…(img-zSdFG8sG-1712536413892)]

[外链图片转存中…(img-EubRSPBe-1712536413892)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/793498
推荐阅读
相关标签
  

闽ICP备14008679号