微基准测试进入Java 9
我已经几个月没有在这里写文章了,这种例外也会继续下去。 我计划在明年三月左右恢复写作。 本文末尾的说明。 等待! 不完全是最后,因为您可以向下滚动。 它在文章结尾处。 继续阅读!
三年前,我在写有关Java编译器如何优化其执行代码的文章。 或者更确切地说, javac
如何做到这一点,而JIT同时做到了。 我制定了一些基准,如Esko Luontola所提到的那样,确实有些糟糕。 这些基准旨在表明JIT甚至可以在收集有关代码执行的重要统计数据之前进行优化。
该文章创建于2013年1月。两个月后, JMH (Java Microbenchmark Harness)的第一个源代码上传就发生了。 从那时起,这个工具就发展了很多,并在明年成为Java下一个版本的一部分。 我有一份合同要写一本有关Java 9的书 ,其中的第5章应该涵盖Java 9进行微基准测试的可能性。 这是开始与JMH合作的好理由。
在详细介绍如何使用JMH及其好处之前,让我们先谈谈一些微基准测试。
微基准测试
微基准测试正在衡量某些小代码片段的性能。 它很少使用,在开始为实际的商业环境做微基准测试之前,我们必须三思。 请记住,过早的优化是万恶之源。 一些开发人员对此声明进行了概括,称优化本身是万恶之源,这也许是事实。 特别是如果我们指的是微基准测试。
微基准测试是一种诱使工具,可以在不知道是否值得优化代码的情况下优化一些小东西。 当我们有一个包含多个模块的庞大应用程序时,它可以在多个服务器上运行,我们如何确定改进应用程序的某些特殊部分可以大大提高性能? 它是否会偿还增加的收入,以产生如此多的利润,以弥补我们在性能测试和开发中花费的成本? 我不愿意说你不知道,只是因为这样的说法太笼统了。 从统计学上几乎可以肯定,这种包括微基准测试在内的优化在大多数情况下不会使您感到痛苦。 它会很疼,您可能不会注意到它,甚至不会享受它,但这是一个完全不同的故事。
何时使用微基准测试? 我可以看到三个方面:
- 您撰写了有关微基准测试的文章。
- 您确定了占用应用程序中大部分资源的代码段,并且可以通过微基准测试改进。
- 您无法确定将占用应用程序中大部分资源的代码段,但您怀疑它。
第一个笑话。 是否可以:您可以尝试使用微基准测试,以了解其工作原理,然后了解Java代码如何工作,哪些运行速度快,哪些运行速度不快。 去年, Takipi发表了一篇文章,他们试图测量Lambda的速度。 阅读这篇非常好的文章,并清楚地证明了博客相对于为印刷品写东西的主要优势。 读者评论并指出了错误,并在本文中进行了更正。
第二是通常的情况。 好的,在读者发表评论之前,纠正了我的观点:第二种应该是通常的情况。 第三是在开发库时,您只是不知道将使用该库的所有应用程序。 在这种情况下,您将尝试优化您认为对大多数可想象的应用程序最关键的部分。 即使在这种情况下,最好还是使用一些示例应用程序。
陷阱
微基准测试的陷阱是什么? 基准测试是作为实验完成的。 我编写的第一个程序是TI计算器代码,我可以算出该程序为分解两个大(当时为10位)素数的步数。 即使在那个时候,我仍在使用旧的俄罗斯秒表来测量懒惰的时间来计算步数。 实验和测量更加容易。
今天,您无法计算CPU执行的步骤数。 程序员无法控制的因素很多,它们可能会改变应用程序的性能,因此无法计算步骤。 我们将测量留给了我们,并且获得了所有测量的所有问题。
测量的最大问题是什么? 我们对某物感兴趣,比如说X,我们通常无法衡量。 因此,我们改为测量Y,并希望Y和X的值耦合在一起。 我们要测量房间的长度,但是要测量激光束从一端到达另一端所花费的时间。 在这种情况下,长度X和时间Y紧密耦合。 很多时候,X和Y仅或多或少地相关。 在大多数情况下,人们进行测量时,X和Y根本不相关。 人们仍然把钱和更多的钱花在这种测量支持的决策上。 以政治选举为例。
微基准测试没有什么不同。 很难做到这一点。 如果您对细节和可能的陷阱感兴趣, Aleksey Shipilev会提供一个不错的一小时视频。 第一个问题是如何衡量执行时间。 小代码运行时间短,并且在测量开始和结束时System.currentTimeMillis()
可能只是返回相同的值,因为我们仍处于同一毫秒内。 即使执行时间为10ms,纯粹由于我们测量时间的量化,测量误差仍然至少为10%。 幸运的是有System.nanoTime()
。 我们开心吗,文森特?
并不是的。 如文档所述, nanoTime()
返回正在运行的Java虚拟机的高分辨率时间源的当前值,以纳秒为单位。 什么是“当前”? 何时进行调用? 还是退回时? 还是介于两者之间? 选择所需的一个,您可能仍然会失败。 所有Java实现都应保证在最近1000ns内该当前值相同。
文档中使用nanoTime()
之前的另一个警告: 跨越大约292年(263纳秒)的连续调用中的差异由于数值溢出而无法正确计算经过时间。
292年? 真?
还有其他问题。 启动Java代码时,代码的前几千次执行将在没有运行时优化的情况下进行解释或执行。 与静态编译语言(如Swift,C,C ++或Golang)的编译器相比,JIT的优势在于,它可以从代码执行中收集运行时信息,并且当发现上次执行的编译基于最近的版本可能会更好运行时统计信息将再次编译代码。 对于也尝试使用统计信息调整其操作参数的垃圾收集可能同样如此。 因此,编写良好的服务器应用程序会随着时间的推移获得一些性能。 它们的启动速度稍慢,然后变得更快。 如果重新启动服务器,则整个迭代将再次开始。
如果您执行微型基准测试,则应注意这种行为。 您是要测量应用程序在预热期间的性能还是在操作过程中如何真正执行?
解决方案是尝试考虑所有这些警告的微型基准测试工具。 Java 9是JMH。
什么是JMH?
“ JMH是用于构建,运行和分析以Java和其他针对JVM的其他语言编写的nano / micro / milli / macro基准测试的Java工具。” (摘自JMH的官方网站 )
您可以将jmh作为独立于您测量的实际项目的独立项目运行,也可以仅将测量代码存储在单独的目录中。 该线束将根据生产类文件进行编译并执行基准测试。 如我所见,最简单的方法是使用Gradle插件执行JMH。 您将基准测试代码存储在名为jmh
的目录中(与main
和test
处于同一级别),并创建可以启动基准测试的main
。
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.io.IOException;public class MicroBenchmark {public static void main(String... args) throws IOException, RunnerException {Options opt = new OptionsBuilder().include(MicroBenchmark.class.getSimpleName()).forks(1).build();new Runner(opt).run();}
有一个不错的构建器界面用于配置,还有一个可以执行基准测试的Runner
类。
玩一点
在《 Java 9编程实例》一书中,其中一个例子是Mastermind游戏 。 第五章是关于并行解决游戏以加快猜测速度的所有内容。 (如果您不了解该游戏,请在Wikipedia上阅读它,我不想在这里解释它,但是您需要它来理解以下内容。)
正常的猜测很简单。 有一个隐藏的秘密。 秘诀是从6种颜色中选择4种不同颜色的钉子。 当我们猜测时,我们一个接一个地考虑可能的颜色变化,并向表格提出问题:如果这种选择是秘密,所有答案都是正确的吗? 换句话说:这个猜测可以隐藏起来吗,或者以前的答案在答案中有矛盾吗? 如果可以将这种猜测作为秘密,那么我们将尝试将钉子放在桌子上。 答案可能是4/0(alleluia)或其他。 在后一种情况下,我们继续搜索。 这样,可以通过五个步骤解决6色4列表格。
为了简化和可视化,我们用数字命名颜色,例如01234456789
(在jmh基准中有10种颜色,因为6种颜色还不够)和6种钉子。 这个秘密,我们使用是987654
,因为这是最后的猜测,我们从去123456
, 123457
等。
1983年8月,当我在瑞典学校计算机(ABC80)上使用BASIC语言首次编写此游戏时,在运行于40MHz 6种颜色,4个位置的z80处理器上,每次猜测都花了20到30秒。 如今,我的MacBook Pro可以使用10种颜色和6种钉子,单线程大约每秒7次玩整个游戏。 但是,当我的机器中有4个处理器支持8个并行线程时,这还不够。
为了加快执行速度,我将猜测空间划分为相等的间隔,并启动了单独的猜测器,每个猜测器将猜测分散到阻塞队列中。 主线程从队列中读取并在猜测出现时将其放在表上。 万一某些线程创建一个猜测而主线程尝试将其用作猜测时已过时,则可能需要一些后期处理,但我们仍然希望可以大大提高速度。
真的加快了猜测速度吗? 那是JMH的目的。
为了运行基准测试,我们需要一些可以实际执行游戏的代码
@State(Scope.Benchmark)public static class ThreadsAndQueueSizes {@Param(value = {"1", "4", "8", "16", "32"})String nrThreads;@Param(value = { "1", "10", "100", "1000000"})String queueSize;}@Benchmark@Fork(1)public void playParallel(ThreadsAndQueueSizes t3qs) throws InterruptedException {int nrThreads = Integer.valueOf(t3qs.nrThreads);int queueSize = Integer.valueOf(t3qs.queueSize);new ParallelGamePlayer(nrThreads, queueSize).play();}@Benchmark@Fork(1)public void playSimple(){new SimpleGamePlayer().play();}
JMH框架将多次执行代码,以测量使用多个参数运行的时间。 将执行方法playParallel
来针对playParallel
和32个线程运行算法,每个线程的最大队列长度分别为playParallel
和一百万。 当队列已满时,各个猜测者将停止猜测,直到主线程从队列中拉出至少一个猜测为止。
我怀疑如果我们有很多线程,并且我们不限制队列的长度,那么工作线程将使用仅基于空表的初始猜测来填充队列,因此不会带来太多价值。 执行将近15分钟后,我们会看到什么?
Benchmark (nrThreads) (queueSize) Mode Cnt Score Error Units
MicroBenchmark.playParallel 1 1 thrpt 20 6.871 ± 0.720 ops/s
MicroBenchmark.playParallel 1 10 thrpt 20 7.481 ± 0.463 ops/s
MicroBenchmark.playParallel 1 100 thrpt 20 7.491 ± 0.577 ops/s
MicroBenchmark.playParallel 1 1000000 thrpt 20 7.667 ± 0.110 ops/s
MicroBenchmark.playParallel 4 1 thrpt 20 13.786 ± 0.260 ops/s
MicroBenchmark.playParallel 4 10 thrpt 20 13.407 ± 0.517 ops/s
MicroBenchmark.playParallel 4 100 thrpt 20 13.251 ± 0.296 ops/s
MicroBenchmark.playParallel 4 1000000 thrpt 20 11.829 ± 0.232 ops/s
MicroBenchmark.playParallel 8 1 thrpt 20 14.030 ± 0.252 ops/s
MicroBenchmark.playParallel 8 10 thrpt 20 13.565 ± 0.345 ops/s
MicroBenchmark.playParallel 8 100 thrpt 20 12.944 ± 0.265 ops/s
MicroBenchmark.playParallel 8 1000000 thrpt 20 10.870 ± 0.388 ops/s
MicroBenchmark.playParallel 16 1 thrpt 20 16.698 ± 0.364 ops/s
MicroBenchmark.playParallel 16 10 thrpt 20 16.726 ± 0.288 ops/s
MicroBenchmark.playParallel 16 100 thrpt 20 16.662 ± 0.202 ops/s
MicroBenchmark.playParallel 16 1000000 thrpt 20 10.139 ± 0.783 ops/s
MicroBenchmark.playParallel 32 1 thrpt 20 16.109 ± 0.472 ops/s
MicroBenchmark.playParallel 32 10 thrpt 20 16.598 ± 0.415 ops/s
MicroBenchmark.playParallel 32 100 thrpt 20 15.883 ± 0.454 ops/s
MicroBenchmark.playParallel 32 1000000 thrpt 20 6.103 ± 0.867 ops/s
MicroBenchmark.playSimple N/A N/A thrpt 20 6.354 ± 0.200 ops/s
(分数越高,越好。)它表明,如果启动16个线程并且在某种程度上限制了队列的长度,我们将获得最佳性能。 在一个线程(一个主线程和一个工作线程)上运行并行算法要比单线程实现慢一些。 这似乎没问题:我们有启动新线程以及线程之间通信的开销。 我们拥有的最大性能约为16个线程。 因为我们可以在这台机器上拥有8个内核,所以我们希望能看到8个内核。为什么?
如果我们用随机的东西替换标准的密码987654
(即使对于CPU来说也很无聊)会怎样?
Benchmark (nrThreads) (queueSize) Mode Cnt Score Error Units
MicroBenchmark.playParallel 1 1 thrpt 20 12.141 ± 1.385 ops/s
MicroBenchmark.playParallel 1 10 thrpt 20 12.522 ± 1.496 ops/s
MicroBenchmark.playParallel 1 100 thrpt 20 12.516 ± 1.712 ops/s
MicroBenchmark.playParallel 1 1000000 thrpt 20 11.930 ± 1.188 ops/s
MicroBenchmark.playParallel 4 1 thrpt 20 19.412 ± 0.877 ops/s
MicroBenchmark.playParallel 4 10 thrpt 20 17.989 ± 1.248 ops/s
MicroBenchmark.playParallel 4 100 thrpt 20 16.826 ± 1.703 ops/s
MicroBenchmark.playParallel 4 1000000 thrpt 20 15.814 ± 0.697 ops/s
MicroBenchmark.playParallel 8 1 thrpt 20 19.733 ± 0.687 ops/s
MicroBenchmark.playParallel 8 10 thrpt 20 19.356 ± 1.004 ops/s
MicroBenchmark.playParallel 8 100 thrpt 20 19.571 ± 0.542 ops/s
MicroBenchmark.playParallel 8 1000000 thrpt 20 12.640 ± 0.694 ops/s
MicroBenchmark.playParallel 16 1 thrpt 20 16.527 ± 0.372 ops/s
MicroBenchmark.playParallel 16 10 thrpt 20 19.021 ± 0.475 ops/s
MicroBenchmark.playParallel 16 100 thrpt 20 18.465 ± 0.504 ops/s
MicroBenchmark.playParallel 16 1000000 thrpt 20 10.220 ± 1.043 ops/s
MicroBenchmark.playParallel 32 1 thrpt 20 17.816 ± 0.468 ops/s
MicroBenchmark.playParallel 32 10 thrpt 20 17.555 ± 0.465 ops/s
MicroBenchmark.playParallel 32 100 thrpt 20 17.236 ± 0.605 ops/s
MicroBenchmark.playParallel 32 1000000 thrpt 20 6.861 ± 1.017 ops/s
由于我们不需要仔细研究所有可能的变化,因此性能得以提高。 如果是一个线程,则增加一倍。 在有多个线程的情况下,增益不是很多。 并请注意,这不会提高代码本身的速度,只能使用统计的随机机密来更实际地进行测量。 我们还可以看到,在8个线程中获得16个线程不再有意义。 仅当我们选择接近变体结尾的秘密时,这才有意义。 为什么? 根据您在此处看到的内容以及GitHub中提供的源代码,您可以给出答案。
摘要
计划于2017年2月发行《 Java 9示例编程 》一书。但是,由于我们生活在一个开放源码的世界中,因此您可以使发布者控制对1.xx-SNAPSHOT
版本的访问。 现在,我告诉了您在编写本书代码时使用的初步GitHub URL,您还可以预购eBook,并提供反馈以帮助我创建更好的书。
翻译自: https://www.javacodegeeks.com/2016/09/microbenchmarking-comes-java-9.html
微基准测试进入Java 9相关推荐
- java基准测试_微基准测试进入Java 9
java基准测试 我已经几个月没有在这里写文章了,这种例外还会继续. 我计划在明年三月左右恢复写作. 本文末尾的说明. 等待! 不完全是最后,因为您可以向下滚动. 它在文章结尾处. 继续阅读! 三年前 ...
- 如何在Java中编写正确的微基准测试?
您如何用Java编写(并运行)正确的微基准测试? 我正在寻找一些代码示例和注释,以说明要考虑的各种问题. 示例:基准测试应测量时间/迭代或迭代/时间,为什么? 相关: 秒表基准测试是否可以接受? #1 ...
- Java微基准测试框架JMH
本文转自:https://www.xncoding.com/2018/01/07/java/jmh.html 作者:XiongNeng JMH,即Java Microbenchmark Harness ...
- 使用JMH做Java微基准测试
摘要: # 使用JMH做Java微基准测试 在使用Java编程过程中,我们对于一些代码调用的细节有多种编写方式,但是不确定它们性能时,往往采用重复多次计数的方式来解决.但是随着JVM不断的进化,随着代 ...
- JMH(Java Microbenchmark Harness) Java微基准测试
官网:OpenJDK: jmh 什么是JMH?微基准测试,他是测的某一个方法的性能到底是好或者不好,换了方法的实现之后他的性能到底好还是不好 创建JMH测试 创建Maven项目,添加依赖,我们需要添加 ...
- JMH(Java Microbenchmark Harness, java微基准测试工具)
JMH是什么 JMH是Java Microbenchmark Harness(java 微基准测试工具)的简称,是openJDK的一部分. JMH VS JMeter 看来简单比较下他们俩.JMete ...
- Dart微基准测试第一部分
在过去的几个月里,我开始收到越来越多关于某些特定 Dart 操作性能的问题.以下是Romain Rastel在他关于提高 Flutter中 ChangeNotifier 性能的工作的背景下提出的此类问 ...
- 微基准测试 r_在您的构建过程中添加微基准测试
微基准测试 r 介绍 作为一个行业,我们正在采用更高的透明度和更可预测的构建过程,以降低构建软件的风险. 持续交付的核心原则之一是通过反馈循环收集反馈. 在Dev9中 ,我们采用了与CD原则一致的&q ...
- 关于微服务和 Java 需要知道的 5 件事
概览 许多企业在不断努力加快开发速度,减少客户遇到的宕机时间 .微服务架构是更快地迭代.更高效地扩展和创建适应能力更强的应用程序的唯一途径.使用微服务构建的应用程序由各种各样的服务组成,这些服务执行不 ...
最新文章
- webpack4.0让编译速度飙升
- 线程堆栈大小 pthread_attr_setstacksize 的使用
- php addslashes 数组,用递归addslashes函数转义数组
- go项目中使用数据库的配置文件
- spring 获取cookies_springMVC操作cookie和session
- 模板技巧之:费用科目条件过滤
- /proc/sysrq-trigger文件的强大功能 shell
- matlab 颜色代码 大全_空客 | 红、绿、蓝、白,各类颜色代码的含义
- Akka-CQRS(15)- Http标准安全解决方案:OAuth2+JWT
- A64指令集如何将64位的数值写入通用寄存器
- 学习利器,借助Tampermonkey写一个B站视频加速器脚本
- 电视游戏大厅现状:“渠道为王”遭遇水土不服
- Android逆向之分析某锁机恶意软件
- 详解OpenCV的椭圆曲线点坐标近似计算函数ellipse2Poly()
- 前端剑法第二式————淬焰
- HTML 几种特别分割线特效 详细出处参考:http://www.jb51.net/web/28414.html
- 【涂鸦物联网足迹】涂鸦云平台接口列表—智能门锁
- linux命令里的xz是干嘛的,xz命令 - Linux命令大全 | linux教程
- 网易2017内推笔试题--小易老师的疯狂数列
- 计算机发展史上有杰出贡献的科学家,山东省计算机文化基础考试练习题
热门文章
- java io系列09之 FileDescriptor总结
- JavaFX之TableView的MenuButton
- win10操作技巧介绍,很实用!
- HashSet的存储方式
- java getimage_Java ImageView.getImage方法代码示例
- 什么注解可以改变BigDecimal类型的字段返回的小数位数?
- 华为云微认证答题_158人次通过华为认证考试!今年,湖北这所高校频现大动作...
- python queue 生产者 消费者_【python】-- 队列(Queue)、生产者消费者模型
- oracle连接外部数据库_使用Oracle验证外部数据
- java登录界面命令_Java命令行界面(第16部分):JArgp