在对 Java 应用程序进行基准测试时,如何补偿没有"quiet"计算机?



我一直在运行数值模拟。 我可以判断我的模拟是否不起作用(即,它们无法给出可接受的答案),但是因为我通常在后台运行的指定内核上运行可变数量的这些(当我工作时),查看时钟时间告诉我它们运行的速度。

我不想要时钟时间;我想要 CPU 时间。 似乎没有一篇文章提到这个小方面。 特别是,使用"安静"机器的建议似乎模糊了正在测量的内容。

我不需要太多的细节,我只想知道模拟 A 的运行速度比模拟 B 或 C 快或慢 15%,尽管事实上 A 自己运行了一段时间,然后我开始了 B,然后是 C。 也许我在退役前玩了一段时间,这将在部分时间内运行更高优先级的应用程序。 不要告诉我理想情况下我应该使用"安静"的机器;我的问题特别问如何在没有专用机器的情况下进行基准测试。 我也不想在衡量应用程序运行需要多长时间时扼杀应用程序的效率;似乎只有在需要大量细节时才需要大量开销。 我说的对吗?

我想修改我的应用程序,以便在检查批处理作业是否成功时,我还可以看到在 CPU 时间内达到这些结果所花费的时间。 基准测试能给我想要的答案吗? 我可以简单地使用Java 9的基准测试工具,还是需要其他东西?

在大多数操作系统上,您可以轻松地从 JVM 外部测量 CPU 时间而不是挂钟时间。 例如time java foo.jar在Unix/Linux上,甚至在Linux上perf stat java foo.jar

这样做的最大问题是某些工作负载比其他工作负载具有更多的并行性。 考虑这个简单的例子。 这是不现实的,但对于在更并行和不太并行阶段之间交替的实际程序,数学工作原理相同。

  • 版本 A 纯串行 9 分钟,并保持 8 个内核饱和 1 分钟。 挂钟时间 = 10 分钟,CPU 时间 = 17 分钟

  • 版本 B 串行 1 分钟,并使所有 8 个内核忙 5 分钟。 挂机时间 = 6 分钟,CPU 时间 = 5*8 + 1 = 41 分钟

如果您只查看 CPU 时间,您将不知道哪个版本卡在其工作的固有串行部分。 (这是假设纯粹受 CPU 限制,没有 I/O 等待。

但是,对于两个大多是串行的类似实现,CPU 时间和挂机时间可以给你一个合理的猜测。

但是像 HotSpot 这样的现代 JVM 使用多线程垃圾收集,所以即使你自己的代码永远不会启动多个线程,一个让 GC 做更多工作的版本可以使用更多的 CPU 时间,但仍然更快。 不过,这可能很少见。


另一个混淆因素:内存带宽和缓存占用空间的争用意味着执行相同的工作需要更多的 CPU 时间,因为您的代码将花费更多时间等待内存。

对于超线程或其他 SMT CPU 架构(如锐龙),一个物理内核可以充当多个逻辑内核,让两个逻辑内核都处于活动状态会增加总吞吐量,但代价是每线程性能较低。

因此,在HT 同级节点空闲的内核上 1 分钟的 CPU 时间可以完成比另一个逻辑内核也处于活动状态时更多的工作。

在两个逻辑内核都处于活动状态的情况下,现代 Skylake 或 Ryzen 可能会为您提供 50% 到 99% 的单线程性能,即为单个内核提供所有可用的执行资源,这完全取决于代码在每个线程上运行的内容。 (如果 FP 延迟的两个瓶颈都与非常长的循环携带依赖链相加和乘以,而无序执行看不到过去,例如,两者都按照严格 FP 的顺序对非常大的数组求和,这是 HT 的最佳情况。 两个线程都不会减慢另一个线程的速度,因为 FP 添加吞吐量是 3 到 8 倍的 FP 添加延迟。

但在最坏的情况下,如果两个任务都因 L1d 缓存未命中而减慢了很多速度,HT 甚至会因为在同一内核上同时运行两个任务而失去吞吐量,而不是先运行一个然后运行另一个。