Unity IL2CPP - performance fixes

Yes, IL2CPP still much slower than MONO in common cases. Can we decrease this performance penalty somehow?

Yes, IL2CPP still much slower than MONO in common cases. Can we decrease this performance penalty somehow?

Partially, yes. Official docs says that we can disable some checks for IL2CPP code generation. So, time to check it as possible optimization for my ECS framework.

Test code (without IL2CPP optimizations)

Why it tested in this way you can read at “Performance tests of Event, ActionList, Observer, InterfaceObserver“.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System.Collections;
using UnityEngine;
namespace LeopotamGroup.Ecs.Tests {
class Test8 : MonoBehaviour, IEcsInitSystem {
class C8_1 { }
class C8_2 { }

EcsWorld _world = null;

IEnumerator Start () {
yield return new WaitForSeconds (2f);
var systems = new EcsSystems (new EcsWorld ()).Add (this);
systems.Initialize ();
systems.Destroy ();
}

void IEcsInitSystem.Initialize () {
const int TESTS = 40;
const int T = 10000000;
var sw = new System.Diagnostics.Stopwatch ();
var result = 0;
var entity = _world.CreateEntity ();
_world.AddComponent<C8_1> (entity);
_world.AddComponent<C8_2> (entity);
_world.ProcessDelayedUpdates ();

for (var test = 0; test < TESTS; test++) {
sw.Reset ();
sw.Start ();
for (var i = 0; i < T; i++) {
_world.GetComponent<C8_1> (entity);
_world.GetComponent<C8_2> (entity);
}
sw.Stop ();
result += sw.Elapsed.Milliseconds;
}
Debug.LogFormat ("GetComponent: {0}", result / (float) TESTS);
}

void IEcsInitSystem.Destroy () { }
}
}

Test code (with IL2CPP optimizations)

There is only one difference - new attribute for test class (and all ECS internal classes):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System.Collections;
using UnityEngine;

// Unity IL2CPP performance optimization attribute.
namespace Unity.IL2CPP.CompilerServices {
enum Option {
NullChecks = 1,
ArrayBoundsChecks = 2
}

[AttributeUsage (
AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property,
Inherited = false,
AllowMultiple = true)]
class Il2CppSetOptionAttribute : Attribute {
public Option Option { get; private set; }
public object Value { get; private set; }

public Il2CppSetOptionAttribute (Option option, object value) {
Option = option;
Value = value;
}
}
}

namespace LeopotamGroup.Ecs.Tests {
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
class Test8 : MonoBehaviour, IEcsInitSystem {
class C8_1 { }
class C8_2 { }

EcsWorld _world = null;

IEnumerator Start () {
yield return new WaitForSeconds (2f);
var systems = new EcsSystems (new EcsWorld ()).Add (this);
systems.Initialize ();
systems.Destroy ();
}

void IEcsInitSystem.Initialize () {
const int TESTS = 40;
const int T = 10000000;
var sw = new System.Diagnostics.Stopwatch ();
var result = 0;
var entity = _world.CreateEntity ();
_world.AddComponent<C8_1> (entity);
_world.AddComponent<C8_2> (entity);
_world.ProcessDelayedUpdates ();

for (var test = 0; test < TESTS; test++) {
sw.Reset ();
sw.Start ();
for (var i = 0; i < T; i++) {
_world.GetComponent<C8_1> (entity);
_world.GetComponent<C8_2> (entity);
}
sw.Stop ();
result += sw.Elapsed.Milliseconds;
}
Debug.LogFormat ("GetComponent: {0}", result / (float) TESTS);
}

void IEcsInitSystem.Destroy () { }
}
}

Results

  • MONO runtime: 168ms
  • IL2CPP runtime without optimizations: 293ms
  • IL2CPP runtime with optimizations: 194ms

Looks good enough (30% boost), but still slower than MONO version.