我正在对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非常超大,因为测试可以用16米就好了)。由此我得出结论,垃圾收集在这里没有影响。不过,比较客户端与服务器(其他选项未改变)仍有12/15%的差异。
答案 0 :(得分:6)
正如您所见,JIT可能会扭曲测试结果,因为它在后台线程中运行,从运行测试的主线程中窃取cpu周期。
除了偷窃周期之外,它也是asynchornos,所以当你完成预热并开始真正的测试时,你无法确定它是否完成了它的工作。要强制进行同步JIT编译,可以使用-XBatch
非标准选项强制JIT编译到前台线程,这样就可以确保在预热完成时JIT已完成。
HotSpot不会立即编译方法,而是等待方法执行了一定次数。在-{X}选项的page上,它指出-server的默认值为10000次,而-client的默认值为1500次。这可能是一个原因
减速,特别是如果你的热身最终调用1500到10000次之间的许多关键方法:使用-client
选项它们将在预热阶段被JITed,但是使用-server运行,编译可能会延迟执行你的profiled代码。
您可以通过设置-XX:CompileThreshold
来更改HotSpot编译方法之前所需的方法调用次数。我选择二十,所以即使在测试运行几次时,即使是模糊的热点(温暖的斑点?)也会在预热期间被转换。这在过去对我有用,但YMMV和不同的值可能会给你更好的结果。
您还可以查看HotSpot VM Options页面,找到-client和-server选项之间不同的其他选项,尤其是垃圾收集器选项,因为它们差异很大。
见