查看原文
其他

IL2CPP优化(上):去虚拟化

2016-08-02 Unity官方 Unity官方平台
Unity的脚本虚拟机团队一直在寻找让代码更快运行的方式。本系列文章将为大家介绍IL2CPP AOT编译器的小优化,并教大家如何利用它们。尽管这些小优化并不能让您的代码运行速度提高2-3倍,但它们会在游戏中起到重要作用,我们希望这些优化能让您对代码的执行方式有更深入的理解。

现代编译器都很擅于执行一些优化来提高代码的运行时性能。作为开发者,我们经常可以通过将已了解功用的代码,通过一定信息明确传达给编译器,进而帮助编译器提高效率。本文将详细讲解一个关于 IL2CPP的小优化,并看看它将如何提高您现有代码的运行效率。

去虚拟化
众所周知,虚方法的调用通常比直接调用的消耗更大。我们一直致力于改善libil2cpp运行库的性能以降低虚方法调用的消耗(下篇文章将介绍更多该内容),但某些排序算法仍然需要一些运行时的查找。编译器无法知道哪些方法会在运行时被调用,亦或可不可以被调用?

去虚拟化是一个很常见的编译器优化策略,也就是将虚方法调用改为直接的方法调用。当编译器编译时可以提供准确的“实际”方法时,这个策略就会被启用。不幸的是,这点往往很难做到,因为编译器通常无法看到整个代码库。然而,如果可以的话,这将使虚方法的调用更快速。

典型例子
作为一个年轻的开发者,我学习虚方法是从一个相当惯例的动物示例开始的。这个例子您可能也很熟悉:    




在 Unity (版本 5.3.5) 中,我们可以使用这些类来做个小农场:




这里每次调用Speak的都是虚方法调用。让我们看看 IL2CPP去虚拟化这些方法调用是如何提高性能的。

生成的C++代码还不赖
我比较喜欢IL2CPP的一个功能是它会生成C++代码而非汇编代码。当然,这些生成的代码与一般手写的看起来并不一样,但这种代码要比汇编代码容易理解得多。下面看看生成的 foreach循环体代码:




这里移除了一些生成的代码来简化其内容。看到那个丑陋的Invoke调用了么?它将会在 vtable中查找适当的虚方法并进行调用。vtable的查找会比直接的函数调用慢一些,但这可以理解。因为 Animal(动物)可能是 Cow(牛)或者Pig(猪),或者一些其它的派生类型。

下面看看生成的代码中的第二个调用Debug.LogFormat,这个看起来更像是直接调用:




这个例子中仍然使用的是虚方法调用! 事实上IL2CPP对于优化非常保守,绝大多数情况下其优先确保正确性。由于它并未对整个项目做完全的分析来确保可以进行直接调用,因而选择了更安全(也更慢)的虚拟方法调用。

假设我们知道农场中没有其它类型的牛了,因此Cow(牛)这个类不会产生衍生类。如果我们清楚告知编译器这点,就能获得一个更好的结果。现将类的定义改为如下:




关键字sealed将告诉编辑器 Cow(牛)不会有衍生类(sealed也可以直接应用于 Speak方法)。现在 IL2CPP能确信进行直接调用了:




这里调用 Speak就不会再慢了,因为我们已经清楚的告诉编译器,并且有把握地允许编译器进行优化。

这种优化不会使您的游戏运行速度显著变快。但这对于后来的人阅读代码和编辑器本身来说,都是表达任何编程设想“代码化”的上佳实践。如果您想使用IL2CPP编译,强烈建议仔细阅读项目生成的C++代码,可能会有意想不到的收获!

下一篇我们将讨论为什么虚方法调用消耗高,以及怎样使其变得更快。请保持关注!


延展阅读

Unity官方活动圆满落幕

Unity展台不容忽视的精彩

Unity 5.4正式版发布!

基于Unity 5.3.5p8的C#编译器升级!

如何中断Unity动画状态机的转换过程?


更多Unity官方最新消息,尽在Unity官方中文社区(forum.china.unity3d.com),请保持关注!


点击“阅读原文”进入Unity官方中文社区!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存