Unity项目常见Lua解决方案性能比较

2016年09月28日 10:36 0 点赞 0 评论 更新于 2025-11-21 13:35
Unity项目常见Lua解决方案性能比较

测试说明

Unity不支持热更新一直是一个令人困扰的痛点。作者在第一个项目上线后,深刻体会到每次更新的代价巨大,然而官方路线图中却迟迟未推出热更新功能。

UWA此前分享过Android平台热更新解决方案,直接替换dll是一种可行方式,但在iOS上由于使用IL2CPP而无法实现,这本质上是商业原因而非技术问题。另一个常见的解决方案是使用Lua。考虑到作者的不少同事之前有Cocos2d - lua开发经验,且有对应的后端框架如Skynet,因此在立项时,作者考虑采用Unity + Lua的方式进行开发。

测试内容

本次测试结果包含以下三种解决方案的比较:

  • slua, commit #ef57252
  • ulua, commit #dbe98bc(作者已不再维护,转至tolua)
  • tolua, commit #710dedc

注意事项

  1. 当然还存在其他解决方案,如CsTolua,但由于作者精力有限,无法一一测试,欢迎提交Pull Request。本次测试将slua和tolua版本升级到最新(ulua原本就是最新)。
  2. 为避免不必要的争端,强调本次测试仅考虑解决方案本身的性能差异。对于有人提出“ulua在iOS下面用的是lua原生vm,跟slua用的luajit有啥好比的?”的疑问,如果有人愿意提供luajit版本,作者很乐意从使用者角度再进行测试。
  3. benchmark中的数据是作者测试的真实结果,仅供参考。而且slua/ulua在OSX编辑器下有几个结果比较异常,若有知道原因的朋友,还望不吝告知。
  4. 作者以slua的Test Case为范本,具体代码请参考slua Perf文件,然后在其他两个项目中复制了一遍。每个测试都统计了lua的os.clock()时间差、C#的Profiler.BeginSample()和Profiler.EndSample()。

测试环境

  1. UWA在原作基础上,对最新的Lua版本用更多机型(iPhone 4s、iPhone 5s)进行了测试作为补充,同时对原部分数据进行了修正。所使用的测试代码和原作一样,依然是6个测试用例。测试方法也相同,每个测试顺序执行五次,然后重启。
  2. 目前UWA只测试了目前使用最广泛的移动平台(Android和iOS)。

测试代码

Test1

function test1()
local transform = cube.transform
local start = os.clock()
for i=1,200000 do
transform.position=transform.position
end
print("test1/lua " .. ((os.clock() - start) * 1000));
end

Test2

function test2()
local transform=cube.transform
local start = os.clock()
for i=1,200000 do
transform:Rotate(Vector3.up, 90)
end
print("test2/lua " .. ((os.clock() - start) * 1000));
end

Test3

function test3()
local start = os.clock()
for i=1,2000000 do
local v = Vector3(i,i,i)
Vector3.Normalize(v)
end
print("test3/lua " .. ((os.clock() - start) * 1000));
end

Test4

function test4()
local t = cube.transform
local v = Vector3.one
local start = os.clock()
for i=1,200000 do
local v = GameObject()
end
print("test4/lua " .. ((os.clock() - start) * 1000));
end

Test5

function test5()
local v = cube.transform.position
local start = os.clock()
for i=1,20000 do
local v = GameObject()
v:AddComponent(SkinnedMeshRenderer)
local c=v:GetComponent(SkinnedMeshRenderer)
c.shadowCastingMode=0
c.receiveShadows=false
end
print("test5/lua " .. ((os.clock() - start) * 1000));
end

Test6

function test6()
local transform=cube.transform
local start = os.clock()
for i=1,200000 do
local t=Quaternion.Euler(100,100,100)
local q=Quaternion.Slerp(Quaternion.identity,t,0.5)
end
print("test6/lua jit  " .. ((os.clock() - start) * 1000));
end

Android平台测试结果

以下为UWA在Android上对高、中、低配置的三款设备进行测试后得到的平均数据。图中下方的表格部分为柱状图的准确数值,其数值表示完成测试用例所需的时间,单位为毫秒。

  1. 低端设备:三星S3(Android OS 4.3) 注:红色部分的数值已远超其他的测试数据,因此UWA进行了截断显示,具体数值可见下方的表格。
  2. 中端设备:红米Note2(Android OS 5.0.2)
  3. 高端设备:三星S6(Android OS 6.0.1)

iOS平台测试结果

以下为UWA在iOS上对armv7和arm64的两款设备进行测试后得到的平均数据。测试中使用了il2cpp+Universal的发布方式,同时禁用了bitcode。图中下方的表格部分为柱状图的准确数值,其数值表示完成测试用例所需的时间,单位为毫秒。

  1. armv7设备:iPhone 4s (OS 7.1.2)
  2. arm64设备:iPhone 5s(OS 9.3.5)

总结

  1. 在Android设备上,slua在Test 2和3上表现较为出色,这两个测试用例主要涉及大量的向量操作。而tolua在Test 1、4和5上较为占优,这三个测试主要是组件属性的赋值和GameObject的创建等操作。ulua在中高端设备上的性能基本介于两者之间,但在低端机上有较为明显的性能问题。
  2. 在iOS设备上,tolua的性能表现最佳,ulua次之,slua的性能较tolua和ulua稍逊一筹。其中,Test3中slua的向量操作在iOS设备上耗时较高。目前暂时无法给出原因,但在多款iOS设备上测试均得到相似结论,值得研发团队注意。

以上是对目前市面上主流Lua版本的性能分析,希望对大家在Lua版本的选择和使用上有所帮助。需要说明的是,框架的选择不应仅考虑性能,还需考虑可维护性、稳定性等其他维度。本次测试仅供性能层面的参考,如有相关疑问,可加入相关技术QQ群交流讨论:446956587。