IL2CPP & Mono
On iOS and Android, choose between the Mono or IL2CPP Scripting Backends in Player Settings. To change the Scripting Backend, go to the Player Settings window (menu: Edit > Project Settings > Player), scroll down to the Other Settings section, and select either Mono or IL2CPP from the Scripting Backend drop-down menu.
Note: As of 2017.3, choose either the IL2CPP Scripting Backend or the Mono Scripting Backend. However, both WebGL and UWP only support IL2CPP. iOS still supports the Mono Scripting Backend for fast iteration, but you cannot submit Mono (32-bit) application to Apple anymore.
Each Scripting Backend has benefits and drawbacks that should influence your decision on which is the right choice for your situation:
Code generation is heavily improved compared to Mono.
Debugging Script code in C++ from top to bottom is possible.
You can enable Engine code stripping to reduce code size.
Build times are longer than with Mono.
Faster build times than IL2CPP.
Supports more managed libraries due to Just In Time compilation (JIT).
Supports runtime code execution.
Must ship managed assemblies (.dll files that mono- or .net- produces).
Tip: You should use IL2CPP to both develop and ship your Project. If iteration times end up being too slow using IL2CPP, switch temporarily to the Mono Scripting Backend during development to improve iteration speed.
Note: The default target architectures in the Player Settings are optimized for release builds. Using this default during development increases your build time because Unity builds the binary for each target architecture selected:
- The default Target Architecture in the Android Player Settings are armv7 and x86 with the IL2CPP and Mono Scripting Backend.
- The default Architecture in the iOS Player Settings are armv7 and arm64 with the IL2CPP Scripting Backend.
Code size has a direct influence on disk space and runtime memory. It’s important that Unity removes any code paths you aren’t using from the code base. Unity strips code automatically during a build, working on two different levels:
Managed code stripping
Native code stripping
Unity strips managed code at the method level. To change the Stripping Level, go to the Player Settings window, scroll down to the Other Settings section, locate the Stripping Level drop-down menu and select Strip Assemblies .
The UnityLinker removes unused types (classes, structs, etc.) from the Intermediate Language (IL). Even if you use a type, the UnityLinker removes the type’s unused methods.
Note: Although this functionality is optional on builds using the Mono Scripting Backend, it is always enabled on builds using the IL2CPP Scripting Backend.
Unity enables Strip Engine Code by default in the Player Settings and enables native code stripping. Enable Strip Engine Code to remove unused modules and classes in the native Unity engine code. Disable Strip Engine Code to preserve all of the modules and classes in the native Unity engine code.
Note: For publically available platforms, native code stripping is only supported on iOS, WebGL, and Android.
Unity 2017.3 onwards supports native code stripping on Android; in prior versions, the Unity Android runtime shipped as a pre-linked .so library, which Unity could not strip. The Android runtime shipped in 2017.3 is a static engine code library, allowing for native code stripping. The final link happens during the build, which is ultimately what accounts for the slightly longer build times.
Note: WebGL is currently the only platform which supports stripping unused Unity modules.
Unity makes the best attempt to eliminate all unused Unity modules. This means if any Scene uses or any script references a component from a Unity module you include in the build, Unity doesn't strip the module. Unity doesn’t strip core modules, such as Camera, AssetBundle, Halo, etc. but in future releases, Unity strips these too.
Removing modules saves a substantial amount of memory. For example, one of the largest modules in Unity is the physics module, which accounts for about 5MB of gzipped ASM.js code. If you remove the physics module of an empty Project it reduces the build size from 17MB to 12MB.
The UnityLinker works on a basic mark and sweep principle, similar to a garbage collector. The UnityLinker builds a map of each type and method included in each assembly from a build. The UnityLinker marks a number of types and methods as "roots" and the UnityLinker then walks the graph of dependencies between types and methods.
If, for example, one type’s method calls a method on another type, then the Unity Linker marks the called type and methodas in-use. Once the UnityLinker marks all the roots’ dependencies, the system reweaves the assemblies, omitting methods or entire types that are not marked as used.
The UnityLinker marks its internal classes as roots if they’ve been used in a Scene or from content in Resources. Similarly, the UnityLinker marks all types and methods in the user assemblies as roots.
If you use types and methods from other assemblies directly in a Scene or in an Asset you include in resources, Unity marks these as roots.
Use the link.xml file to mark additional types and methods as roots. If your Project uses AssetBundles, use the BuildPlayerOption.assetBundleManifestPath to mark additional types and methods as roots too.
User Assemblies are the assemblies Unity generates from loose code within the Assets folder. Unity places most of the C# code in Assembly-CSharp.dll; whereas Unity places code in /Assets/Standard Assets/ or /Assets/Plugins/ in Assembly-CSharp-firstpass.dll, which is also considered a user assembly.
If a significant proportion of a codebase’ types or methods are unused, you could save some binary-size and build time by migrating stable code into pre-built assemblies and allowing the UnityLinker to strip them. Use Assembly Definition Files to migrate stable code into pre-built assemblies.
For reference types, IL2CPP generates the implementation (C++ code) which can be shared between Generics using reference types. However, IL2CPP doesn't share value types because IL2CPP needs to generate the code for each of the types separately. This results in your code size increasing.
In general, there should not be any noticeable performance difference, but it depends on the specific use-case and what it should be optimized for. Classes are usually on the heap while structs are on the stack (with some exceptions, such as in the case of coroutines). For memory performance and usage this matters, and using non-reference types leads to other problems. You must copy function parameters using value types to influence performance. For additional information see this blog post for more information. Be warned though that Integer or Enum types are not shared at this moment.
Assembly Definition Files allow you to define custom managed assemblies and assign user Scripts to them on a per-folder basis.
In turn, this results in faster iteration times, because Unity will only build those assemblies actually affected by script changes.
Note: While multiple assemblies do grant modularity, they also increase the application’s binary size and runtime memory. Tests show that the executable can grow by up to 4kB per assembly.
Build Report is an API which is included in Unity but has no UI yet. Building a Project generates a buildreport file which lets you discover what is stripped and why it was stripped from the final executable.
To preview the stripping information:
Build your Project.
Leave the Editor open.
Connect to http://files.unity3d.com/build-report/.
The Build Report tool connects to your running Unity Editor, downloads and presents the breakdown of the build report.
It’s possible to use the binary2text tool on the generated file in Library/LatestBuild.buildreport to view data from the report. Binary2text is shipped with Unity under Unity.app/Contents/Tools/ on Mac or Unity/Editor/Data/Tools/ on Windows. The build report is available in Unity 5.5 and later.