UnityにおけるSLua,Tolua,XLua,ILRuntime効率評価

17314 ワード

Unityスクリプト効率評価


SLua、Tolua、XLua、ILRuntimeの4つのスクリプトプラグインの効率テストを行い、フレームワークスクリプトを選択します.本プロジェクト: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を設計し,シナリオ効率(JITと非JIT)をそれぞれ以下の3つの側面から考察し,実験結果は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

Test 11はMono呼び出しスクリプト中空メソッドであり、Test 12~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の2倍程度であるため効率が高いことが分かる.変数読み取り読者は自分で比較することができ、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はTest 2の中でSLua、Toluaに比べて1つ以上の数量級を差出して、主にToluaとSLuaがUnityの値のタイプの変数に対してluaの実現をしたためで、このような値のタイプのSLuaとToluaの中で1つのtableで、XLuaの中で1つのUserdataで、だからSLuaとToluaはTest 2をする時Unityと対話していない(JITの結果からも分かるように、JITはC関数を処理できないので、JIT後Test 2の効果の向上は明らかである)が、XLuaは頻繁にUnityと対話し、効率の消耗は明らかである.対象型の変数については,3種類のlua処理機構が同一であるが,内部実装の詳細が異なるだけであり,詳細は本論文で議論する範囲内ではなく,実験データから見るとToluaの内部実装がより効率的である.lua+unityをよく使って、性能を飛ばします——luaとc#のインタラクティブな編、この文章はC#とLuaのインタラクティブなもとに対してとても詳しい説明があって、プラグインの後続は改善がありますが、核心の思想は依然として変わりません.
ILRuntime解析:データ的にILRuntime解釈器の効率はluaよりはるかに遅くないが,Vector 3のような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は全てLuaJIT 2を使用するため.1.0 B 2バージョンなので、スクリプト自体の効率は理論的には一致しているはずで、データ的にも悪くありません.実験の結果、主にLua解釈器の速度がILRuntimeより明らかに優れていることが明らかになった(言語内部の実現は異なり、無理に比較すると、結局luaはcが書いたものである).また、LuaJITの効率向上にもいくつかの桁があり、LuaJITには多くの穴があるが、うまく使えるか最適化器であることが分かった.

まとめ


総合的に見ると、Toluaは現在より効率的なUnity Luaソリューションであり、その後、Toluaの内部実現をさらに分析し、これまでより効率を最適化してきた.