新浪新闻客户端

解读开源的方舟编译器,创新的RC与多层的IR

解读开源的方舟编译器,创新的RC与多层的IR
2019年09月12日 12:30 新浪网 作者 真养猫的哈士奇

  编译器是很基础的软件,不过已经很多年没有冒出新的编译器了,技术也很成熟了,华为为什么要搞一个方舟编译器呢?当然是为了编译程序使之更好的运行了。多数人都知道JAVA是一个跨平台的编程语言,为什么JAVA可以跨平台呢?这就是JAVA虚拟机(JVM)的功劳,JAVA语言其实是通过JVM来运行的,JVM与各个平台之间做了适配,其实跨平台的是JVM,Android的ART虚拟机是一个特别的JVM。在计算机领域没有什么问题是不能通过增加一个间接的中间层来解决的,JVM就是这样的中间层。很多人会问Android 的APP需要跨平台运行吗?现在来看不需要,那Google为什么选JAVA作为开发语言呢?可能有很多因素的考量,但是谁也没有想到SUN居然被著名的专利流氓ORACLE收购了,即便Google把JVM改写了好几遍也依然没躲过这个专利官司,真正的大公司还是要有自己能完全控制的编程语言呀,华为会走出这一步吗?有点跑题了,虚拟机是JAVA的精髓所在,虚拟机带来的好处是以性能为代价的,那么那些必须考考虑效率的库和应用怎么办呢?用JNI技术调用C/C++库,又是一笔不必要的开销,还是影响效率。JAVA作为Android的开发语言已经成为事实,即便不需要跨平台依然要忍受虚拟机带来的低效问题。如何解决呢?干掉虚拟机最好了,反正现在也不需要跨平台,这也是方舟编译器的主要目的之一,当然,也并不是说方舟不能支持跨平台,从公布的材料中看,还是有跨平台方面的考虑只是不是JAVA虚拟机这种形式了。

  解读开源的方舟编译器,创新的RC与多层的IR

  既来之则安之,既然只能用JAVA就想法优化他,优化JAVA的运行效率关键还是在于虚拟机。Android 在5.0之后将Dalvik虚拟机替换为ART虚拟机,这也是Android Run Time,在几经更改之后现在Android采用的是 解释执行+ JIT + AOT 的混合编译策略。

  解读开源的方舟编译器,创新的RC与多层的IR

  一般的静态编程语言(编译时确定类型,C/C++、JAVA都是)用编译器编译成二进制代码并不难,但是JAVA还存在一些动态特性,比如反射,通常被称为JAVA语言的高级特性,使用也很广泛,尤其是框架。反射这样的特性对程序员很友好,但是对编译器是个灾难,与runtime密切相关,方舟编译器如何解决这个问题还没有具体的Codes公布,不过在Readme中有一句可以留意:更轻量的语言运行时,这很可能与反射有关。编译器本身是一个翻译的过程,现代编译器的结构基本就如下图LLVM的结构差不多,分为前后端,方舟编译器也是如此,目前主要开源的就是里面的IR部分。还是那句话,在计算机领域没有什么问题是不能通过增加一个间接的中间层来解决的,IR也是一个中间层,连接编译器的前端和后端。更形象的说,IR可以理解为JAVA的字节码,后端可以理解为JAVA虚拟机。

  解读开源的方舟编译器,创新的RC与多层的IR

  在之前公布的信息中表示方舟编译器支持跨语言编译(尚未开源),跨语言编译的原理就是将多种语言翻译成同一种IR表示语言,然后两部分合并优化。方舟编译器的IR分多层,其中编程语言相关的优化使用高层表示(high level ir)

  通用优化使用中层表示(mid level ir),编译时就是一个持续降低层次的过程。在high level 中我们还能看到 if、while这样的操作码,与高级语言很像。用个例子看,C语言如下:

  int fact(int n) {if (n != 1)return foo(n-1);else return 1;}翻译成MIR如下所示:

  func &fact i32 {var %n i32 ) {if ( ne ( dread i32 %n, constval i32 1 ) ) {callassigned $foo (sub i32 ( dread i32 %n, constval i32 1 ) ) { dassign %tempvar_1 }return i32 ( dread %tempvar_1 ) }else {return i32 ( constval i32 1 )}}

  可以看出来很像,MIR中的语言像极了高级语言,只是有一些冗余,对于编译器来说准确是第一位的,需要适当的冗余。根据方舟编译器的文档说明,当所有语言都翻译成MIR后,都有自己的Opcodes,更加接近原语言,但是在逐级降低优化的时就越来越接近处理器的原生指令。

  方舟编译器目前开源的代码还有一部分是关于GC(Garbage Collection)的,GC是对内存的管理,在程序运行中内存是一种宝贵的资源,当这块内存的外部引用不存在时需要释放内存,有些语言比如C/C++不提供内存回收机制,需要程序员手动的new/delete、malloc/free,一不小心就会造成内存泄漏,而JAVA的GC机制就很好的解决了这个问题。

  自动内存管理通常有两种垃圾回收策略:一种是引用追踪垃圾回收(Tracing Garbage Collection,Tracing GC),另一种是基于引用计数(Reference Counting,RC)垃圾回收。JAVA上的垃圾回收采用的是GC方式,这个追踪过程回依赖整个系统中多线程的间歇同步和停顿,可能会造成卡顿。方舟编译器是在JAVA上尝试RC,RC的方式是在函数返回时或异常退出时进行标记,这些操作会带来额外的开销,华为是想通过编译器优化将RC开销降低。

  RC并不是一种落后的内存回收机制,广受好评的苹果swift就采用的RC回收机制,在用户体验非常重要的客户端RC其实很适合,但是JAVA实现RC并不是很好弄,他还有庞大的旧JAVA库。方舟编译器引入RC机制的目的为了解决GC造成的卡顿问题, RC的机制最大的问题在于可能产生循环引用,方舟编译器的应对方法是:

  1、程序员标记(目前还没有开放给第三方);

  2、引入一个环模式匹配的算法,在Runtime里会收集环在程序过程中的信息记录到手机上,然后给三方应有使用,这是一个学习的过程,学习完下次运行时就会快速知道如何避免这些环;

  3、GC兜底确保环能正确的解掉。

  确切的说方舟编译器并没有抛弃GC,而是采用了RC为主GC兜底的方式。当内存阈值,或者程序员调动System.GC的时候使用GC来回收内存,根据华为在首场开源技术沙龙上的说法触发GC的几率大概为5%,这样确实可以大大提高流畅性,目的基本达到了。

  目前方舟编译器开源的代码还很少,不好分析,也给出一个演示示例,这个演示示例可以做的更好一些。

  解读开源的方舟编译器,创新的RC与多层的IR

  采用RC的方式,还会有一个循环引用的问题,目前方舟编译器开源的代码还很少,不知这个循环引用如何解决,对于已存在库的标记要如何添加呢?说到标记我想到了一个人工智能训练的数据标注,从理论上来说引用的标记和解除循环也是可以自动学习的,如果说能做到自动学习,那将是编译器这些年来最大的进步。所以我对下一步的开源很有兴趣,想知道是如何做的。

  RC的自动学习解环是我之前没想到的,不知道这个自动学习算法是否会开源,对于他是如何保证解环正确的还是充满好奇。目前方舟编译器开源的部分还是少,等待更多的开源,包括运行时。

特别声明:以上文章内容仅代表作者本人观点,不代表新浪网观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与新浪网联系。
权利保护声明页/Notice to Right Holders

举报邮箱:jubao@vip.sina.com

Copyright © 1996-2024 SINA Corporation

All Rights Reserved 新浪公司 版权所有