XLua 与 ILRuntime 性能测试

首先。。今天是个好日子,因为可以

好了,进入正题
现在很多项目都使用xlua来开发整个项目,但是实际上使用的并不是xlua标榜的“热修复”,毕竟国内游戏还是要要求可以热更新新功能的,因此如果采用热修复的方案,则需要小版本使用lua写功能,大版本又要把lua版本转换为C#代码重写一次,不太现实,因此现实中的许多公司都是使用lua来写大部分的逻辑。

但是u3d是个C#语言为编程语言的引擎(当然还有JS…),一般lua项目中,我们会把逻辑运算量大,复杂度高,对性能有要求的代码写在C#代码,或者C++ DLL库中,比如加载,更新,框架,战斗等, 这就需要程序要经常同时使用C#和lua写代码,容易人格分裂。

在Ilruntime 1.3版本之后,有稳定的调试插件(通过tcp 连接,因此可以真机调试),值绑定等功能后 也成为一个不错选择,程序员不需要更换语言来编写项目。至于它的局限性,对比lua来说都是半斤八两,比如主工程的泛型类无法导出,常用值类型需要生成wrap等(否则会有严重性能问题)。

目前很多人对Ilruntime 的看法有2点。 1,使用的项目比较少,未预见的坑比较多。 2,性能比较差,毕竟lua 有Jit, 在支持Jit的设备上是接近c的性能,大部分的性能损耗在接口交互上,而Ilruntime 是自己实现了一套解释器,是C#编写的,原生性能较差。 因此我打算做一个性能测试,看看真实的情况是什么。

使用的ILruntime库地址
https://github.com/Ourpalm/ILRuntime
使用的Xlua库地址
https://github.com/Tencent/xLua

注:Ilruntime 已经设置全局宏 DISABLE_ILRUNTIME_DEBUG, 并且hotfix项目为Release, 生成了Vecto3_Binding
Xlua 生成了 Vector3_Wrap
.Net 3.5版本下

测试3种情况下的性能情况

Test1 测试U3d内部值计算

ILRuntime:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void Test1()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < 1000000; i++)
            {
                Vector3 a = new Vector3(1, 2, 3);
                Vector3 b = new Vector3(4, 5, 6);
                Vector3 c = a + b;
            }
            sw.Stop();
            UnityEngine.Debug.Log("il test1:" + sw.ElapsedMilliseconds);
        }

Xlua:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 void LuaTest1()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();

        env.DoString(@"

        for i = 0, 1000000, 1 do

        local a = CS.UnityEngine.Vector3(1,2,3)

        local b = CS.UnityEngine.Vector3(4,5,6)

        local c = a + b

        end

        "
);
        sw.Stop();

        Debug.Log("lua test1:" + sw.ElapsedMilliseconds);
    }

Test2 测试库与主项目中的函数调用 其中lua分2种,一种是lua内部创建function, 一种是使用 [LuaCallCsharp]调用工程中的函数

ILRuntime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void Test2()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 1000000; i++)
            {
                Vector3 a = new Vector3(1, 2, 3);
                Vector3 b = new Vector3(4, 5, 6);
                Add(a, b);
            }
            sw.Stop();
            UnityEngine.Debug.Log("il test2:" + sw.ElapsedMilliseconds);
        }

public void Add(Vector3 a, Vector3 b)
        {
            Vector3 r = a + b;
        }

Xlua

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
void LuaTest2()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
 
        env.DoString(@"

        local c = function(o, x)
            local r = o + x
        end

        for i = 0, 1000000, 1 do

        local a = CS.UnityEngine.Vector3(1,2,3)

        local b = CS.UnityEngine.Vector3(4,5,6)

        c(a,b)

        end

        "
);
        sw.Stop();

        Debug.Log("lua test2(luacalllocal):" + sw.ElapsedMilliseconds);

        sw.Reset();
        sw.Start();

        env.DoString(@"

        local c = CS.LuaCallTest
        local co = c()

        for i = 0, 1000000, 1 do

        local a = CS.UnityEngine.Vector3(1,2,3)

        local b = CS.UnityEngine.Vector3(4,5,6)

        co:LuaAdd(a, b)

        end

        "
);
        sw.Stop();

        Debug.Log("lua test2(luacallcs):" + sw.ElapsedMilliseconds);
    }

C#调用函数
[LuaCallCSharp]
public class LuaCallTest
{
    public void LuaAdd(Vector3 a, Vector3 b)
    {
        Vector3 c = a + b;
    }
}

Test3 测试库系统值运算

IlRuntime

1
2
3
4
5
6
7
8
9
10
11
12
13
public void Test3()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 1000000; i++)
            {
                int a = 1;
                int b = 2;
                int c = (a + b) / b * a;
            }
            sw.Stop();
            UnityEngine.Debug.Log("il test3:" + sw.ElapsedMilliseconds);
        }

XLua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void LuaTest3()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();

        env.DoString(@"

        for i = 0, 1000000, 1 do

        local a = 1

        local b = 2

        local c = (a + b) / b * a

        end

        "
);
        sw.Stop();

        Debug.Log("lua test3:" + sw.ElapsedMilliseconds);
    }

使用真机测试 Samsung S8E Android:8.0

耗时:ms
[supsystic-tables id=’1′]

通过对比发现 ILRuntime 无论是直接使用主工程值类型计算,还是调用主工程函数,性能都比Xlua快1.3-2倍。 但是Test3部分的性能被xlua碾压,说明在系统值计算的时候,lua可以利用jit获得近视于native的性能, 而ilruntime必须通过CLR绑定来在C#层面计算。

顺便测试IL2CPP条件下的性能数据
[supsystic-tables id=’2′]

ILRuntime 在Il2cpp 下应该是自己写的编译器被负优化了, 而Xlua的bridge代码则因为变成cpp性能得到提升。在大规模的值计算ILruntime慢了1.05倍,但是跨域函数调用依然是领先,系统值计算依然是短板。

正题来说作为一个热更新框架Ilruntime 的表现还是不错的,而且在解释器和Binding代码上还有大量的优化空间,其中Binding里有大量计算sizeOf(StackObject)的函数,优化后可以提升80-100ms, 期待作者的更新。

2 评论

  1. test3 里,你把除法改为+或者*之类,会发现ILRuntime比xlua慢了好多倍。原因很简单, / 实在有点慢,导致 两者之间的性能差距减少了。
    而且你应该在一个循环里放入多个表达式,不然时间都花了循环上了。
    像这样或者更合理
    for( …. ) {
    a = a + b;
    b = a * b;
    a = b – a;
    ….
    }

    1. 而且, 你在c#里是 整数 除法,在lua里却是浮点除法。这两者速度差了好几倍呢。 而浮点除法又比整数相加之类慢了十几二十倍。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.