67

我正在开发一个高性能的 Android 应用程序(一个游戏),尽管我首先尝试编写代码以提高可读性,但我喜欢在脑海中保留一个幕后发生的事情的画面。使用 C++,我对编译器将为我做什么和不做什么有了相当好的直觉。我正在尝试对 Java/Android 做同样的事情。

因此这个问题。我在网上几乎找不到关于这个话题的信息。Java 编译器、Dalvik 转换器 (dx) 和/或 JITter(在 Android 2.2+ 上)是否会执行如下优化?

  • 方法内联。在什么条件下?private方法总是可以安全地内联;这会完成吗?public final方法呢?其他类对象的方法?static方法?如果编译器可以轻松推断出对象的运行时类型怎么办?我应该尽可能finalstatic尽可能声明方法吗?

  • 公共子表达式消除。例如,如果我访问someObject.someField两次,是否只进行一次查找?如果是对 getter 的调用呢?如果我两次使用某个算术表达式怎么办?它只会被评估一次吗?for如果我使用某个表达式的结果(我知道它的值不会改变)作为循环的上限怎么办?

  • 数组查找的边界检查。工具链会在某些情况下消除这种情况,比如原型for循环吗?

  • 值内联。对某些内容的访问是否public static final int总是内联的?即使他们在另一个班级?即使它们在另一个包裹中?

  • 分支预测。这是一个多大的问题?在典型的 Android 设备上进行分支是否会对性能产生很大影响?

  • 简单的算术。会someInt * 2被取代someInt << 1吗?

等等...

4

3 回答 3

105

我是 Ben,一位在 JIT @ Google 工作的工程师。当 Bill 和我开始这个项目时,目标是尽快交付一个有效的 JIT,同时尽量减少对资源争用的影响(例如内存占用、CPU 被编译器线程劫持),以便它可以在低端设备上运行出色地。因此,我们使用了一个非常原始的基于轨迹的模型。也就是说,传递给 JIT 编译器的编译实体是一个基本块,有时短到一条指令。这样的跟踪将在运行时通过一种称为链接的技术拼接在一起,这样解释器和代码缓存查找就不会经常被调用。在某种程度上,加速的主要来源来自于消除频繁执行的代码路径上重复的解释器解析开销。

也就是说,我们确实使用 Froyo JIT 实现了很多本地优化:

  • 寄存器分配(v5te 目标为 8 个寄存器,因为 JIT 生成 Thumb 代码/v7 为 16 个寄存器)
  • 调度(例如,Dalvik 寄存器的冗余 ld/st 消除、负载提升、存储下沉)
  • 冗余空检查消除(如果可以在基本块中找到这种冗余)。
  • 简单计数循环的循环形成和优化(即循环体中没有侧出口)。对于这样的循环,优化了基于扩展归纳变量的数组访问,以便仅在循环序言中执行空值和范围检查。
  • 每个虚拟调用站点一个条目内联缓存,在运行时带有动态修补。
  • 窥孔优化,例如 mul/div 的文字操作数的功耗降低。

在 Gingerbread 中,我们为 getter/setter 添加了简单的内联。由于底层的 JIT 前端仍然是简单的基于跟踪的,如果被调用者在那里有分支,它将不会被内联。但是实现了内联缓存机制,以便可以毫无问题地内联虚拟 getter/setter。

我们目前正在努力将编译范围扩大到超越简单的跟踪,以便编译器有更大的代码分析和优化窗口。敬请关注。

于 2011-02-08T07:20:38.397 回答
10

我确信我的回答不会回答你所有的问题,但我想如果它回答了一个问题,那就是一场胜利。

您似乎对该主题有深刻的了解并且知道自己想要什么,因此您可能想要执行以下操作。构建一个示例应用程序,其中包含您要调查的方面。

获取您获得的 APK 并通过APK Tool运行它。正如我们所知,对您自己的代码进行逆向工程以完成您想要做的事情是非常好的。

APK 工具将提取和解码您的资源,并将文件反向工程.dex.smali文件。您可能还想查看smali项目以获取有关如何读取.smali文件及其限制的更多信息。

同样,我很确定这不会回答您的所有问题,但这可能是一个好的开始。

于 2011-02-06T12:28:46.307 回答
5

首先,让我先说我不是 dalvik 专家,我的一些回答可能是错误的。但是我已经深入研究了dalvik中的JIT代码,并且我对dalvik运行的字节码非常熟悉。

  1. 方法内联 - 据我所知,这永远不会发生。我几乎肯定它永远不会在字节码级别发生,而且我认为它目前不会发生在 JIT 级别 - 尽管它可能在未来发生。

  2. 通用子表达式消除 - 我相信这只适用于不使用任何非最终变量/字段的子表达式。即使到那时它也会发生,我并不完全肯定。如果完成,我希望它在字节码级别完成,可能不是 JIT 级别。

  3. 数组查找的边界检查 - 没有线索

  4. 值内联 - 据我所知,是的 - 它们将在所有这些场景中内联。

  5. 分支预测 - 不确定

  6. 简单的算术 - 据我所知

另外,我想向您提及另一种方法 - dx 和 dalvik 都是开源的,因此您可以随心所欲地挖掘它们。虽然,它们显然不是小的代码库,所以在那个级别上挖掘它们需要相当多的努力

于 2011-02-07T03:29:49.617 回答