我正在开发一款高性能的Android应用程序(游戏),虽然我首先尝试编写可读性代码,但我想在脑海里留下一张引人注目的图片。使用C ++,我已经对编译器将为我做什么和不做什么做了很好的直觉。我正在尝试为Java / Android做同样的事情。
因此这个问题。我在网上找不到这个话题。 Java编译器,Dalvik转换器(dx)和/或JITter(在Android 2.2+上)是否会执行如下优化?
方法内联。在什么条件下? private
方法始终可以安全地内联;这样做会吗? public final
方法怎么样?关于其他类对象的方法? static
方法?如果编译器可以轻松推导出对象的运行时类型,该怎么办?我应该尽可能将方法声明为final
或static
吗?
常见的子表达式消除。例如,如果我两次访问someObject.someField
,查找只会进行一次吗?如果它是对吸气剂的召唤怎么办?如果我使用一些算术表达式两次会怎么样它只会被评估一次吗?如果我使用某些表达式的结果(我知道不会改变它的值)作为for
循环的上限,该怎么办?
检查数组查找的边界。工具链是否会在某些条件下消除这种情况,例如原型for
循环?
价值内联。访问某些public static final int
是否总是内联?即使他们在另一个班级?即使他们在另一个包裹中?
分支预测。这甚至有多大问题?在典型的Android设备上是否会产生巨大的性能影响?
简单算术。将someInt * 2
替换为someInt << 1
?
...诸如此类
答案 0 :(得分:104)
这是Ben,JIT @ Google的工程师之一。当Bill和我开始这个项目时,我们的目标是尽快提供一个有效的JIT,对资源争用的影响最小(例如内存占用,CPU被编译器线程劫持),这样它就可以在低端设备上运行好。因此我们使用了非常原始的基于轨迹的模型。也就是说,传递给JIT编译器的编译实体是一个基本块,有时与单个指令一样短。这些跟踪将在运行时通过称为链接的技术拼接在一起,以便不会经常调用解释器和代码缓存查找。在某种程度上,加速的主要来源是消除重复的解释器解析经常执行的代码路径上的开销。
也就是说,我们确实用Froyo JIT实现了很多局部优化:
在Gingerbread中,我们为getter / setter添加了简单的内联。由于底层JIT前端仍然是基于简单的跟踪,如果被调用者在那里有分支,则不会内联。但是实现了内联缓存机制,以便可以毫无问题地内联虚拟getter / setter。
我们目前正致力于将编译范围扩展到简单的跟踪之外,以便编译器具有更大的代码分析和优化窗口。请继续关注。
答案 1 :(得分:10)
我确信我的答案不会回答你所有的问题,但我想如果它能回答一个问题就是胜利。
您似乎对该主题有深刻的了解并知道您想要什么,因此您可能希望执行以下操作。构建一个包含您要调查的方面的示例应用程序。
获取你获得的APK并通过APK Tool运行它。正如我们所知,逆向工程你自己的代码来做你想要的事情就完全没问题了。
APK工具会提取和解码您的资源,并会将.dex
个文件反向工程为.smali
个文件。您可能还想查找smali项目,以获取有关如何阅读.smali
文件及其限制的更多信息。
我再次确信这不会回答你所有的问题,但这可能是一个好的开始。
答案 2 :(得分:5)
首先,让我先说一下,我不是dalvik的专家,我的一些回答可能是错的。但是我已经在dalvik中挖掘了JIT代码,而且我对dalvik运行的字节码非常熟悉。
方法内联 - 据我所知,这种情况从未发生过。我几乎肯定它永远不会发生在字节码级别,我认为它目前不会发生在JIT级别 - 尽管它可能会在将来发生。
公共子表达式消除 - 我相信这只能用于不使用任何非最终变量/字段的子表达式。如果即便如此,我也不会完全肯定。如果完成,我希望它可以在字节码级别完成,可能不是JIT级别。
检查数组查找的边界 - 没有线索
价值内联 - 据我所知,是的 - 他们将在所有这些情景中被内联。
分支预测 - 不确定
简单算术 - 据我所知,
另外,我想提到另一种方法 - dx和dalvik都是开源的,所以你可以随心所欲地挖掘它们。虽然,它们显然不是小代码库,但是需要花费大量精力在这个级别上深入研究它们