IL2CPP: Profiling and Debugging
Jonathan Chambers andJosh Peterson
Agenda
The Abridged Version
IL2CPP
Where is IL2CPP?
Creating IL2CPP
How do we make IL2CPP?
Fundamental principle: Test first• Unit tests - C++ and C# (~500)• Integration tests (~2000)• Platform integration tests (~1000)
Code conversion (il2cpp.exe) - C#Runtime (libil2cpp) - C++
How do we make IL2CPP?
Tools we use
• We debug with an IL2CPP standalone player
• We check build time and size against real projects
Internal tools
Internal organization of libil2cpp code
Internal Calls
Generated Code
PALVM
iOS
Windows
Other Platform
Our goal: move fast
Photo credit: Alvesgaspar (CC BY-SA 3.0)
Debugging script code with IL2CPP
Best option: Debug code in the editor
Acceptable option: Debug on device with Mono backend
Debugging script code with IL2CPP
Most fun option: Debug the generated C++ code!
When to debug generated C++ code
An exception occurs only on the device Marshaling
Interaction with native code
Deep dive: Debugging a managed exception in generated C++ code
• The converted code is the IL code, not C# code• Filter underscore characters from names• Sanity check often• Understand managed type layouts in native code
Principles for debugging generated code
• Debugging generated C++ code is probably not a technique to use often.
• Generated code will change in newer versions.• Generated C++ code can give Xcode problems.
Caveats
But for certain situations, this is a great skill to use!
Check out our IL2CPP Internals blog series!
Debugging tips for generated code - http://blogs.unity3d.com/2015/05/20/il2cpp-internals-debugging-tips-for-generated-code/
More information
Performance and Profiling
We make things fast for you!
The Good News
Make it small and make it fast!
Speed *and* Size
Make your code fast in general
.Net Profiler
Make your code fast in Unity
Unity Profiler
Make your code fast on device
Native Profiler
var md5 = MD5.Create(); var values = new byte[1024*1024]; var random = new Random(100); random.NextBytes(values); TimeAction (() => md5.ComputeHash(values));
Example
MD5 Hash
0ms
25ms
50ms
75ms
100ms
iPod 4G
Unity Mono IL2CPP
MD5 Hash
0ms
25ms
50ms
75ms
100ms
iPod 4G iPhone 6
Unity Mono IL2CPP
struct Vector2 { public float X; public float Y; public Vector2(float x, float y) { X = x; Y = x; } public float Magnitude() { return (float)Math.Sqrt(X*X + Y*Y); } }
Example 2
const int kNumItems = 1000000; var vals = new Vector2[kNumItems]; for (var i = 0; i < values.Length; i++) values[i] = new Vector2(i, values.Length - i); TimeAction (() => Array.Sort (vals, new Vector2Comparer ()));
Example 2 - cont’d
class Vector2Comparer : IComparer { public int Compare(object x, object y) { var v1 = (Vector2) x; var v2 = (Vector2) y; var mag1 = v1.Magnitude(); var mag2 = v2.Magnitude(); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; } }
IComparer
Instruments
class Vector2Comparer_v2 : IComparer<Vector2>{ public int Compare(Vector2 v1, Vector2 v2) { var mag1 = v1.Magnitude(); var mag2 = v2.Magnitude(); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; } }
IComparer<T>
Interface - iPod 4G
0s
10s
20s
30s
40s
IComparer IComparer<T>
IL2CPP
static int CompareVector2 (Vector2 v1, Vector2 v2){ var mag1 = v1.Magnitude(); var mag2 = v2.Magnitude(); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; }
Delegate v1
static int CompareVector2_v2(Vector2 v1, Vector2 v2){ var mag1 = Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y); var mag2 = Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; }
Delegate v2
Delegate Optimization? - iPod 4G
0s
2s
4s
6s
8s
Method Call Inline
IL2CPP
static int CompareVector2_v2(Vector2 v1, Vector2 v2){ var mag1 = Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y); var mag2 = Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; } // Our function we inlined public float Magnitude() { return (float)Math.Sqrt(X*X + Y*Y); }
Delegate v2 - What’s going on?
static int CompareVector2_v2(Vector2 v1, Vector2 v2){ var mag1 = (float)Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y); var mag2 = (float)Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y); if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; }
Delegate v2 - Corrected
Delegate - iPod 4G
0s
2s
4s
6s
8s
Method Call Inline Inline Fixed
IL2CPP
static int CompareVector2_v3(Vector2 v1, Vector2 v2){ var mag1 = v1.X * v1.X + v1.Y * v1.Y; var mag2 = v2.X * v2.X + v2.Y * v2.Y; if (mag1 > mag2) return 1; if (mag1 < mag2) return -1; return 0; }
Delegate v3
Delegate - iPod 4G
0s
1.5s
3s
4.5s
6s
Method Call Inline No Sqrt
IL2CPP
Vector2 Sort by Magnitude - iPod 4G
0s
35s
70s
105s
140s
IComparable IComparable<T> Delegate 1 Delegate 2 Delegate 3
Unity Mono IL2CPP
Two More Things
Fast and Unsafe
public float Magnitude() { Debug.Log ("Let's slow things down!"); return (float)Math.Sqrt(X*X + Y*Y); }
One Time Initialization
public float Magnitude() { Debug.Log ("Let's slow things down!"); return (float)Math.Sqrt(X*X + Y*Y); }
// Generated C++ prologue static bool s_Il2CppMethodIntialized; if (!s_Il2CppMethodIntialized) { _stringLiteral0 = il2cpp_codegen_string_literal_from_index(0); s_Il2CppMethodIntialized = true; }
One Time Initialization
Thank you!
@jon_cham@petersonjm1
Questions?
Top Related