4

我正在对 MPEG 解码器进行一些优化。为了确保我的优化不会破坏任何东西,我有一个测试套件可以对整个代码库(优化的和原始的)进行基准测试,并验证它们是否产生相同的结果(基本上只是通过解码器和 crc32 提供几个不同的流输出)。

在 Sun 1.6.0_18 中使用“-server”选项时,测试套件在预热后的优化版本上运行大约慢 12%(与默认的“-client”设置相比),而原始代码库获得了良好的提升运行速度大约是客户端模式下的两倍。

虽然起初这对我来说似乎只是一个热身问题,但我添加了一个循环来多次重复整个测试套件。然后从测试的第 3 次迭代开始,每次通过的执行时间变得恒定,但优化后的版本仍然比客户端模式慢 12%。

我也很确定这不是垃圾收集问题,因为代码在启动后绝对不涉及对象分配。该代码主要由一些位操作操作(流解码)和许多基本的浮动数学(生成 PCM 音频)组成。唯一涉及的 JDK 类是 ByteArrayInputStream(将流提供给测试并从测试中排除磁盘 IO)和 CRC32(用于验证结果)。我还观察到 Sun JDK 1.7.0_b98 的相同行为(只有 15% 而不是那里的 12%)。哦,测试都是在同一台机器(单核)上完成的,没有运行其他应用程序(WinXP)。虽然测量的执行时间存在一些不可避免的变化(使用 System.nanoTime btw),但使用相同设置的不同测试运行之间的变化从未超过 2%,通常小于 1%(预热后),

是否有任何已知的编码模式在服务器 JIT 上表现更差?如果做不到这一点,哪些选项可用于“窥视”引擎盖并观察 JIT 在那里做什么?

  • 也许我用错了我的“热身”描述。没有明确的预热代码。整个测试套件(由 12 个不同的 mpeg 流组成,总共包含约 180K 音频帧)执行 10 次,我将前 3 次运行视为“热身”。在我的机器上,一轮测试大约需要 40 秒 100% cpu。

  • 我按照建议使用 JVM 选项并使用“-Xms512m -Xmx512m -Xss128k -server -XX:CompileThreshold=1 -XX:+PrintCompilation -XX:+AggressiveOpts -XX:+PrintGC”我可以验证所有编译都发生在前3轮。垃圾收集每 3-4 轮启动一次,最多耗时 40ms(512m 非常大,因为测试可以用 16m 运行就好了)。由此我得出结论,垃圾收集在这里没有影响。尽管如此,将客户端与服务器进行比较(其他选项不变)仍然存在 12/15% 的差异。

4

1 回答 1

6

正如您所看到的,JIT 可以扭曲测试结果,因为它在后台线程中运行,从运行测试的主线程中窃取 cpu 周期。

除了偷循环,它也是异步的,所以当你完成热身并开始真正的测试时,你不能确定它已经完成了它的工作。要强制同步 JIT 编译,您可以使用-XBatch非标准选项将 JIT 编译强制到前台线程,这样您就可以确保在预热完成时 JIT 已经完成。

HotSpot 不会立即编译方法,而是等待方法执行一定次数。在 -XX 选项的页面上,它声明 -server 的默认值为 10000 次,而 -client 的默认值为 1500 次。这可能是导致速度变慢的原因,特别是如果您的热身最终调用了 1500 到 10000 次之间的许多关键方法:使用-client选项它们将在热身阶段被 JITed,但是使用 -server 运行,编译可能会延迟执行您的异形代码。

您可以通过设置来更改 HotSpot 编译方法之前所需的方法调用次数-XX:CompileThreshold。我选择了 20,以便在热身过程中转换模糊的热点(不冷不热的点?),即使只运行了几次测试。这在过去对我有用,但 YMMV 和不同的值可能会给你更好的结果。

您还可以查看 HotSpot VM 选项页面以查找 -client 和 -server 选项之间不同的其他选项,特别是垃圾收集器选项,因为它们有很大不同。

于 2010-05-27T19:44:14.123 回答