关于华为方舟编译器,你想知道的都在这里!

关于华为方舟编译器,你想知道的都在这里!

2017年5月的一个凌晨,华为某实验室,方舟编译器上第一个Java程序“HelloWorld”跑通了!

当时项目组的一位工程师哽咽道:

“I never thought printing 'Hello, World' would be this hard .”(我从来没想到,打出"Hello,World"会如此艰难。)

十年前,华为启动编译组,到这一刻,初见曙光。

2019年4月,华为方舟编译器正式面世!

今天,我们聚焦华为「方舟编译器」,这个一发布就引起巨大响动的底层核心技术。

一、为什么需要方舟编译器

安卓系统从2008年1.0到9.0,十来年间安卓系统大小版本超过15个。2016年开始,不断融入了华为等公司发起的文件系统、人工智能学习、智能硬件调度和内存管理等底层创新技术,安卓手机的体验已经今非昔比。但是,依然有四大方面难题,影响着安卓的体验。解决这四大难题,也是华为方舟编译器的使命。

1. Java的虚拟机

Java为了能够实现跨平台操作,便借助虚拟机来调度硬件平台资源。在虚拟机里,还需要集成翻译器或者编译器,来将Java的字节码(即中间代码)解释成机器听得懂的机器语言,或者直接编译成机器直接执行的010101的机器码。

2008年,Android 1.0刚发布的时候,使用的是一个叫Dalvik的虚拟机,里面集成了一个解释器,每次用户在安卓手机上运行APP时,就会叫醒这个解释器,来给安卓的硬件解释APP想要干嘛。这就相当于新闻发布会,发言人讲一句自己的母语,然后再由专业翻译将其翻译成外国记者听得懂的语言,效率非常低下,一个小时可能也问不了几个问题。

这个问题严重拖了安卓手机的后腿,所以一年多后的2010年,安卓2.2版本引入了JIT(Just in Time,即时编译)机制。JIT比较聪明,当用户在安卓手机运行APP时,会同时将用户经常使用的功能编译为机器能直接执行的010101机器码,不用一句一句地去翻译。当出现不常用的功能时,再把解释器叫起来翻译。

JIT虽然变聪明了一点,但是每次启动APP都要重新编译一次,不能一劳永逸。加上Dalvik虚拟机性能比较落后,所以2014年10月的Android 5.0版本将虚拟机从Dalvik替代成ART(Android Run Time),同时把JIT的编译器替代成AOT (Ahead of Time)。意思就是说,APP在下载后安装到手机上时同时把能编译的代码先编译成机器听得懂的010101。剩下不太好翻译的代码,就在用户使用时再叫醒解释器来翻译。AOT相比JIT的好处,就是不用每次打开APP都需要先编译一遍。但是,坏处就是用户安装APP的时间有点长。

越来越多的用户吐槽为什么安装一个APP也慢吞吞。于是,2017年的Android 7.0又做了一点改进,安装时先不编译中间代码,而是在用户空闲时将能够编译成机器码的那部分代码,通过AOT编译器先静态编译了。如果AOT还没来得及编译或者不能编译,再叫醒JIT+解释器两个难兄难弟来顶住。这种机制,相当于用时间换空间,既缩短了用户安装APP的等待时间,又将虚拟机里编译器和解释器能做的优化提升到最大效率了

很多人以为华为方舟编译器就是Android 7.0的ART虚拟机,其实不然。

无论是编译器还是解释器,只是在虚拟机上打补丁。手机上的虚拟机+编译器+解释器本身不仅占用硬件资源,还无法发挥好软件运行性能。对此,行业中不少厂商采用了提升安卓手机内存和存储空间的方法来弥补虚拟机的弊端。

2.额外的JNI开销

JNI,全称Java Native Interface(Java原生接口),用来和C/C++等代码进行交互。

目前95%的TOP应用都是使用Java和C/C++等多种语言混合开发而成。Java和C/C++属于两种不同架构的语言,各有自己的使用规范。为了APP正常运行,它俩之间需要互通有无,这个互通有无的接口就是JNI。在数据访问、函数调用、生命周期维护、异常处理等方面都需要这两种代码互相调用。这就意味着手机硬件资源要分配一部分给JNI去做调度。不仅占用了硬件资源,而且这种机制本身就效率较低。这便是额外的JNI开销。

3.安卓虚拟机的编译器受限于手机硬件和代码优化模板单一,代码优化空间有限

编译器包含三个部分。前端Front End,主要负责将源代码翻译成IR(Intermediate Representation,中间表示);中端的Optimizer主要负责代码优化,将前端翻译过来的IR代码优化得更高效;后端Back End则将优化后的IR编译成010101的机器码。

为了防止生态过于碎片化,安卓只为第三方开放了简单的编译代码优化模板,代码优化空间有限。

4.Java现有内存回收机制容易造成间歇性卡顿

当手机内存资源不够用的时候,安卓虚拟机就会召唤GC(Garbage Collection)“封锁公路”,让所有手机运行的Java线程全部暂停,等待它回收内存空间,避免“交通流量超载”。这个GC机制,无法精确控制和干预,用户也无法把它去掉,所以性能比较差的手机还存在“间歇性”卡顿。

十余年间,通过安卓系统的持续优化,以及内存的不断加持,安卓手机构筑了足够流畅的用户体验。但是安卓的四大难题,一直影响着安卓手机的体验。

对此,华为科学家和工程师们持续攻关了十年。

二、十年方舟

2009年,华为启动5G基础技术研究,同时开始创建编译组,第一批海内外研究人员加入。

2013年,华为推出面向基站领域的自研编译器HCC,并正式提出编译器框架构想。

2014年,众多海内外知名专家加入华为,方舟项目正式启动。

2016年,成立编译器与编程语言实验室。

2019年,华为方舟编译器正式面世!

这十年,每一次攻关都蕴含着华为软件工程师们的倾力投入,每一个进展都值得铭记。

2017年5月,方舟编译器上第一个Java程序“HelloWorld”跑通。

2017年8月的一个凌晨,在华为的一个实验室里,项目组已经连续数日24小时不间断攻关,却始终看不到success的返回信息。绝望之下,工程师把所有通信数据打印出来,逐个字节排查,最终发现有一处字节的顺序不一致。纠正后,华为方舟跑通了第一个安卓后台服务DiskStatus,这标志着对安卓的改造进入了实操阶段

2018年春节前一周,方舟编译器跑通安卓系统所有后台服务,并成功移植到手机。

接下来,项目组在除夕前夜启动了方舟编译手机的Beta测试。大年初一清晨,总架构师发来了第一条经编译器编译的运行程序发出来的拜年消息:

春节快乐,方舟大吉!

当P30发布会上「方舟」甫一出现,就在中国软件行业掀起热烈的讨论。实际上,方舟已经不是传统意义上从高级语言到机器码的“万能翻译”,更是一个编译运行系统。

一方面,方舟编译器首次在Java领域将虚拟机干掉了,也是软件史上首次将Java/C/C++等混合代码一次编译成机器码直接在手机上运行彻底告别Java的JNI额外开销,也彻底告别了虚拟机GC内存回收带来的应用进程掉线,使操作流畅度大幅提升。如果说目前最新的安卓系统是和谐号动车,那么经方舟编译的安卓系统便是高铁,是「复兴号」。

华为手机直接通过方舟编译器替换了Android system-server的所有后台服务,这一项就已经足够让华为EMUI更快一步。根据华为官方测试,方舟编译器提升手机系统操作流畅度高达24%,系统响应性能提升44%。入手搭载EMUI 9.1的手机用户,应该已经体验到了。

另一方面,方舟编译器用空间换性能,直接将编译器的代码优化搬到了开发者环境,不再被手机端的硬件资源所限制,为代码优化提供了无限可能。

此外,经测试,新浪微博极速版APP经方舟编译后,操作流畅度提升高达60%

三、干掉虚拟机

方舟编译器从立项开始,就是要干掉虚拟机。这在历史上,是没有人做到的。

SUN公司当初发明Java语言,就是借助虚拟机的这个「万能中转站」,屏蔽掉各种硬件的差异,方便同一个代码在多个平台运行,所以Java是目前最流行的高级编程语言之一。这么多年没有人能够干掉虚拟机,说明难度非常之大。

那么方舟编译器,是如何干掉虚拟机的呢?

最关键的就是要能够将Java代码直接编译成010101的机器码。

这种设想理论上可行,但是从现实来讲太难了。历史上有过一些尝试,比如现在的安卓ART虚拟机。这些尝试能够提前翻译Java语言里很多的静态语义,但是多数的动态语义,仍然搞不定,还得交给虚拟机。

所谓静态语义,我们可以理解为确定的语言和意思,比如「我是张三」;而动态语义,则需要结合上下文去理解,比如「能穿多少就穿多少」,到底穿多还是穿少,要看上下文讲的是冬天还是夏天了。大家熟悉的APP开屏广告就需要用到动态语义。

为了这个目标,华为软件工程师们对自己非常苛求,这里涉及到大量的细节,从对象模型的设计,到每一个bit的用途、每一个对象的元数据metadata(描述数据的数据)、每一个编译优化的设计,都经过了无数次的激烈讨论和推倒重来。

最终,方舟编译器通过编译阶段和运行阶段的双向加持,将静态编译动态语义的两大难点一一解决:一是设计数据模型,二是如何在运行时高效获得动态信息。方舟编译器团队基本遍历了Java的动态语义,进行了大规模的数据建模。同时,大大提高了编译时动态语义分析的精度,特别是涉及跨语言调用时;另外,华为设计了一套具有核心专利的动态语义匹配机制,有效降低了运行时动态语义的开销。

正是因为这两大难点的突破,让方舟编译器能够将Java代码编译成机器能直接执行的语言。经过华为方舟编译器的APP,再也不需要在手机上编译了,从此告别了虚拟机,带来了非常流畅的安卓体验。

四、代码万国通

95%的Top应用都是Java/C/C++等混合语言编写而成。方舟编译器的第二个使命,就是去掉混合语言互相调用带来的JNI开销

华为方舟编译器团队基于多个编程语言的深刻理解和大量研发积累,将混合语言首次实现了统一的中间表示IR,这就相当于同一个人能够理解全世界的语言,所以我们把这个突破叫做代码万国通」。

那么方舟编译器是如何使用统一的IR来表示各种语言呢?

IR是用来表示代码的数据结构,它是编译器的各模块以及相关工具之间用来传递信息的「协议和通用语言」,也是程序变换和编译优化各种算法的承载体。它是编译器的“大脑”,直接决定了编译器的最终效果。它的难度是最高的。

华为方舟编译器团队对IR进行了长达五年的精雕细琢,逐渐摸索出“大脑”里每一条神经、每一个神经元的信号规律,并在此基础上发明了一套核心专利,使得不同语言代码在开发者环境中能够统一编译成同一套可直接执行的机器码,从而消除了混合语言互相调用的开销

五、给代码装上飞机发动机

除了IR,编译器还有一个非常重要的价值,就是代码优化

华为方舟编译器,直接将代码优化从手机环节搬到了开发者环境,未来还可能搬到云端。利用开发者环境更强大的算力,可以实现更先进和精细的优化算法,来达到更强大的优化效果,在很多特定场景,代码优化的提升效果甚至是颠覆性的。

这相当于给APP在方舟里装上飞机发动机,让APP运行如飞!

值得一提的是,开发者使用方舟编译器,并不需要改变原来的编码习惯。开发者可以自行开发代码优化算法,也可以仅通过方舟编译器预置的算法进行代码优化。未来,华为还将提供代码调优工具,开发者可以选择根据工具的优化建议来调整代码,和方舟编译器配合获得更优的执行效果。我们希望,未来广大APP开发者使用方舟编译器,消费者们将会得到更好的体验。

六、再见卡顿!

方舟的第四个使命,就是解决安卓虚拟机GC内存回收带来的卡顿。

目前安卓手机内存资源不够用的时候,GC直接叫停所有应用,所以偶尔会遭遇莫名卡顿。

方舟编译器采用了引用计数法(RC,Reference Counting)来进行内存的实时回收,并且配合使用了专门的消除环算法(消除对象互相引用带来的无法回收问题),来避免GC集中式回收带来的系统卡顿。相比GC,方舟的内存回收是实时的而非集中式的,且不需要暂停应用进程,这样便大大消除了卡顿。

如果把内存回收比作打扫房间,那么GC的策略是专门有一个卫生员,看房间里垃圾太多了,就把所有人请出去,打扫完了再让大家回来。而方舟的RC则是每个人收拾自己的垃圾,用完就清理,保持清洁的同时不影响人的正常活动。

软件有一个大家都很熟悉的死循环,就是电脑被一个无限循环的运行程序把计算机资源占光。

这种“死循环”在软件中叫“环引用”。为了从机制避免手机内存被环引用“吃掉”,方舟编译器引入annotation的“告警”标示,对基础类的环进行标注。当然,Java程序员也可以对业务代码中的环进行标注。经过丰富的实践验证,方舟这种机制可以减少大部分程序中环的出现。另外一方面,方舟编译器在运行状态下引入了高效的环回收机制,允许有选择地智能回收某个APP的内存占用,这对传统的环回收算法是一个非常棒的改进。

安卓卡顿再见!

正是因为方舟做到混合语言的统一中间表示和完全静态编译,它干掉了虚拟机,消除了JNI开销,告别了GC内存回收的“间歇性”卡顿,以及其他很多开销。同时,为开发者进行代码优化提供了一个更广阔的舞台。


科技的进步,离不开全球无数科学家和工程师的奉献,产业的繁荣,更离不开开放合作!8月9日-8月11日2019华为开发者大会,我们期待和知友们进一步探讨!

文章来源:菊厂搞机

本文参考文献及资料:

1.《人类简史》尤瓦尔·赫拉利著

2.《IT通史:计算机技术发展与计算机企业商战风云》李彦编著

3.《华为王成录现场采访手稿和PPT》

4. 华为相关研发专家调研和访谈

5. 维基百科、CSDN、知乎等互联网资讯


关于方舟编译器的更多内容,你还可以戳链接了解更多:

如何评价华为的方舟编译器?www.zhihu.com图标

欢迎继续关注 @华为终端 官方知乎机构号,更多新品资讯等你来!

编辑于 2019-08-09