Java JIT编译器如何优化我的代码?

17

我正在编写需要高度优化速度的底层代码,每个CPU周期都很重要。由于代码是用Java编写的,相比如C语言,我无法编写如此底层的代码,但我想尽可能地发挥VM的所有功能。

我正在处理一个字节数组。目前我主要关注代码中的两个部分。第一个部分是:

int key =  (data[i]     & 0xff)
        | ((data[i + 1] & 0xff) <<  8)
        | ((data[i + 2] & 0xff) << 16)
        | ((data[i + 3] & 0xff) << 24);

第二个是:

key = (key << 15) | (key >>> 17);

从性能表现来看,我猜测这些语句没有按照我预期的方式进行优化。第二个语句基本上是一个ROTL 15, key。第一个语句将4个字节加载到一个int中。这里的0xff掩码只是为了补偿访问的字节如果是负数时造成的隐式强制类型转换到int后添加的符号位。这应该很容易转换为高效的机器代码,但令我惊讶的是如果我去掉掩码,性能反而提高了。 (当然这会破坏我的代码,但是我很想知道会发生什么。)

这里发生了什么?最常见的Java虚拟机是否会在JIT期间优化此代码,就像一个好的C++编译器会优化等价的C++代码一样?我能影响这个过程吗?设置-XX:+AggressiveOpts似乎没有任何区别。

(CPU:x64,平台:Linux / HotSpot)


6
我不明白如何才会到达需要担心性能的地步,你也知道你的性能已经不够好,而且你已经尝试过广泛的算法方案并确定需要微调优化。此外,你必须在一个特定的语言上进行优化,而这个语言又不是超低级别的语言……然而你不知道如何自己确定语言在幕后做了什么,并且还没有对该主题进行足够的个人研究。这种优化是一种相当专业的技能。 - Karl Knechtel
10
哇,我不知道我的问题会冒犯到任何人。您似乎没有默认善意。我正在努力解决这个问题,但也许我的努力在您看来微不足道。无论如何,我很乐意向您学习。回答您的问题:语言是由产品所有者确定的。性能对于该产品的性质至关重要。我不想使用JNI,以免破坏平台的独立性。我所说的代码深入核心,CPU大部分时间都在执行它。如果你问我的话,这就足以证明我的问题有价值。但是即使不是这样的情况,这本身难道不是很有趣吗? - Rinke
1
哦,而且在同一台机器上,我用C++的速度是数据吞吐量的15倍(相对于其他语言)。这至少表明性能还不够好,不是吗? - Rinke
2
如果你在C++中获得了15倍的加速,那么极有可能是你测量错误了,所以请发布你的微基准测试结果。@jmg Hotspot -c2生成的代码与C编译器大致相等,但据我上次检查,它并没有做太多SSE优化——这取决于手头的更大的代码,对于C++程序来说,这肯定会带来明显的加速效果。再次强调,没有更多的代码就无法发表更多意见。 - Voo
1
我假设你是出于善意的,但不一定是你的雇主或客户:) 我并不感到被冒犯,只是觉得这种情况有些令人困惑。这种优化很难;即使只是获得有意义的测量数据也很难。这些任务应该分配给那些已经至少知道如何进行研究,并且最好已经对JVM的内部工作有很多了解的人(或者对相关情况具有等效的知识)。话虽如此,我会尽力提供一些建议:您是否尝试确保使用“服务器”VM? - Karl Knechtel
显示剩余4条评论
4个回答

7

非常感谢。事实上,我的测量结果证明是有缺陷的。我已经修复了这个问题,但是我仍然没有C++版本快。 - Rinke

5
我在Java中进行了很多性能代码的编写,甚至直接编写字节码,足以确认一些事情:JIT是一个具有不明确行为的黑匣子,JIT和编译器非常高效,而且最简单的代码通常会产生最佳性能。
当您考虑JIT的目标时,这是正常的:从任何Java代码中提取最佳性能。当您添加Java是一种相当简单和普通的语言时,简单的代码将被优化,进一步的技巧通常没有好处。
当然,有一些常见的陷阱和需要注意的地方,但我在您的代码示例中看不到。如果要优化您的代码,我会直接转向更高的级别:算法。您的代码复杂度是什么?是否可以缓存某些数据?使用了哪些API?等等……仅仅从算法技巧中就能够产生无穷无尽的性能提升。
如果即使如此还不够,如果该语言不够快,如果您的机器不够快,如果您的算法无法再加速,那么答案不在于"时钟周期",因为您可以挤出20%的效率,但是当您的数据增长时,20%永远不够。为了确保您永远不会再次遇到性能瓶颈,最终的答案在于可扩展性:使您的算法和数据可以无限分配,这样您就可以向任务中添加更多的工作人员。

谢谢。非常抱歉忽略了你回答中更有趣的部分(即在此评论中),但我可以得出结论,根据你的经验,你会说(1)第一个语句中的掩码不应该在编译后的机器代码中显式存在,(2)第二个语句应该被转换为单个ROTL(或ROTR)? - Rinke

3

我同意solendil的观点,但如果您想深入了解低级别的内容,请尝试获取由JIT生成的代码,如此处所述here


那个链接已经失效了,这里是新链接 - skuro

0

在向左移动24位之前,您不需要进行(& 0xff)操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接