JIT(即时编译)是个好东西,Java引领着整个行业。与HotSpot C2不同,JVM发行版仅针对操作系统和指令集架构优化,而C2会为特定操作系统(mac)、指令集(x86_64)和架构(Intel Broadwell)编译和优化class文件。这种方案具备可移植性,同时减少了提交给供应商的build数量。


与Java程序一起运行的本地代码非常重要,像C1编译器和垃圾回收器。那么问题来了,如果自己编译OpenJDK,能否显著提高提高应用程序吞吐量?答案似乎是肯定的。


构建JDK


这里用AdoptOpenJDKMac OpenJDK 13与自己定制的OpenJDK 13进行比较,两者采用相同的33版本。以下是构建说明:


  • 克隆OpenJDK git镜像

  • 检出发行版33

  • 根据文档运行bash configure。


bash configure --with-jvm-variants=server --with-jvm-features=link-time-opt \
--with-extra-cflags='-Ofast -march=native -mtune=broadwell -funroll-loops -fomit-frame-pointer' \
--with-extra-cxxflags='-Ofast -march=native -mtune=broadwell -funroll-loops -fomit-frame-pointer'


使用标准的server变量进行配置,其中包括jvm g1gc和c2功能。此外,还把link-time-opt加到构建中。关于这个功能没有找到相关文档,大致推测用来优化链接时间提高性能。现在考虑C和C++编译优化。


-Ofast是clang和gcc支持的最高级别优化,使用时会有一些问题。二进制文件可能增大,而且浮点数语义检查不那么严格。用-O3能保持严格的浮点数一致性,或者用-O2保持二进制大小不变。我用Ofast和O3进行了测试,没有遇到类似的问题,但你的情况可能不同。


-march=native和-mtune=broadwell通知编译器针体系结构进行优化。有人根据编译器文档认为march代表了mtune,但是显然并非如此。


-funroll-loops确保会执行循环展开。由于指定了march参数,循环展开效果应该非常明显。Clang -O3选项包含循环展开,但必须为gcc手动设置。


-fomit-frame-pointer选项允许编译器在可能的情况下忽略frame指针,进而释放寄存器。但是这可能会给调试JVM本机代码带来麻烦。


  • 执行下面命令制作jdk:


make images CONF=macosx-x86_64-server-release


尽管开启了优化,在2015年的Macbook上完成构建也只需要约15分钟。


  • 可以在build/macosx-x86_64-server-release/images/jdk-bundled下找到jdk,加入PATH就可以开始测试了。


基准测试


DaCapo是运行的第一个测试集。



优化后的JDK在每种情况下都表现出色,使用-Ofast后,avrora和fop测试结果获得了极大的速度提升。很好奇为什么会有这样的结果!


接下来运行了计算机语言基准测试游戏中的一些测试。


自己动手为 OpenJDK 加速_java


执行time for i in {1..10}; do java <class>; done,然后除以10。这一次,JDK的表示没有明显的差异。与DaCapo不同,这些基准不能代表正常的工作量,参考价值较低。例如,这些基准测试不会产生垃圾或者使用JVM功能(C2除外),但是我们知道优化并不会给C2和启动带来好处。


最后,对Netty的HttpObjectEncoder进行了若干JMH微基准测试:


自己动手为 OpenJDK 加速_java_02


在查看了Netty测试集后,我随机选择了这个微基准测试。当内存分配没有用到pool且不返回void Promise时,加速效果显著,其他情况下速度也有很大提升。当然,这些方法不太可能主导应用程序的性能。第二个基准测试似乎出现了小故障。


注意:Netty也支持Native Transport,如果可以最好也对它进行编译!


其他编译器和与作系统


我还尝试了用Intel的编译器构建,并且完善了configure脚本提供支持。但是,这次构建神秘地失败了。当然,我也想看看在Linux上的结果。


总结


尽管最后还是需要用自己的应用程序进行测试,但很明显,为JDK指定体系结构可以显着提高应用吞吐量。既然构建JDK这么方便(Skara项目真的很棒),性能关键型Java应用程序的开发者应该认真考虑构建一个优化的JDK,就像C/C++开发者优化二进制文件那样。


测试结果


自己动手为 OpenJDK 加速_java_03


自己动手为 OpenJDK 加速_java_04


自己动手为 OpenJDK 加速_java_05


结语


我不确定这种方法是否有问题,请不吝赐教!