Unity优化之IL2CPP & Mono

在iOS和Android上,在Player Settings中选择mono或il2cpp脚本后端。要更改脚本后端,请转到“Player Settings 窗口(菜单:Edit > Project Settings > Player),向下滚动到“Other Settings”部分,然后从“Scripting Backend”下拉菜单中选择mono或il2cpp。

注意:从2017.3开始,选择IL2CPP脚本后端或Mono脚本后端。然而,webgl和uwp都只支持il2cpp。iOS仍然支持快速迭代的Mono脚本后端,但您不能再向Apple提交Mono(32位)应用程序。

不同脚本后端的优点和缺点

每个脚本后端都有一些优点和缺点,这些优点和缺点会影响您的决策,对于您的情况,哪一个是正确的选择:

IL2CPP

  • 与mono相比,代码生成得到了很大的改进。
  • 从上到下调试C++脚本代码是可能的。
  • 您可以启用引擎代码剥离以减小代码大小。
  • 构建时间比Mono长。
  • 只支持AOT编译。

Mono

  • 比IL2CPP更快的构建时间。
  • 由于实时编译(JIT),支持更多的托管库。
  • 支持运行时代码执行。
  • 必须提供托管程序集(.mono或.net生成的dll文件)。

提示:您应该使用IL2CPP来开发和交付您的项目。如果使用il2cpp时迭代时间太慢,请在开发期间临时切换到mono脚本后端以提高迭代速度。

注意:Player Settings中的默认目标架构针对发布版本进行了优化。在开发期间使用这个默认值会增加您的构建时间,因为Unity为所选的每个目标体系结构构建二进制文件:

  • android播放器设置中的默认目标体系结构是armv7和带有il2cpp和mono脚本后端的x86。
  • iOS播放器设置中的默认体系结构是ARMV7和带有IL2CPP脚本后端的ARM64。

Code stripping in Unity

代码大小直接影响磁盘空间和运行时内存。Unity从代码库中删除您不使用的任何代码路径是很重要的。Unity在构建过程中自动删除代码,在两个不同的级别上工作:

  • 托管代码剥离
  • 本机代码剥离

Managed code stripping

Unity根据方法级别剥离托管代码。要更改剥离级别,请转到Player Settings窗口,向下滚动到Other Settings部分,找到Stripping Level 下拉菜单并选择 Strip Assemblies

UnityLinker从中间语言(IL)中删除未使用的类型(类、结构等)。即使使用类型,UnityLinker也会删除该类型未使用的方法。

注意:尽管此功能在使用Mono的版本上是可选的,但它始终在使用IL2CPP脚本后端的版本上启用。

Native code stripping

Unity在Player Settings 中默认启用Strip Engine Code ,并启用native code stripping.。启用Strip Engine Code以删除本机Unity引擎代码中未使用的模块和类。禁用Strip Engine Code 以保留本机Unity引擎代码中的所有模块和类。

注意:对于公开可用的平台,native code stripping 剥离仅在iOS、WebGL和Android上受支持。

Unity 2017.3以后的版本支持Android上的native code stripping ;在以前的版本中,Unity Android运行时作为预链接的.so库提供,Unity无法剥离。2017.3发布的Android运行时是一个静态引擎代码库,允许native code stripping。最后一个链接发生在构建过程中,这最终解释了稍微长一点的构建时间。

Unity Module Stripping

注意:WebGL目前是唯一支持剥离未使用的Unity模块的平台。

Unity尽最大努力消除所有未使用的Unity模块。这意味着,如果任何场景使用或任何脚本引用了构建中包含的Unity模块中的组件,Unity不会剥离该模块。Unity不剥离核心模块,如Camera、AssetBundle、Halo等,但在未来的版本中,Unity也会剥离这些模块。

从WebGL上的空项目中剥离模块
移除模块可以节省大量内存。例如,Unity中最大的模块之一是物理模块,它占gzip aped asm.js代码的大约5MB。如果删除一个空项目的物理模块,它会将构建大小从17MB减少到12MB。

C# Code Stripping

UnityLinker基于一个基本的标记和扫描原则工作,类似于垃圾收集器。UnityLinker从构建中构建包含在每个程序集中的每个类型和方法的映射。UnityLinker将许多类型和方法标记为“根”,然后UnityLinker将遍历类型和方法之间的依赖关系图。

例如,如果一个类型的方法对另一个类型调用方法,则Unity链接器将所调用的类型和方法标记为正在使用中。一旦UnityLinker标记了所有根的依赖项,系统就会重写程序集,省略未标记为已使用的方法或整个类型。

Roots from Scenes, Resources, Assemblies, and AssetBundles

UnityLinker将其内部类标记为根(如果它们已在场景中使用或来自资源中的内容)。同样,UnityLinker将用户程序集中的所有类型和方法标记为根。

如果直接在场景或资源中包含的资源中使用来自其他程序集的类型和方法,则Unity将这些类型和方法标记为根。

使用link.xml文件将其他类型和方法标记为根。如果您的项目使用assetbundles,请使用buildplayeroption.assetbundlemanifestpath将其他类型和方法标记为根。

User Assemblies

用户程序集是Unity从assets文件夹中的松散代码生成的程序集。Unity将大部分C代码放在Assembly-CSharp.dll中;而Unity将 /Assets/Standard Assets/或 /Assets/Plugins/ 中的代码放在Assembly-CSharp-firstpass.dll中,这也被视为用户程序集。

如果代码基的类型或方法中有很大一部分未使用,则可以通过将稳定代码迁移到预构建的程序集中并允许UnityLinker除去它们来节省一些二进制大小和构建时间。使用 Assembly Definition Files 将稳定代码迁移到预构建的程序集中。

Generic Sharing

对于引用类型,IL2CPP生成可以使用引用类型在泛型之间共享的实现(C++代码)。但是,IL2CPP不共享值类型,因为IL2CPP需要分别为每个类型生成代码。这会导致代码大小增加。

一般来说,不应该有任何明显的性能差异,但这取决于具体的用例以及它应该针对什么进行优化。类通常在堆上,而结构在堆栈上(除了一些异常,例如协程)。对于内存性能和使用来说,这很重要,使用非引用类型会导致其他问题。必须使用值类型复制函数参数以影响性能。有关更多信息,请参阅此blog post了解更多信息。尽管此时不共享整数或枚举类型,但仍要发出警告。

Assembly Definition Files

程序集定义文件允许您定义自定义托管程序集,并根据每个文件夹为其分配用户脚本。

反过来,这将导致更快的迭代时间,因为Unity将只构建那些实际受脚本更改影响的程序集。

注意:虽然多个程序集确实允许模块化,但它们也会增加应用程序的二进制大小和运行时内存。测试表明,每个程序集的可执行文件可以增长多达4KB。

Build Report

构建报告是一个包含在Unity中但还没有UI的API。构建一个项目会生成一个buildReport文件,该文件允许您发现被剥离的内容以及从最终可执行文件中剥离的原因。

预览剥离信息:

  1. 建立你的项目。
  2. 保持编辑器打开。
  3. 连接到http://files.unity3d.com/build report/。

构建报告工具连接到正在运行的Unity编辑器,下载并显示构建报告的细分。

可以对lLibrary/LatestBuild.buildreport 中生成的文件使用 binary2text工具查看报表中的数据。Binary2Text在Mac上的 Unity.app/Contents/Tools/或Windows上的Unity/Editor/Data/Tools/ 下随Unity一起提供。构建报告在Unity 5.5和更高版本中提供。

上一篇:记录面试过程中的2个算法知识


下一篇:unity il2cpp 设置问题