Unity—IL2CPP & Mono
作者: lingze
iOS和Android平台 , 可以在PlayerSettings面板中选择 Mono 或者 IL2CPP 这两种Scripting Backend (译者注:Scripting Backend可以理解成”脚本后处理技术” ,根据选择的Scripting Backend类型 , 采用不同的处理技术把源代码编译成对应CPU架构支持的native binary),如果要更改Scripting Backend, 可以在PlayerSettings面板设置(路径 : Edit>Project Settings>Player),展开Other Settings,找到Configuration 下的Scripting Backend 就可以切换Mono或者IL2CPP两种模式
注意:Unity2017.3 版本以后,你可以选择IL2CPP 或者Mono这两种脚本后期处理技术 , 但是WebGL和UWP这两个平台只支持 IL2CPP, iOS仍然支持Mono , 但是不再允许Mono(32位)应用提交到Apple Store
以下是IL2CPP脚本后处理技术示例图:
不同 “脚本后处理技术”(Scripting Backend) 的优点和缺点
每一种都有优点和缺点,明白他们的特性就能让你在不同的情况下做出正确的选择:
IL2CPP
- 相比Mono, 代码生成有很大的提高
- 可以调试生成的C ++代码
- 可以启用引擎代码剥离(Engine code stripping)来减少代码的大小
- 相比Mono构建应用非常慢
- 只支持AOT(Ahead of Time)编译
Mono
- 构建应用非常快
- 由于Mono的JIT(Just In Time compilation ) 机制, 所以支持更多托管类库
- 支持运行时代码执行(译者注: 由于JIT机制,所以能在运行的过程中执行新生成的或者动态加载的代码)
- 必须将代码发布成托管程序集(.dll 文件 , 由mono或者.net 生成 )
提示:你应该使用IL2CPP来开发和发布你的项目 ,但是为了提高版本迭代速度,可以在开发期间切换到Mono模式(译者注:因位使用Mono,构建应用非常快)
注意:Player Settings 面板中的target architectures( CPU架构 ARMv7,x86等) 默认选项,Unity 针对其发布版进行了优化。在开发期间使用默认值会增加构建时间,因为Unity会为所选的每种CPU架构构建二进制文件
- Android 平台Target Architecture 默认勾选 armv7 和 x86 (译者注: 这两种CPU架构都支持Mono和IL2CPP,但是arm64不支持Mono)
- iOS平台 Target Architecture 默认勾选 armv7 和 arm64 (默认使用IL2CPP)
优化IL2CP 构建时长
- 使用增量构建;使用增量构建时,C ++编译器仅重新编译自上次构建以来已更改的文件。 要使用增量构建,请将项目构建到先前的构建位置(不删除原来的构建内容)
- 构建时禁用反恶意软件;在构建项目之前禁用反恶意软件(俗称杀毒软件、防护软件等),从而缩短构建时间(测试发现,在新的Windows 10上禁用Windows Defender后,项目构建时间减少了50%~66%)
- 使用固态硬盘的机器构建项目;固态硬盘(SSD)比传统硬盘(HDD)有更快的读写速度 , 将IL代码转换为c++并编译它涉及大量的读写操作, 所以用固态硬盘能加快构建速度
用户程序集(Assembly Definition File)
使用该功能,开发者可以在一个文件夹中自定义程序集。定义清晰的依赖关系,可以确保脚本更改后,只会重新生成必需的程序集,减少编译时间。 项目越大脚本越多,编译时间必然会越长。在进行项目迭代时,这很容易制约效率,因此设置好程序集定义文件可以提高工作效率,减少脚本编译的时间
如上图,如果你仅更改了Main.dll中的脚本,其它程序集都不需要重新编译。由于Main.dll包含的脚本更少,因此它的编译速度比Assembly-CSharp.dll更快。 同样,Stuff.dll的更改仅会导致Main.dll和Stuff.dll重新编译 . 更多关于用户程序集的相关信息,请查看手册:Script compilation and assembly definition files
泛型共享
Mono和.Net运行时也同样采用泛型共享技术,IL2CPP起初并不支持泛型共享,到最近的改进版中才使得泛型共享机制足够的健壮并能使其带来好处。
思考一下,如果你在C#中写一个List<T>的实现,这个List的实现会根据T的类型不同而不同么?对于List的Add函数而言,List<string>和List<object>会是一样的代码么?那如果是List<DateTime>呢?
实际上,泛型的强大之处在于这些C#的实现都是共享的,List<T>泛型类可以适用于任何的T类型。但是当C#代码转换成可执行代码,比如Mono的汇编代码或者由IL2CPP产生的C++代码的时候会发生什么呢?我们能在这两个层面上也实现Add函数的代码共享么?
答案是肯定的,我们能在大多数的情况下做到共享。正如本文后面将要讨论的:泛型函数的泛型共享与否主要取决于这个T的大小如何。如果T是任何的引用类型(像string或者是object),那T的尺寸永远是一个指针的大小。如果T是一个值类型(比如int或者DateTime),大小会不一样,情况也会相对复杂。代码能共享的越多,那么最终可执行文件的尺寸就越小。所以,当T是值类型的时候,IL2PP是不会进行泛型共享
泛型共享是自IL2CPP发布以来一个最重要的改进。通过共享相同的代码实现,它使得C++代码尽可能的小
生成报告
Build Report是一个包含在Unity中但尚未使用UI的API , 构建项目会生成一个构建报告文件,该文件允许你发现剥离的内容以及从最终可执行文件中删除它的原因
查看剥离信息:
- 构建你的项目
- 保持编辑器打开
- 连接到http://files.unity3d.com/build-report/
Build Report工具连接到正在运行的Unity Editor,下载并显示构建报告的细分
可以在Library/LatestBuild.buildreport中生成的文件上使用binary2text工具来查看报告中的数据。 Binary2text随Unity一起提供Unity下的Unity.app/Contents/Tools/或Windows上的Unity / Editor/Data/Tools/。 构建报告在Unity 5.5及更高版本中可用
参考文章
Unity:IL2CPP & Mono Unity:An introduction to IL2CPP internals Unity:Script compilation and assembly definition files