说说 方舟编译器

26,023 阅读7分钟

华为继去年推出黑科技 GPU Turbo 之后,今年再次扔出了一枚重磅炸弹, 安卓性能革命,华为方舟编译器,号称解决安卓程序 “边解释边执行” 的低效,全程执行机器码高效运行程序。架构级优化,显著提升性能。系统操作流畅度提升 24%,系统响应提升 44%,三方应用操作流畅度提升 60%,并承诺向业界开源。敢于开源的话,这个数据应该没有水分。大家在网上应该也看到了 P30 Pro 吊打 S10 的视频。

解决安卓程序 “边解释边执行” 的低效,什么是边解释边执行?也就是说,Android 是如何执行 Apk 中的代码的?这得从机器码说起。

机器码

不论是低级语言,如汇编,还是如今广泛使用的各种高级语言,Java,C++ 等,对于 CPU 来说,全部都是天书。它能认识的只有二进制的机器码。当然,机器码对你来说,也是天书。那你应该如何指挥 CPU 运行你的程序呢?这个时候,你们之间就需要一个翻译,将你的代码翻译成机器码给 CPU,它就知道该如何执行了。面对不同的终端,翻译内容也不一样,你一个只知道 x86 指令集的去翻译 arm 的,肯定翻译不出来,就好似拿着本英语字典去翻译日语。翻译也有工具,下面说说几种常见的翻译工具:

汇编器

将汇编代码直接翻译成机器码。由于汇编代码一般和机器码都是一一对应的,所以它的速度非常快。只是汇编语言,你懂的,可读性差, 用起来难,用来写大型程序对于普通开发者基本是不现实的。

编译器

编译器将我们平常开发用的高级语言,例如 Java/C++ 等,翻译成机器码供 CPU 使用。这种编译很慢,但是执行起来会很快。

解释器

程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次。因此效率比较低。可以想象这种运行方式就慢了很多。 典型的解释型语言,php/js/python 等。

JAVA 代码是如何执行的 ?

Java 程序的执行依赖于 Java 虚拟机(JVM),JVM 能直接识别的叫做字节码。Java 代码经过编译会生成 Class 文件,即字节码文件,再交由 JVM 处理。而 JVM 又是怎么执行 Class 文件的呢?这里以 HotSpot 为例,Class 文件被虚拟机加载后会存放在方法区,实际运行时,虚拟机会执行方法区中的代码。

将字节码翻译成机器码的工作自然就由 JVM 来承担了。在 HotSpot 中,有几种翻译形式。

  • 解释执行

逐条将字节码翻译成机器码并执行

  • 即时编译(Just-in-time ,JIT)

将一个方法中包含的所有字节码编译成机器码后再执行。

前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两 者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。

Android 代码是如何执行的 ?

开发 Android 目前用的最多的还是 Java,即使不是 Java,也是 JVM 语言。Android 工程中的 java 源文件经过编译也是生成 Class 文件,最后被打包成 DEX 字节码文件,负责将 DEX 字节码翻译成机器码的是 Dalvik 或者 Art。

在 Android 5.0 之前,还是 Dalvik 的天下。Dalvik 是解释执行加上 JIT,每次app运行的时候,它动态的将一部分 Dalvik 字节码 解释为机器码。随着 App 的运行,更多的字节码被编译和缓存。因为 JIT 只编译了一部分代码,它具有更小的内存占用和更少的设备物理空间占用。但是,边解释边执行,效率低下,这也是后来 Dalvik 遭到抛弃的原因。

从 Android 4.4 开始,Android 引入了 Art,到 Android 5.0,Art 正式代替了 Dalvik。Art 使用的是 AOT(Ahead of Time)的编译方式,即在应用的安装过程中,就将所有的 Dex 字节码翻译成机器码存储在设备空间中,完全抛弃了 JIT,带来的好处是显而易见的。

  • Apps 运行的更快,因为 DEX 字节码的解释在安装过程中已经完成,直接运行机器码

  • 减少了应用的启动时间,因为本地代码可以直接执行。

  • 节省电量消耗,不需要再去一行一行的解释字节码。

  • 增强了垃圾回收。

  • 增强了开发者工具。

直接运行机器码,恩,等等,这不就是方舟编译器做的事情吗?答案肯定是否定的,否则它因为完全没有存在的必要了。事实上,这种完全基于 AOT 的模式也已经被 Android 抛弃了。如果你用过 5.0 或者 6.0 的安卓机,你一定不会忘记安装应用带来的漫长等待。由于需要在安装期间翻译字节码,所以安装过程会很长,尤其对 一些大型应用来说。另外,安装过程中翻译出来的机器码也占用了更大的机身存储空间。

Android 7.0,Android 又加入了 JIT,一个具备代码分析功能的即时 (JIT) 编译器。事实上,根据二八定律,经常运行的热点代码可能只占 20%,甚至更少,我们没有必要通过 AOT 提前将所有字节码都翻译成机器码。安装过程中放弃 AOT,加快安装速度,所以初次使用时得解释执行。当你在使用应用时,JIT 就开始分析代码了,在合适的时候将字节码翻译成机器码,在 Android 应用运行时持续提高其性能。另外,设备空闲的时候,AOT 就发挥作用了,它会将热点代码翻译成机器码并保存下来,进一步提高运行效率。这么看下来,现在的 Android 是 解释执行 、JIT、AOT 同时存在的。下面这张官网的图片很好的说明了Art 下的 JIT 架构.

关于解释器,高版本中的 Android 实现也不再那么孱弱了,根据官网相关介绍:

通过引入 Mterp(一种解释器,具有以汇编语言编写的核心提取/解码/解释机制),Android 7.0 版本中的解释器性能得以显著提升。Mterp 模仿了快速 Dalvik 解释器,并支持 arm、arm64、x86、x86_64、mips 和 mips64。对于计算代码而言,ART 的 Mterp 大致相当于 Dalvik 的快速解释器。 不过,有时候,它的速度可能会显著变慢,甚至急剧变慢:

  • 调用性能。
  • 字符串操作和 Dalvik 中其他被视为内嵌函数的高频用户方法。
  • 堆栈内存使用量较高。

Android 8.0 解决了这些问题。

说了这么多,无非是安装速度,空间占用,运行速度的平衡,目前 Android 应该已经做得很好了,但仍然存在不少诟病。

现在可以确定的是,方舟编译器绝对不可能是 完全 AOT。PPT 中最后一句话是 “希望 APP 厂商尽快使用” ,并不是手机厂商,所以不排除方舟编译器可以直接将 Apk ,或者说 Apk 中的 DEX 打包成机器码格式。但由于机器码并不是平台兼容的,所以并不能确定方舟编译器是否必须要绑定 EMUI。其实说到底,这一切都应该是为了华为的新手机系统作铺垫。华为的野心之大,以及其极其超前的技术储备,相信一个完整华为生态已经离我们不远了。

编译器叫方舟,新系统干脆就叫 诺亚 吧!

文章首发于微信公众号: 秉心说 , 专注 Java 、 Android 原创知识分享,LeetCode 题解,欢迎扫码关注!