问题标签 [jmh]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
5 回答
14918 浏览

gradle - 如何将 JMH 与 gradle 一起使用?

我想将JMH(一个 OpenJDK 微基准测试工具)与gradle一起使用。但是,我在编译时得到了 NPE。另一方面,JMH 在从 Maven 中使用时可以工作。

我没有发布任何内容,因为它是基本的 - 应用 java 插件并添加对 JHM 工具( )的build.gradle依赖。org.openjdk.jmh:jmh-core:0.2

我已经尝试过这里写的内容,但没有成功。

我还需要做什么?我想设置代理的东西,但我还是没弄明白。

例外:

0 投票
2 回答
520 浏览

java - 我应该使用“-server”选项运行 JMH 基准测试吗

我正在使用JMH,一个 OpenJDK 微基准测试工具。构建过程创建microbenchmarks.jar我调用java -jar并传递 jar 名称和 JMH 参数。

我想知道我们是否应该使用-server选项运行基准测试,为什么?

换句话说,我应该运行我的基准测试:

0 投票
1 回答
5271 浏览

java - JMH 没有上课以进行基准测试

有一个错误,JMH 没有选择我的课程进行基准测试。

其中 EventRunner 包含:

我得到这个错误:

排除:org.sample.MyBenchmark.testMethod,不匹配 com.stecurra.benchmark.strategy.EventRunner 没有匹配的基准。拼写错误的正则表达式?使用 -v 进行详细输出。

如何将我的正则表达式更改为有效?

谢谢

0 投票
2 回答
593 浏览

java - JMH 谜题:StringBuilder 与 StringBand

我很难理解这个基准发生了什么。我想测量我的示例类StringBandStringBuilder. with 的想法StringBand是在 上连接字符串toString(),而不是在append().

来源

这是StringBand源代码-为基准而精简:

此代码使用:UnsafeUtil.getChars()要实际获取Stringchar[] 而无需复制,请参见此处的代码。我们也可以使用getChars()它仍然是一样的。

这是 JMH 测试:

分析

这是我对添加两个 20 个字符的字符串时发生的情况的理解。

字符串生成器

  • new char[20+16]已创建(36 个字符)
  • arraycopy被调用复制 20 个string1字符到StringBuilder
  • 在第二次附加之前,StringBuilder扩展容量,因为 40 > 36
  • 因此,new char[36*2+2]被创建
  • arraycopy新缓冲区的 20 个字符
  • arraycopy追加 20 个字符string2
  • 最后,toString()返回new String(buffer, 0, 40)

弦带

  • new String[2]被建造
  • 两者都追加只是将字符串保留在内部缓冲区中,直到toString()被调用
  • length增加了两次
  • new char[40]已创建(结果字符串的总长度)
  • arraycopy20个第一个字符串字符(UnsafeUtil提供字符串的真实char[]缓冲区)
  • arraycopy20 秒的字符串字符
  • 最后,返回new String(buffer, 0, 40)

期望

StringBand我们有:

  • 少一个arraycopy- 这样做的全部目的是什么
  • 更少的分配大小:new String[]new char[]与两个new char[]
  • 另外,我们没有像StringBuilder方法中那样进行很多检查(对于大小等)

所以我希望它的StringBand工作原理至少与 相同StringBuilder,如果不是更快的话。

基准测试结果

我在 2013 年中期的 MacBookPro 上运行基准测试。使用 JMH v0.2 和 Java 1.7b45

命令:

预热迭代次数(2)很好,因为我可以看到第二次迭代达到了相同的性能。

结果是说StringBuilder快两倍。当我将线程数增加到 16 或BlackHole在代码中显式使用 s 时,也会发生同样的情况。

为什么?

0 投票
5 回答
5038 浏览

java - 在构造函数中设置 Java 集合的大小是否更好?

如果我知道当时的大小,将大小传递Collection给构造函数会更好吗?在扩展和分配/重新分配Collection方面的节省效果是否显着?Collection

如果我知道的最小尺寸Collection但不知道上限怎么办。至少以最小的尺寸创建它仍然值得吗?

0 投票
3 回答
8572 浏览

java - 在 Eclipse 中从 main 运行 JMH 时出现“没有匹配的基准”

我想通过在 Eclipse 中将其作为 Java 应用程序运行来尝试 JMH 的新功能。我导入并构建了 jmh-samples 项目。编译的类以 /jmh-samples/target/generated-sources/annotations 结尾,/target/ 中有几个 JAR,从命令行运行 microbenchmarks.jar 照常工作。

但是,当我执行 main 我总是得到

有任何想法吗?我使用的是 0.3 版

0 投票
1 回答
663 浏览

java - 分支预测不起作用吗?

关于这个问题,答案指定未排序的数组需要更多时间,因为它未能通过分支预测测试。但是如果我们在程序中做一个小的改动:

在这里我已经替换(来自原始问题)

未排序的数组给出了大约。同样的结果,我想问为什么分支预测在这种情况下不起作用?

0 投票
1 回答
345 浏览

java - 什么可以解释编写对堆位置的引用的巨大性能损失?

在研究分代垃圾收集器对应用程序性能的微妙影响时,我发现在一个非常基本的操作(简单写入堆位置)的性能方面存在相当惊人的差异,即写入的值是原始值还是引用。

微基准

结果

由于整个循环几乎慢了 8 倍,因此写入本身可能慢了 10 倍以上。什么可以解释这种放缓?

写出原始数组的速度超过每纳秒 10 次写入。也许我应该问我问题的另一面:是什么让原始写作如此之快?(顺便说一句,我已经检查过了,时间与数组大小成线性关系。)

请注意,这都是单线程的;指定@Threads(2)将增加两个测量值,但比率将相似。


一点背景知识:卡表和相关的写屏障

年轻代中的对象可能碰巧只能从老年代中的对象访问。为了避免收集活动对象,YG 收集器必须知道自上次 YG 收集以来写入老年代区域的任何引用。这是通过一种称为卡表的“脏标志表”来实现的,它为每个 512 字节的堆块有一个标志。

当我们意识到每次写入引用都必须伴随着卡表不变的代码时,该方案的“丑陋”部分就出现了:必须标记卡表中保护被写入地址的位置一样。这段代码被称为写屏障

在特定的机器代码中,如下所示:

当写入的值是原始值时,这就是相同的高级操作所需要的全部内容:

写入屏障似乎“仅”贡献了一次写入,但我的测量表明它会导致数量级的减速。我无法解释这一点。

UseCondCardMark只会让事情变得更糟

如果条目已被标记为脏,则有一个非常模糊的 JVM 标志应该避免卡表写入。这主要在一些退化的情况下很重要,因为大量的卡表写入导致线程之间通过 CPU 缓存进行错误共享。无论如何,我尝试使用该标志:

0 投票
1 回答
858 浏览

java - 当 JMH 什么都不做时,它会做什么?

这是我的第一个 JMH 基准测试。我可能做错了一切,但是......

我的基准看起来像这样

我开始了它……然后等了又等,然后杀死了它。我怀疑 中的问题@Setup,所以我简化了它,但没有任何改变。跑步开始时非常乐观......

然后什么也没有发生。很长一段时间后,它继续写20行,如

和5行像

然后它输出一些结果

并更正其估计的 eta:

我是否@Setup比我更频繁地被调用,或者还有什么可能是导致缓慢的原因?

0 投票
1 回答
362 浏览

java - 循环习语的奇怪 JIT 悲观化

在分析这里最近一个问题的结果时,我遇到了一个非常奇怪的现象:显然,HotSpot 的 JIT 优化的额外层实际上会减慢我机器上的执行速度。

这是我用于测量的代码:

代码非常微妙,所以让我指出重要的部分:

  • “正常索引”变体使用直接变量i作为数组索引。HotSpot 可以轻松确定i整个循环的范围并消除数组边界检查;
  • “屏蔽索引”变体索引为j,实际上等于i,但这一事实通过 AND 屏蔽操作从 HotSpot 中“隐藏”了;
  • “带出口点”变体引入了显式循环出口点。下面将解释这一点的重要性。

循环展开和重新排序

观察边界以两种重要方式检查数字:

  • 它具有与之相关的运行时开销(比较后跟条件分支);
  • 它构成了一个循环退出点,可以在任何步骤中中断循环。事实证明,这对适用的 JIT 优化产生了重要影响。

通过检查上述四种方法发出的机器代码,我注意到以下几点:

  • 在所有情况下,循环都是展开的;
  • 在 的情况下normalIndex,它被区分为唯一没有过早循环退出点的情况,所有展开步骤的操作都被重新排序,以便首先执行所有数组获取,然后将所有值异或到累加器中。

预期和实际测量结果

现在我们可以根据讨论的特征对这四种方法进行分类:

  • normalIndex没有边界检查,也没有循环退出点;
  • normalWithExitPoint没有边界检查和 1 个退出点;
  • maskedIndex有 1 个边界检查和 1 个出口点;
  • maskedWithExitPoint有 1 个边界检查和 2 个出口点。

显而易见的期望是,上面的列表应该按性能降序排列方法;但是,这些是我的实际结果:

  • normalWithExitPointmaskedIndex是相同的模测量误差,即使只有后者有边界检查;
  • 观察到的最大异常normalIndex应该是最快的,但明显慢于,除了多了一行代码,即引入退出点的代码之外normalWithExitPoint,在各方面都与它相同。

由于normalIndex是唯一对其应用了额外的重新排序“优化”的方法,因此得出的结论是,这是导致速度下降的原因。

我正在测试:

  • Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)(Java 7 更新 40)
  • OS X 版本 10.9.1
  • 2.66 GHz 英特尔酷睿 i7

我也成功地在 Java 8 EA b118 上重现了结果。

我的问题:

上述现象是否可以在其他类似机器上重现?从一开始提到的问题中,我已经暗示至少有些机器不会重现它,所以同一个 CPU 的另一个结果会很有趣。

更新 1:更多测量灵感来自maaartinus的发现

我收集了下表,它将执行时间与-XX:LoopUnrollLimit命令行参数相关联。在这里,我只关注两个变体,有和没有if (entry == 0) break;线:

可以观察到以下突然变化:

  • 在从 14 到 15 的过渡中,该withoutExitPoint变体接受了有益的 LCM 1转换并显着加快了速度。由于循环展开限制,所有加载的值都适合寄存器;

  • 在 18->19 上,withExitPoint变体获得了加速,小于上述值;

  • 在 22->23 上,withoutExitPoint变体变慢了。在这一点上,我看到溢出到堆栈位置,如maaartinus的回答中所述,开始发生。

我的设置的默认loopUnrollLimit值为 60,因此我在最后一列中展示了它的结果。


1 LCM = 本地代码运动。正是这种转换导致所有数组访问都发生在顶部,然后处理加载的值。

更新 2:这实际上是一个已知的,报告的问题

https://bugs.openjdk.java.net/browse/JDK-7101232



normalIndex附录:机器码中的展开和重新排序的循环