Dalvik 和 Android 工具链可以带来哪些优化?
我正在开发一个高性能 Android 应用程序(一款游戏),虽然我首先尝试编写代码以提高可读性,但我喜欢在脑海中保留一幅幕后发生的事情的图片。通过 C++,我对编译器能为我做什么和不能做什么有了相当好的直觉。我正在尝试为 Java/Android 做同样的事情。
于是就有了这个问题。我在网上找不到关于这个主题的信息。 Java 编译器、Dalvik 转换器 (dx) 和/或 JITter(在 Android 2.2+ 上)是否会执行如下优化?
方法内联。在什么条件下?
私有
方法始终可以安全地内联;这会完成吗?public Final
方法怎么样?其他类的对象上的方法?静态
方法?如果编译器可以轻松推断出对象的运行时类型怎么办?我应该尽可能将方法声明为final
或static
吗?公共子表达式消除。例如,如果我访问
someObject.someField
两次,查找只会完成一次吗?如果这是对 getter 的调用怎么办?如果我使用某个算术表达式两次怎么办?只会评估一次吗?如果我使用某个表达式的结果(我知道其值不会更改)作为for
循环的上限会怎样?数组查找的边界检查。工具链是否会在某些条件下消除这种情况,例如典型的
for
循环?值内联。对某些
public static final int
的访问是否总是内联的?即使他们在另一个班级?即使它们在另一个包中?分支预测。这究竟是一个多大的问题呢?在典型的 Android 设备上,分支是否会对性能造成很大影响?
简单的算术。将
someInt * 2
替换为someInt << 1
?
诸如此类...
I'm working on a high-performance Android application (a game), and though I try to code for readability first, I like to keep in the back of my mind a picture of what is happening under the hood. With C++, I've developed a fairly good intuition about what the compiler will and won't do for me. I'm trying to do the same for Java/Android.
Hence this question. I could find very little about this topic on the web. Will the Java compiler, Dalvik converter (dx) and/or JITter (on Android 2.2+) perform optimizations like the following?
Method inlining. Under what conditions?
private
methods can always safely be inlined; will this be done? How aboutpublic final
methods? Methods on objects of other classes?static
methods? What if the runtime type of the object can easily be deduced by the compiler? Should I declare methods asfinal
orstatic
wherever possible?Common subexpression elimination. For example, if I access
someObject.someField
twice, will the lookup be done only once? What if it's a call to a getter? What if I use some arithmetic expression twice; will it be evaluated only once? What if I use the result of some expression, whose value I know not to change, as the upper bound of afor
loop?Bounds checking on array lookups. Will the toolchain eliminate this in certain conditions, like the archetypical
for
loop?Value inlining. Will accesses to some
public static final int
always be inlined? Even if they're in another class? Even if they're in another package?Branch prediction. How big an issue is this even? Is branching a large performance hit on a typical Android device?
Simple arithmetic. Will
someInt * 2
be replaced bysomeInt << 1
?
Etcetera...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我是 Ben,JIT @ Google 的工程师之一。当 Bill 和我开始这个项目时,目标是尽快交付一个有效的 JIT,同时对资源争用(例如内存占用、CPU 被编译器线程劫持)影响最小,以便它可以在低端设备上运行出色地。因此我们使用了一个非常原始的基于跟踪的模型。也就是说,传递给 JIT 编译器的编译实体是一个基本块,有时短至一条指令。此类跟踪将在运行时通过称为链接的技术缝合在一起,以便解释器和代码缓存查找不会经常被调用。在某种程度上,加速的主要来源来自于消除频繁执行的代码路径上重复的解释器解析开销。
也就是说,我们确实使用 Froyo JIT 实现了相当多的本地优化:
在 Gingerbread 中,我们为 getter/setter 添加了简单的内联。由于底层 JIT 前端仍然是基于简单的跟踪,因此如果被调用者在其中有分支,则不会内联。但是内联缓存机制的实现使得虚拟 getter/setter 可以毫无问题地内联。
我们目前正在努力扩大编译范围,超越简单的跟踪,以便编译器有更大的窗口进行代码分析和优化。敬请关注。
This is Ben, one of the engineers working on the JIT @ Google. When Bill and I started on this project, the goal was to deliver a working JIT as soon as possible with minimal impact to resource contention (eg memory footprint, CPU hijacked by the compiler thread) so that it can run on low-end devices as well. Therefore we used a very primitive trace based model. That is, the compilation entity passed to the JIT compiler is a basic block, sometimes as short as a single instruction. Such traces will be stitched together at runtime through a technique called chaining so that the interpreter and code cache lookup won't be invoked often. To some degree the major source of speedup comes from eliminating the repeated interpreter parsing overhead on frequently executed code paths.
That said, we do have quite a few local optimizations implemented with the Froyo JIT:
In Gingerbread we added simple inlining for getters/setters. Since the underlying JIT frontend is still simple trace based, if the callee has branches in there it won't be inlined. But the inline cache mechanism is implemented so that virtual getters/setters can be inlined without problems.
We are currently working on enlarging the compilation scope beyond a simple trace so that the compiler has a larger window for code analysis and optimization. Stay tuned.
我确信我的答案不会回答您的所有问题,但我想如果它回答了一个问题,那就是一场胜利。
您似乎对该主题有深入的了解并且知道自己想要什么,因此您可能想要执行以下操作。构建一个包含您想要研究的方面的示例应用程序。
获取您获得的 APK 并通过 APK 工具运行它。据我们所知,对您自己的代码进行逆向工程以执行您想要的操作是完全可以的。
APK 工具将提取并解码您的资源,并将
.dex
文件逆向工程为.smali
文件。您可能还想查找 smali 项目,以获取有关如何阅读 < code>.smali 文件及其限制。我再次确信这不会回答您的所有问题,但这可能是一个好的开始。
I'm sure that my answer will not answer all of your questions but I guess it is a win if it answers even one.
You seem to have a profound knowledge on the subject and know what you want so you might want to do the following. Build an example application containing the aspects you want to investigate on.
Take the APK you get and run it through the APK Tool. Reverse engineering your own code to do just what you are intending is perfectly fine as we know.
The APK Tool will extract and decode your resources and will reverse engineer
.dex
files to.smali
files. You might want to look up the smali project too to get more information about how to read the.smali
files and about its limitations.Again I'm pretty sure that this is not going to answer all of your questions but it might be a good start.
首先,我先声明一下,我不是dalvik方面的专家,我的一些回答可能是错误的。但我深入研究了dalvik中的JIT代码,对dalvik运行的字节码相当熟悉。
方法内联 - 据我所知,这种情况永远不会发生。我几乎肯定它永远不会在字节码级别发生,而且我认为它目前不会在 JIT 级别发生 - 尽管将来可能会发生。
公共子表达式消除 - 我相信这只适用于不使用任何非最终变量/字段的子表达式。即使到那时我也不完全肯定它是否会发生。如果完成,我希望它在字节码级别完成,可能不是 JIT 级别。
数组查找的边界检查 - 没有线索
值内联 - 据我所知,是的 - 它们将在所有这些场景中内联。
>
分支预测 - 不确定
简单算术 - 据我所知还不够
另外,我想提一下另一种接近您的途径 - dx 和 dalvik 都是开源的,因此您可以根据自己的喜好深入研究它们。不过,它们显然不是小代码库,因此需要付出相当大的努力才能在这个级别上深入研究它们
First, let me preface this by saying that I'm not an expert on dalvik, and some of my responses may be wrong. But I have dug into the JIT code in dalvik, and I'm quite familiar with the bytecode that dalvik runs.
Method inlining - as far as I know, this never happens. I'm almost positive it never happens at the bytecode level, and I don't think it happens at the JIT level currently - although it might in the future.
Common subexpression elimination - I believe this would only be done for subexpressions that don't use any non-final variables/fields. I'm not entirely positive if it would happen even then. If it is done, I would expect it to be done at the bytecode level, probably not the JIT level.
Bounds checking on array lookups - no clue
Value inlining - As far as I know, yes - they will be inlined in all of those scenarios.
Branch prediction - not sure
Simple arithmetic - not as far as I know
Also, I'd like to mention another avenue of approach to you - dx and dalvik are both open source, so you can dig into them all you like. Although, they're obviously not small codebases, so would take a fair bit of effort to dig into them at that level