游戏蛮牛学习群(纯技术交流,不闲聊):539178957
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

蛮牛译馆

关注:633

当前位置:游戏蛮牛 技术专区 蛮牛译馆

查看: 1224|回复: 11

[外文翻译] 【译】IL2CPP的优化 : Devirtualization

[复制链接]  [移动端链接]
3偶尔光临
269/300
排名
14078
昨日变化
7

29

主题

36

帖子

269

积分

Rank: 3Rank: 3Rank: 3

UID
67815
好友
8
蛮牛币
965
威望
0
注册时间
2015-1-12
在线时间
82 小时
最后登录
2018-6-20

专栏作家

QQ
发表于 2017-12-26 19:35:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 SilentLion 于 2018-1-21 15:59 编辑

本文首发于知乎专栏:MACK的游戏开发笔记,欢迎各位关注。

最近在做Unity的静态代码分析的尝试,看到一篇针对IL2CPP的优化的文章,这篇文章介绍了如何通过sealed关键字告诉编译器是否会有派生,让编译器对虚方法的调用做出优化,直接调用提升性能。因为我们目前各个平台都使用IL2CPP,因此这个优化非常重要。另外配合Unity静态代码分析,可以在开发阶段识别出所有有性能隐患的代码。
原文来自UnityBlogs https://blogs.unity3d.com/cn/2016/07/26/il2cpp-optimizations-devirtualization/

Unity的脚本虚拟机团队一直在寻找让代码运行得更快的方法。这是三篇介绍关于IL2CPP AOT编译器小优化文章的第一篇,另外这篇文章也会教大家如何进行优化。虽然这些优化并不会让你的代码运行速度提升两到三倍,但是它们也会对游戏起到非常重要的帮助,我们也希望它们能帮助您了解你的代码是如何运行的。
现代编译器非常擅长执行各种优化来提高代码运行时的性能。作为开发人员,我们通常可以向编译器显式的传达一些代码信息来帮助编译器提升性能。今天,我们将详细讨论IL2CPP的一个小优化,看看它如何改进现有代码的运行效率。

Devirtualization
众所周知,虚方法的调用通常比函数直接调用开销更大。我们对libil2cpp的运行时库中进行了一些性能优化,以降低虚函数的调用开销(在下一篇文章中会有更多的介绍),但是它们仍然需在运行时进行一些查找有一些开销。编译器无法知道在运行时那个函数会被调用,或者是否可以被调用?
Devirtualization 是一种常见的编译器优化策略,它将通过虚方法通过虚表的调用转换为直接调用。当编译器在编译时能够准确地知道运行时实际会调用哪种方法时,编译器就会使用这种策略优化。但不幸的是,这点往往很难做到,因为编译器通常无法了解整个代码库的代码。但是如果可以做到的话,它可以使虚拟方法的调用变的更快。

典型的例子
当我作为一个年轻开发者的时候,我通过一个相当常见的动物例子学习了虚方法的相关知识。下面这段代码您可能也很熟悉:
[C#] 纯文本查看 复制代码
public abstract class Animal {
  public abstract string Speak();
}

public class Cow : Animal {
   public override string Speak() {
       return "Moo";
   }
}

public class Pig : Animal {
    public override string Speak() {
        return "Oink";
   }
}

接下来在Unity(5.3.5版)中,我们可也以使用这些类来做一个小农场:
[AppleScript] 纯文本查看 复制代码
public class Farm: MonoBehaviour {
   void Start () {
       Animal[] animals = new Animal[] {new Cow(), new Pig()};
       foreach (var animal in animals)
           Debug.LogFormat("Some animal says '{0}'", animal.Speak());

       var cow = new Cow();
       Debug.LogFormat("The cow says '{0}'", cow.Speak());
   }
}

这里的每次调用都是一个虚方法的调用。让我们看看能否让IL2CPP对这些方法调用做出优化直接调用来提高执行性能。
生成的C++代码
我非常喜欢IL2CPP的一个特性就是它时生成C++代码而不是汇编代码。当然,这段代码看起来不像一般手写的C++代码,但是还是比汇编更容易理解。让我们看看生成的foreach里的代码:
[AppleScript] 纯文本查看 复制代码
// Set up a local variable to point to the animal array
AnimalU5BU5D_t2837741914* L_5 = V_2;
int32_t L_6 = V_3;
int32_t L_7 = L_6;

// Get the current animal from the array
V_1 = ((L_5)->GetAt(static_cast<il2cpp_array_size_t>(L_7)));
Animal_t3277885659 * L_9 = V_1;

// Call the Speak method
String_t* L_10 = VirtFuncInvoker0< String_t* >::Invoke(4 /* System.String AssemblyCSharp.Animal::Speak() */, L_9);

我已经删除了一些其他的生成代码来做简化。看到那个丑陋的Invoke调用了吗?它先在虚表中查找真正被调用的虚方法,然后才调用它。显而易见,虚表的查找会比直接调用函数慢很多。因为这种动物可以是一头牛或一头猪,也可以是某种其他类型的动物。
接下来让我们看看第二段代码生成的C++代码。第二段代码我们new了一个Cow,然后调用了LogFormat打印Cow的Speak函数的返回值,这看上去应该是直接调用函数了吧:
[AppleScript] 纯文本查看 复制代码
// Create a new cow
Cow_t1312235562 * L_14 = (Cow_t1312235562 *)il2cpp_codegen_object_new(Cow_t1312235562_il2cpp_TypeInfo_var);
Cow__ctor_m2285919473(L_14, /*hidden argument*/NULL);
V_4 = L_14;
Cow_t1312235562 * L_16 = V_4;

// Call the Speak method
String_t* L_17 = VirtFuncInvoker0< String_t* >::Invoke(4 /* System.String AssemblyCSharp.Cow::Speak() */, L_16);
但即使在这种情况下,我们可以看到编译器仍然在通过虚表调用函数!IL2CPP在优化方面相当保守,在大多数情况下都更倾向于保证正确性。由于它没有对全程序进行分析来确定这是一个可以直接调用的函数,因为可能牛也有派生类,所以它选择了更安全(和更慢)的虚方法调用。
但是假如我们知道农场里没有其他种类的牛了,牛没有其他派生类了。那么我们就可以把这些信息显式传达给编译器,让编译器优化,我们就能得到一个更好的结果。让我们对Cow做一些修改:
[AppleScript] 纯文本查看 复制代码
public sealed class Cow : Animal {
   public override string Speak() {
       return "Moo";
   }
}

sealed关键字可以告诉编译器,Cow不会有派生类了(sealed 也可以修饰Speak函数)。这样IL2CPP就能确信可以直接进行方法调用了:
[AppleScript] 纯文本查看 复制代码
// Create a new cow
Cow_t1312235562 * L_14 = (Cow_t1312235562 *)il2cpp_codegen_object_new(Cow_t1312235562_il2cpp_TypeInfo_var);
Cow__ctor_m2285919473(L_14, /*hidden argument*/NULL);
V_4 = L_14;
Cow_t1312235562 * L_16 = V_4;

// Look ma, no virtual call!
String_t* L_17 = Cow_Speak_m1607867742(L_16, /*hidden argument*/NULL);
可以看到这次调用就是直接调用不会再慢了,因为我们已经明确的告诉编译器相关信息,可以让编译器进行优化了。
虽然这种优化可能不会让您的游戏运行速度有显著的提升,但是对于代码的阅读和编译器本身来说,这都是一个非常好的实践,清楚的表达您写的代码的意图。如果您使用IL2CPP进行编译,那么我强烈建议您阅读一下编译后生成的C++代码,或许会有意想不到的收获!
下一次我们将讨论为什么虚方法调用开销比较高,以及如何使其运行的更快。

评分

参与人数 1鲜花 +1 收起 理由
fdsfewr343 + 1 很给力!

查看全部评分


回复

使用道具 举报

6蛮牛粉丝
1042/1500
排名
3068
昨日变化
11

1

主题

208

帖子

1042

积分

Rank: 6Rank: 6Rank: 6

UID
55491
好友
1
蛮牛币
1762
威望
0
注册时间
2014-11-14
在线时间
393 小时
最后登录
2018-6-22
发表于 2017-12-26 20:16:12 | 显示全部楼层
这帖子内容 已经有人在这里发过了

回复 支持 反对

使用道具 举报

7日久生情
4135/5000
排名
2914
昨日变化
8

5

主题

3111

帖子

4135

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209186
好友
5
蛮牛币
5089
威望
0
注册时间
2017-3-1
在线时间
575 小时
最后登录
2018-6-22
发表于 2017-12-27 09:02:02 | 显示全部楼层
谢谢分享

回复

使用道具 举报

7日久生情
2535/5000
排名
3392
昨日变化
3

2

主题

1780

帖子

2535

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
10199
威望
0
注册时间
2017-9-6
在线时间
362 小时
最后登录
2018-6-7
发表于 2017-12-31 08:09:37 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

6蛮牛粉丝
1154/1500
排名
3511
昨日变化
17

0

主题

480

帖子

1154

积分

Rank: 6Rank: 6Rank: 6

UID
182268
好友
2
蛮牛币
1038
威望
0
注册时间
2016-11-9
在线时间
298 小时
最后登录
2018-6-22
发表于 2018-1-3 14:37:14 | 显示全部楼层
Universal Chroma Key {:107:}{:107:}

回复 支持 反对

使用道具 举报

3偶尔光临
269/300
排名
14078
昨日变化
7

29

主题

36

帖子

269

积分

Rank: 3Rank: 3Rank: 3

UID
67815
好友
8
蛮牛币
965
威望
0
注册时间
2015-1-12
在线时间
82 小时
最后登录
2018-6-20

专栏作家

QQ
 楼主| 发表于 2018-1-21 15:59:09 | 显示全部楼层
z534605060 发表于 2017-12-26 20:16
这帖子内容 已经有人在这里发过了

呃,还有其他人翻译了吗?

回复 支持 反对

使用道具 举报

7日久生情
2535/5000
排名
3392
昨日变化
3

2

主题

1780

帖子

2535

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
10199
威望
0
注册时间
2017-9-6
在线时间
362 小时
最后登录
2018-6-7
发表于 2018-2-12 08:04:45 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

3偶尔光临
209/300
排名
9937
昨日变化
122

0

主题

47

帖子

209

积分

Rank: 3Rank: 3Rank: 3

UID
272330
好友
0
蛮牛币
47
威望
0
注册时间
2018-3-14
在线时间
66 小时
最后登录
2018-6-23
发表于 2018-4-8 14:49:08 | 显示全部楼层
看过必谢!

回复

使用道具 举报

排名
23409
昨日变化
26

0

主题

20

帖子

50

积分

Rank: 2Rank: 2

UID
268041
好友
0
蛮牛币
18
威望
0
注册时间
2018-2-7
在线时间
14 小时
最后登录
2018-4-19
发表于 2018-4-19 10:09:53 | 显示全部楼层
感谢分享

回复

使用道具 举报

7日久生情
2153/5000
排名
3147
昨日变化
15

0

主题

1502

帖子

2153

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
185339
好友
0
蛮牛币
2990
威望
0
注册时间
2016-11-20
在线时间
235 小时
最后登录
2018-6-22
发表于 2018-4-26 09:28:56 | 显示全部楼层

回复

使用道具 举报

排名
14931
昨日变化
10

0

主题

18

帖子

79

积分

Rank: 2Rank: 2

UID
85241
好友
0
蛮牛币
125
威望
0
注册时间
2016-11-24
在线时间
17 小时
最后登录
2018-6-8
发表于 2018-5-3 11:13:16 | 显示全部楼层
强烈支持!!!!!!!!

回复

使用道具 举报

排名
32057
昨日变化
20

0

主题

10

帖子

26

积分

Rank: 1

UID
270994
好友
0
蛮牛币
2
威望
0
注册时间
2018-3-6
在线时间
6 小时
最后登录
2018-6-21
发表于 前天 16:22 | 显示全部楼层
赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

关闭

站长推荐 上一条 /1 下一条

快速回复 返回顶部 返回列表