微基准测试进入Java 9

b05673_mockupcover_normal_ 我已经几个月没有在这里写文章了,这种例外也会继续下去。 我计划在明年三月左右恢复写作。 本文末尾的说明。 等待! 不完全是最后,因为您可以向下滚动。 它在文章结尾处。 继续阅读!

三年前,我在写有关Java编译器如何优化其执行代码的文章。 或者更确切地说, javac如何做到这一点,而JIT同时做到了。 我制定了一些基准,如Esko Luontola所提到的那样,确实有些糟糕。 这些基准旨在表明JIT甚至可以在收集有关代码执行的重要统计数据之前进行优化。

该文章创建于2013年1月。两个月后, JMH (Java Microbenchmark Harness)的第一个源代码上传就发生了。 从那时起,这个工具就发展了很多,并在明年成为Java下一个版本的一部分。 我有一份合同要写一本有关Java 9的书 ,其中的第5章应该涵盖Java 9进行微基准测试的可能性。 这是开始与JMH合作的好理由。

在详细介绍如何使用JMH及其好处之前,让我们先谈谈一些微基准测试。

微基准测试

微基准测试正在衡量某些小代码片段的性能。 它很少使用,在开始为实际的商业环境做微基准测试之前,我们必须三思。 请记住,过早的优化是万恶之源。 一些开发人员对此声明进行了概括,称优化本身是万恶之源,这也许是事实。 特别是如果我们指的是微基准测试。

微基准测试是一种诱使工具,可以在不知道是否值得优化代码的情况下优化一些小东西。 当我们有一个包含多个模块的庞大应用程序时,它可以在多个服务器上运行,我们如何确定改进应用程序的某些特殊部分可以大大提高性能? 它是否会偿还增加的收入,以产生如此多的利润,以弥补我们在性能测试和开发中花费的成本? 我不愿意说你不知道,只是因为这样的说法太笼统了。 从统计学上几乎可以肯定,这种包括微基准测试在内的优化在大多数情况下不会使您感到痛苦。 它会很疼,您可能不会注意到它,甚至不会享受它,但这是一个完全不同的故事。

何时使用微基准测试? 我可以看到三个方面:

  1. 您撰写了有关微基准测试的文章。
  2. 您确定了占用应用程序中大部分资源的代码段,并且可以通过微基准测试改进。
  3. 您无法确定将占用应用程序中大部分资源的代码段,但您怀疑它。

第一个笑话。 是否可以:您可以尝试使用微基准测试,以了解其工作原理,然后了解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的目录中(与maintest处于同一级别),并创建可以启动基准测试的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 ,因为这是最后的猜测,我们从去123456123457等。

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

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

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

相关文章

摆渡车(noip2018 pj t3)

摆渡车(题目和测试右转 洛谷P5017) 做法:dp各种优化(剪枝) 这道题考场上看了一脸懵逼...第一眼看这 tm 不是个一维dp吗...结果按着这个朦胧的思路,删删改改约莫0.5h,终于过了小样例,然后一测大样例...GG了。…

关于树的冷知识

1、为什么冬天树干刷白漆? 刷的是是石灰水,主要成分是石灰乳,还有食盐,大豆粉,石榴合剂。 好处一、石灰具有一定的杀菌、杀虫作用,可以杀死寄生在树干上的一些越冬的真菌、细菌和害虫。 好处二、由于害虫一般都喜欢黑色、航脏的地方&#x…

乐玩自动化测试模块_自动化测试模型(一)自动化测试模型介绍

一个自动化测试框架就是一个集成体系,在这一体系中包含测试功能的函数库,测试数据源,测试对象标准,以及各种可重用的模块。自动化测试在发展过程中经历了以下几个阶段,模块驱动测试,数据驱动测试及对象驱动…

linux特殊权限位之setuid、setgid和sticky

我们登陆到系统之后,创建一个普通文件或者目录的时候,会有一个默认的权限。普通文件是644,目录文件是755,想必大家都知道这个是由umask这个值决定的。我们可以直接执行umask命令查看,linux系统默认的umask值是0022。想改变创建文件默认的权限,我们直接修…

js获取当前时间,并实时更新

可以使用JavaScript的Date()对象来获取当前时间&#xff0c;并使用setInterval()函数实现实时更新。 以下是一个示例代码&#xff1a; <p id"time"></p>function updateTime() {var now new Date();var hours now.getHours();var minutes now.getMi…

11.23日常

整理相关资料&#xff0c;阅读c#课本转载于:https://www.cnblogs.com/JL3Peanut/p/10032318.html

python requests 10041报错_pythonrequests返回unicode异常消息(或如何设置请求区域设置)...

您可以尝试os.strerror&#xff0c;但它可能不会返回任何内容或相同的非英语字符串。在ENGLISH_WINDOWS_SOCKET_MESSAGES {10004: "Interrupted function call.",10013: "Permission denied.",10014: "Bad address.",10022: "Invalid argu…

深度学习(花书)

1.英文主页http://www.deeplearningbook.org/lecture_slides.html 2.中文主页https://github.com/exacity/deeplearningbook-chinese 在线阅读https://exacity.github.io/deeplearningbook-chinese/转载于:https://www.cnblogs.com/Eufisky/p/10010046.html

表面配准论文1--基于高阶图匹配方法的稠密表面配准

Dense Non-rigid Surface Registration Using High-Order Graph Matching 一.摘要提出高阶图匹配方程来解决非刚性表面配准问题&#xff0c;单阶项描述了几何和外观相似性&#xff08;曲率和纹理&#xff09;&#xff0c;高阶项对内部嵌入能量&#xff08;intrinsic embedding …

Apache Spark软件包,从XML到JSON

Apache Spark社区为扩展Spark付出了很多努力。 最近&#xff0c;我们希望将XML数据集转换为更易于查询的内容。 我们主要对每天进行的数十亿笔交易之上的数据探索感兴趣。 XML是一种众所周知的格式&#xff0c;但是有时使用起来可能很复杂。 例如&#xff0c;在Apache Hive中&a…

js中几种实用的跨域方法原理详解

这里说的js跨域是指通过js在不同的域之间进行数据传输或通信&#xff0c;比如用ajax向一个不同的域请求数据&#xff0c;或者通过js获取页面中不同域的框架中(iframe)的数据。只要协议、域名、端口有任何一个不同&#xff0c;都被当作是不同的域。 下表给出了相对http://store.…

python中matplotlib画图_Python-matplotlib画图(莫烦笔记)

这个是我对于莫烦老师的matplotlib模块的视频做的一个笔记。1.前言Matplotlib是一个python的 2D绘图库&#xff0c;它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。通过Matplotlib&#xff0c;开发者可以仅需要几行代码&#xff0c;便可以生成绘图&#xff0c…

js词法分析

javascript词法分析函数在运行的瞬间&#xff0c;生成一个活动对象&#xff08;Active Object&#xff09;&#xff0c;简称AO&#xff1b;具体分为两个阶段&#xff1a;一.分析阶段JavaScript代码运行前有一个类似编译的过程即词法分析&#xff0c;词法分析主要有三个步骤&…

如何使一维数组一行一行的输出成二维数组的格式

以9个元素的数组为例&#xff0c;输出3x3的格式 基本思想是对每一个元素的index进行检查&#xff0c;当index是3,6,9等可以被3整除的数时&#xff0c;在打印它们之前&#xff0c;先打印一个\n。注意0也可以被3整除&#xff0c;但是此时不打印\n&#xff0c;所以要加上i>0的…

python我想对你说_python学习第3天-----字典、解构

1.字典1)定义&#xff1a;查找速度快&#xff0c;效率高&#xff1b;用{}括起来&#xff0c;内部使用key:value的形式来保存数据&#xff1b;键值对是无序的&#xff0c;不是按照定义的方式保存数据的(类似于json文件)&#xff0c;例如&#xff1a; {jay:周杰伦,jj::林俊杰}注&…

bms_output.put_line使用方法

https://blog.csdn.net/sxww321/article/details/4020300转载于:https://www.cnblogs.com/diyunpeng/p/10022923.html

linux 权限掩码 umask

一 权限掩码umaskumask是chmod配套的&#xff0c;总共为4位&#xff08;gid/uid,属主&#xff0c;组权&#xff0c;其它用户的权限&#xff09;,不过通常用到的是后3个&#xff0c;例如你用chmod 755 file&#xff08;此时这文件的权限是属主读(4)写(2)&#xff0b;执行(1),同…

ant 构建_有用的Ant构建标签

ant 构建问题&#xff1a; 如何在ant文件中执行以下任务&#xff1f; 制作zip文件。 运行命令。 将文件复制到远程计算机。 在远程Linux机器上运行命令。 打开输入框并响应输入值。 拨打蚂蚁电话。 答案&#xff1a; 1.制作zip文件&#xff1a; 以下是在ant中制作zip文…

C++文件交互(txt、excel)

前些天完成了计算机视觉大作业&#xff0c;《基于双目立体视觉的深度图像生成》&#xff0c;虽然刚开始觉得作业很难&#xff0c;也没找到现成的程序&#xff0c;但做到最后还是学到很多知识&#xff0c;get到很多技能&#xff0c;现在一一总结下来。 1、C输出数据到txt 包含…