微信邦 发表于 2021-7-7 13:02:15

Open Source Talk 02|首个中国团队自己培养的OpenJDK Reviewer背后的那些事

About「Open Source Talk」栏目开源无边界,分享有价值。Code is not cold,「Open Source Talk」栏目,将陆续邀请众多开源嘉宾做客,和大家一起分享和交流开源道路中的成长心得。以知识和分享为起点,传承开源的星星火光。
本期引言
本篇文章,傅杰博士将回顾自己十年来的OpenJDK开源经历,讲述自己一路上遇到的坎坷、成长和心得。


1开源:协同共建,互利共赢
      十年前,我从西安交通大学保送至中科院计算所硕博连读,加入计算所龙芯CPU团队,主力承担龙芯上高性能Java虚拟机C2编译器的研发,从此与OpenJDK结缘。加入腾讯后,主要从事Kona JDK在大数据和机器学习等领域的探索和实践。
      2018 年腾讯930 变革后,成立了开源协同项目组和对外开源管理办公室,正式将开源提升到公司战略层面。我所在的Tencent Kona JDK Oteam(含多位OpenJDK社区Reviewer/Committer/Author)正是基于开源协同的模式组建和运营的。开源是一种互利共赢的生态建设。将代码贡献到OpenJDK开源社区,不仅使得全球用户共享研发成果,也让贡献者获得成就感。此外,通过开源互动,还可以不断提升团队的认知水平和研发能力,逐步扩大团队影响力。团队leader经常告诫我们不要做开源的食利者,要积极贡献开源,做社区的良好公民。正是在这种开源文化和理念的指引下,Kona积极参与OpenJDK社区,贡献有价值的代码,获得了开源社区的点赞和致谢。2腾讯Kona开源贡献概览      腾讯Kona是JDK15/16官宣的全球Notable贡献者,并连续两次蝉联国内贡献度第一。截止到今年5月,Kona向OpenJDK社区最重要的JDK主干项目累计贡献了150多个Patch,涉及HotSpot虚拟机内核(Compiler、GC和Runtime)、SVC、Core Libraries和Infrastructure等领域。其中,比较突出的包括:Vector API、C2编译器、ZGC、jmap大堆Heap Dump加速等。下面以Kona在OpenJDK社区系统性参与开源贡献的首个Java前沿新特性,Vector API为例,介绍Kona的开源输出。
       Vector API本质上是一组通用Java向量加速的编程接口,对典型数据科学场景具有显著的加速效果。例如,根据官方公布的数据,Vector API对矩阵乘法的加速为2~5倍;对于点积运算的加速高达14~16倍。Kona在生产实践中首次适配并完善了Vector API对AVX512高性能向量指令集的支持,解决了多个由编译器Bug导致的正确性问题,修复了由于memory barrier缺失导致的稳定性问题。上述开源贡献解决了Vector API长期遗留的正确性和稳定性痛点,使得Vector API在广告训练等实际业务系统中落地,为产业界的规模化应用蹚平了道路。值得注意的是,Kona对Vector API架构改进和性能优化也有贡献。



在架构设计方面,Kona修复了Vector API对C2编译器强耦合的顶层设计缺陷。如上图所示,虚拟机执行引擎包括解释器、C1编译器和C2编译器三种。修复前,Vector API无法脱离C2编译器执行,与C2在事实上构成强耦合关系。但是,Vector API作为Java语言层面的API,不应依赖于特定执行引擎的实现,理论上应该被所有执行引擎支持。发现上述缺陷后,我们从顶层Java编程接口出发,自顶向下分析了Vector API的架构设计和执行流程,一直深入到虚拟机执行引擎的实现细节,成功定位到强耦合的原因是向量长度选择逻辑仅在C2中有实现。于是,我们为另外两款执行引擎(Interpreter和C1)补充实现了向量长度选择模块,并且与社区专家进行了多个回合的研讨,最终修复了该缺陷。此后,Vector API和其它Java API一样,可以跑在任意一种执行引擎上。

在性能优化方面,Kona是JDK17中SVML(Short Vector Math Library)向量加速新特性三大贡献者之一(另外两位是Intel和Oracle)。SVML是一款高性能向量优化数学库,用于进一步加速Vector API的向量数学运算。受C2编译器历史遗留的至少两个double才能向量化的限制,官方最初的SVML无法对单个double数据进行加速。但实际业务中会经常使用单个double的数学运算,优化的需求也很大。经过反复分析和实验,我们打破了C2编译器强制要求至少两个double的不合理限制,巧妙地将单个double数据转换为64位向量进行SVML加速。优化后性能成倍提升,部分2~4倍,个别高达8~9倍,如上图所示。

3OpenJDK社区经历与感受
      今年5月,我有幸获得了由社区专家发起的OpenJDK Reviewer的提名(如下图所示),并于两周之后高票通过。在此特别感谢团队的培养和帮助,同时也感谢OpenJDK社区对Kona的认可。


       虽然我十年前就开始接触OpenJDK,但真正参与社区却是从2018年12月才开始的。2019年11月,在贡献了30多个Patch后,我有幸被社区专家提名为OpenJDK Committer。之后又经历了约19个月收获颇丰的社区历练,我累计贡献了140多个Patch,成功晋级OpenJDK Reviewer。十年回首,自己经历了很多坎坷,也走过了不少弯路。在此分享一些个人的经验和教训,希望对大家能有所帮助。
       首先,社区参与要趁早。作为一名开发者,自己最后悔的事情就是没有及早参与开源社区。加入社区之前,视野狭小闭塞,能力和经验积累主要靠从实践中自我摸索,成长缓慢。加入社区后,每天接触到的都是领域前沿,与社区顶级专家的交流讨论也成为常态,进步显著加快。对于刚加入开源社区的新手而言,最重要的事情是学习社区规则。下图展示了OpenJDK社区重要的规则:
   
       OpenJDK社区将开发者从初级到高级划分为Contributor、Author、Committer和Reviewer四种角色。其中,只要签署了OCA(Oracle Contributor Agreement)协议并贡献了1个Patch之后,就自动成为OpenJDK的Contributor。当贡献的Patch数目达到2个以后,就可以向社区申请成为Author。
       成为Author后,开发者便拥有了JDK的JBS账号,以后贡献会更加方便。但Author还没有代码的Push权限。当贡献了至少8个重要的Patch后,经社区提名并投票通过后,可以晋升为Committer。注意,不是任意8个Patch,而是8个硬核(英文表述为“Significant”)贡献才有资格被提名Committer。
       除了最基本的数量要求之外,社区对Committer和Reviewer的提名更关注Patch本身的价值、难度和质量,这也是为何大多数OpenJDK开发者长期停留在Author级别难以晋升的原因。晋升为Committer后,就获得了向OpenJDK代码库Push代码的权限。
       Reviewer的提名则要求至少有32个硬核的贡献,难度比Committer进一步加大。在代码评审方面,OpenJDK明确要求虚拟机内核HotSpot的代码至少两人Review通过之后才能入库,并且在入库前强制等待24小时,以保证全球各个时区的开发者都有机会对代码修改进行评审。
       其次,不惧权威,大胆质疑。我在做前面提及的SVML优化时,过程并非一帆风顺。当我的优化代码编写完毕后,却发现POW算子的性能反而下降了约27%。起初,我怀疑是自己的实现有问题。我花了一天时间,对该场景涉及的调用链路和汇编代码进行了分析和排查,但并没有发现问题。第二天,确认自己的优化代码实现没有问题后,我及时将方向转换到了SVML库本身。可能有人觉得,SVML库是由业界权威Intel提供的,不可能有问题。但实际上,后来的排查结果证实就是SVML本身的Bug导致的性能下降。我在多种机器上全面评估了SVML中POW算子的性能,发现在非AVX512机型上性能下降,唯有AVX512机型性能才提升。于是,我纠正了官方SVML的优化逻辑,当且仅当底层硬件支持AVX512指令时才对POW算子启用优化,从而规避了SVML的性能下降问题。上述优化策略的改进在OpenJDK社区很快得到了Intel专家的认可,并且迅速合并到了OpenJDK社区。如果盲目相信权威,我所遇到的性能下降问题可能永远无解。
       再次,注意理论与工程的差异。从理论上分析理所应当的事情,在实际工程实践中未必如此。在此,跟大家分享自己优化Math.pow(x, 0.5)的真实故事。从数学角度分析,将Math.pow(x, 0.5)替换成Math.sqrt(x)是等价的。但是,在编译器的工程实现中,上述直接替换却是错误的。这是因为根据IEEE754的规范,当x的值为-0.0和负无穷大时,计算机计算得到的结果并不相等!IEEE754浮点运算规范规定如下:Math.pow(-0.0, 0.5) = 0.0
Math.sqrt(-0.0) = -0.0Math.pow(Double.NEGATIVE_INFINITY, 0.5) = Infinity
Math.sqrt(Double.NEGATIVE_INFINITY) = NaN注意:在编译器实现时,0.0和-0.0也是两个不同的浮点结果。因此,在编译器优化实践中,绝对不允许将Math.pow(x, 0.5)直接替换为Math.sqrt(x)。随后,我实现了一种已被JDK17接受的优化方案:仅对x > 0时才进行替换。故大家在工程实践中务必注意与理论的差异。
       最后,尊重知识产权。尊重他人的知识产权,切勿将别人的开源代码,不加任何引用说明地据为己有。OpenJDK社区对代码知识产权的尊重令人惊讶。例如,我曾经修复了一个由处理器弱一致性访存模型触发的GC稳定性问题(Bug ID:JDK-8229169),该Bug影响包括x86在内的所有处理器,尤其影响ARM等弱一致性访存处理器。我最初只在JDK14上进行了修复。后来由于其重要性,在我不知情的前提下,该Patch被社区GC专家反向移植到了JDK8和JDK11等业界广泛使用的版本。但是,令我惊讶的是,在JDK8等所有被反向移植的源代码库中,对应代码的作者竟然保留了我的名字,如下图中红色方框所示。在整个移植过程中,我没有出一份力,却依然保留我为代码的作者,连commit信息都一模一样。当时我就震惊了,从此也更加懂得了尊重他人的知识产权。

4晋升OpenJDK Committer/Reviewer的秘诀

      如何才能获得开源社区认可,并成长为Committer、Reviewer呢?
      持续贡献有价值的代码是关键。凡是实际业务场景需要的改进都是有价值的。自己对OpenJDK的贡献绝大部分来源于广告训练、大数据和腾讯云等实际业务系统。在此,我谨向相关业务开发和运维同学表示衷心感谢。从实际业务需求出发,持续贡献有价值的代码,是快速获得开源社区认可的唯一秘诀。
       兴趣和坚守缺一不可。OpenJDK是复杂度极高的底层基础软件,学习门槛高,开发调试难度大。因此,初学者在Bug修复和性能优化时往往面临巨大的困难和挑战。如果没有强烈的兴趣爱好作为支撑,就容易被困难险阻吓跑,也就无法通过长期历练成为社区的Committer/Reviewer。曾有同行问我是否因为KPI才做开源。我回答不是。Kona从来都没有把社区贡献设为KPI,兴趣爱好是我们最大的驱动力。 我们心中的“执念”是通过腾讯JDK前沿实践的贡献,解决Java在生产中的长期短板和痛点,让Java长久成功。此外,开发者需要认准一个方向长期坚守下去,由浅入深,逐步构建自己的品牌与核心竞争力。从这个角度讲,开发者需要有坐几年冷板凳的心理准备。
       以兴趣爱好为驱动,持续贡献有价值的代码,长期坚守并深耕一个方向,那么开发者成长为Committer/Reviewer就指日可待了!
       最后,欢迎大家加入到Tencent KonaJDK开源项目中来,和傅杰导师一起探索开源的广阔世界!
页: [1]
查看完整版本: Open Source Talk 02|首个中国团队自己培养的OpenJDK Reviewer背后的那些事