当前位置: 首页>前端>正文

tolua和xlua tolua和xlua哪个好

Unity脚本效率评测

对SLua、Tolua、XLua和ILRuntime四个脚本插件进行效率测试,对框架脚本进行选型。 本文项目:https://github.com/cateatcatx/UnityScriptPTest tolua:https://github.com/topameng/tolua slua:https://github.com/pangweiwei/slua xlua:https://github.com/Tencent/xLua ILRuntime:https://github.com/Ourpalm/ILRuntime

用例版本

Unity5.6.0p3
SLua 1.3.2
Tolua# github 2017/4/25 19:08:37
XLua 2.1.7
ILRuntime 1.11
Lua: luajit-2.1.0-beta2

测试环境

Smartian T2、Win7(64bit)

实验方案

总共设计了17个Test,分别从以下3个方面来考察脚本效率(JIT和非JIT),实验结果取10次的平均值,时间单位为ms。通过实验数据简单分析原因(Lua插件会横向对比,ILRuntime会单独考虑,因为毕竟C#和Lua本身差别较大)。

1. Mono -> Script,Mono调用脚本
    2. Script -> Mono,脚本调用Mono
    3. Script自身,脚本自身执行效率

Mono -> Script

Test11

Test12

Test13

Test14

Test15

Test16

Sum

Tolua

186

449

598

407

577

759

2978

SLua

315

751

901

757

1253

1883

5863

XLua

145

924

1010

573

1507

1929

6091

ILRuntime

711

422

368

379

397

393

2672

Tolua(JIT)

168

454

592

416

578

826

3037

SLua(JIT)

384

842

956

824

1328

3439

7775

XLua(JIT)

189

957

1047

608

1540

1700

6043

ILRuntime(JIT)

1232

892

903

969

1175

1102

6275


Lua分析:

-- Test11
function EmptyFunc()
    _V0 = _V0 + 1
end

_V0 = 1 -- Test12
_V1 = "12345" -- Test13
_V2 = GameObject.New() -- Test14
_V3 = Vector3.New(1, 2, 3) -- Test15
_V4 = {1, 2, 3} -- Test16

Test11为Mono调用脚本中空方法,Test12~16为测试Mono到脚本中取变量(ILRuntime代码类似,访问类的static函数与变量)。ILRuntime因为没有变量类型的转换所以效率最为优秀(JIT模式下使用的是Mono的反射取值所以会更慢,ILRuntime内部可能对类型变量有缓存,所以比反射快很多),Lua中可以看到Tolua的综合性能尤为突出(所有测试均好于其他Lua)。
对比Tolua和SLua的实现发现,Tolua会尽量减少与C++通信的次数,因为c#与c++通信会有一定效率损耗(参数的Marshaling等),虽然是Mono与Lua通信,但是其中还夹着一层C++,所以Mono与Lua通信的主要优化思路就是减少与C++的通信,从实验数据来看Tolua的这种优化效果是很明显的。

// SLua的函数调用
public bool pcall(int nArgs, int errfunc)
{

    if (!state.isMainThread())
    {
        Logger.LogError("Can't call lua function in bg thread");
        return false;
    }

    LuaDLL.lua_getref(L, valueref);

    if (!LuaDLL.lua_isfunction(L, -1))
    {
        LuaDLL.lua_pop(L, 1);
        throw new Exception("Call invalid function.");
    }

    LuaDLL.lua_insert(L, -nArgs - 1);
    if (LuaDLL.lua_pcall(L, nArgs, -1, errfunc) != 0)
    {
        LuaDLL.lua_pop(L, 1);
        return false;
    }
    return true;
}

// Tolua的函数调用
public void Call()
{
     BeginPCall();
     PCall();
     EndPCall();
}
public int BeginPCall(int reference)
{                        
    return LuaDLL.tolua_beginpcall(L, reference);
}

public int LuaPCall(int nArgs, int nResults, int errfunc)
{
    return LuaDLL.lua_pcall(L, nArgs, nResults, errfunc);
}

public void LuaSetTop(int newTop)
{
    LuaDLL.lua_settop(L, newTop);
}

对比SLua和Tolua代码的函数调用部分,发现SLua的C++调用多余Tolua两倍左右,所以效率高下立见。变量读取读者可以自行对比,总结就是Tolua通过减少C++调用的方式来优化效率,后期对Lua的进一步优化也要遵循这个思路。


ILRuntime分析:
实验发现解释执行下,ILRuntime的获取变量的效率要明显好于Lua,主要是因为都是C#对象,不需要进行类型转换。ILRuntime的JIT模式其实是用的Mono的,效率反而比解释执行更低,猜测是因为JIT下主要采用反射调用函数和取变量,而ILRuntime的解释器可能内部有缓存(因为没看过实现,都是不负责任猜测)。

Script->Mono

Test0

Test1

Test2

Test3

Test4

Test5

Test6

Test7

Test10

Sum

Tolua

675

797

1410

354

839

343

407

1681

915

7426

SLua

768

640

2305

466

1110

408

394

3379

1299

10774

XLua

590

648

8504

450

775

1213

695

1267

845

14989

ILRuntime

1152

1054

4012

315

998

437

1026

3272

1434

13703

Tolua(JIT)

612

701

7.4

357

823

318

37

128

884

3871

SLua(JIT)

732

679

5.4

465

1197

411

10

165

1307

4974

XLua(JIT)

636

668

8312

438

767

1303

734

1340

911

15113

ILRuntime(JIT)

72

197

84

260

402

33

219

303

77

1651


Lua分析:

function Test0(transform)   
    local t = os.clock()

    for i = 1,200000 do
        transform.position = transform.position
    end

    return os.clock() - t
end

function Test1(transform)           
    local t = os.clock()
    for i = 1,200000 do
        transform:Rotate(up, 1) 
    end

    return os.clock() - t
end

function Test2()    
    local t = os.clock()

    for i = 1, 2000000 do
        local v = Vector3.New(i, i, i)
        local x,y,z = v.x, v.y, v.z
    end

    return os.clock() - t
end

function Test3()
    local t = os.clock()    

    for i = 1,20000 do              
        GameObject.New()
    end

    return os.clock() - t
end

function Test4()    
    local t = os.clock()
    local tp = typeof(SkinnedMeshRenderer)

    for i = 1,20000 do              
        local go = GameObject.New()
        go:AddComponent(tp)
        local c = go:GetComponent(tp)
        c.receiveShadows=false
    end

    return os.clock() - t
end

function Test5()
    local t = os.clock()

    for i = 1,200000 do     
        local p = Input.mousePosition
        --Physics.RayCast
    end

    return os.clock() - t
end

function Test6()    
    local Vector3 = Vector3 
    local t = os.clock()

    for i = 1, 200000 do
        local v = Vector3.New(i,i,i)
        Vector3.Normalize(v)
    end

    return os.clock() - t
end

function Test7()        
    local Quaternion = Quaternion
    local t = os.clock()

    for i=1,200000 do
        local q1 = Quaternion.Euler(i, i, i)        
        local q2 = Quaternion.Euler(i * 2, i * 2, i * 2)
        Quaternion.Slerp(Quaternion.identity, q1, 0.5)      
    end

    return os.clock() - t
end

function Test10(trans)
    local t = os.clock()

    for i = 1, 200000 do
        UserClass.TestFunc1(1, "123", trans.position, trans)
    end 

    return os.clock() - t
end

总体效率还是Tolua胜出,其中XLua在Test2中较比SLua、Tolua差出不止一个数量级,主要是因为Tolua和SLua对于Unity的值类型变量做了lua的实现,这种值类型SLua和Tolua中是一个table,而在XLua中是一个Userdata,所以SLua和Tolua在做Test2的时候并没有跟Unity交互(从JIT结果也能看出来,JIT不能处理C函数,所以JIT后Test2效果提升明显),而XLua需要频繁和Unity交互,效率消耗明显。对于对象类型的变量,3种lua处理机制是雷同的,只是内部实现细节不一样而已,细节不再本文讨论范围内,从实验数据上来看,还是Tolua的内部实现更加效率。用好lua+unity,让性能飞起来——lua与c#交互篇,这篇文章对C#与Lua的交互原来有非常详细的说明,虽然插件后续有改进,但是核心思想还是不变的。


ILRuntime分析:
数据上来看ILRuntime解释器的效率还是很高的并不比lua慢太多,但是对于Vector3这种Unity值类型的处理跟lua差距比较大(主要是因为SLua和Tolua中的Unity值类型其实就是table,等于没有跟Unity交互)。ILRuntime还是一个很有潜力的Unity热更解决方案的,毕竟C#配合VS的开发效率还是比Lua高不少的。其中的JIT部分是Mono层的,跟本身的C#代码是没有区别的,不参与对比。

Script自身

Test8

Test9

Sum

Tolua

254

4246

4500

SLua

255

4766

5022

XLua

311

4506

4817

ILRuntime

852

79048

79900

Tolua(JIT)

46

371

417

SLua(JIT)

48

414

463

XLua(JIT)

40

469

510

ILRuntime(JIT)

222

313

536

function Test8()
    local total = 0
    local t = os.clock()

    for i = 0, 1000000, 1 do
        total = total + i - (i/2) * (i + 3) / (i + 5)
    end

    return os.clock() - t   
end

function Test9()
    local array = {}

    for i = 1, 1024 do
        array[i] = i
    end

    local total = 0
    local t = os.clock()

    for j = 1, 100000 do
        for i = 1, 1024 do
            total = total + array[i]
        end         
    end

    return os.clock() - t
end

因为Lua全部使用的是LuaJIT2.1.0B2版本,所以其实脚本自身的效率理论上应该是一致的,从数据上看也差不多。实验结果上主要体现了Lua解释器的速度要明显好于ILRuntime(语言内部实现不一样,勉强对比在一块,毕竟lua是c写的),并且发现LuaJIT对效率的提升也是好几个数量级,虽然LuaJIT很多坑,但是如果能用好还是个优化利器。

总结

综合来看Tolua是现在效率较好的Unity Lua解决方案,后续会对Tolua的内部实现做进一步剖析,从来做进一步的效率优化。



https://www.xamrdz.com/web/2tq1938846.html

相关文章: