前面写了xlua的值类型优化,个人觉得还有优化空间(设想),并做了初步实验。(PushAsTable不在考虑,空间又大又慢)
lua层计算上,广泛的来说,有两种写法。一种是直接使用cs层的Vector,另一种是在lua层创建自己的table。下面从多维度对比这两种vector的性能。
创建
cs层的vector3
CS.UnityEngine.Vector3可以先缓存起来,查表(rawget)不计入时间。Vector3类的元表会调用CS层的__CreateInstance方法(lua元表调用+跨语言调用),随后从栈中取出三个元素(3次跨语言调用),然后调用CS方法在CS层新建个vector,随后将这个vector push到lua栈(2次跨语言调用一次新建udata),所以总费用是1次元表,6次跨语言调用,一次新建udata。
lua层的vector3
调用函数,new_table,很简洁。费用是1次函数调用+1个新建table的花费。
对比
cs层的费用主要在跨语言调用,lua层的费用主要就是newtable的费用。按照实验结果而言,二者速度相差不大(但是lua层占用的内存更大)
基础计算(_add)
cs层的vector3
local cs_vector3 = CS.UnityEngine.Vector3(1,2,3)生成一个cs vector。这时候的cs_vector3是一个C层的udata,元表指向的CS层的wrap(或反射)。占用内存不大,只有2个标记位和3个float值的内存,并且无cs层的占用。当使用属性时比如说cs_vector3_a+cs_vector3_b时,会先产生一个元表调用的费用,随后因为函数是在cs层,所以会产生一个跨语言调用的费用。这时候进入了CS层,CS层需要取udata的数据,是通过调用C API完成的,所以这时候又会产生跨语言的费用。to_userdata一个,unpack一个,两个向量就是4份。随后计算完还要打包回去又是2个。打包完就要切换成C/Lua,又是一份,所以一共的费用是:跨语言费用是8个,元表调用1个,新建udata 1个。
lua层的vector3
local lua_vector3 = lua_vector(1,2,3)生成一个lua_vector3,这时候lua_vector3占用的是一个table的内存,也无cs层的占用,元表指向lua层的函数。当使用属性时比如说lua_vector3_a+lua_vector3_b时,会先产生一个元表调用的费用,随后直接在lua层计算,而后new一个table返回。所以一共的费用是:元表调用1个,新建lua表1个。
对比
cs层的费用主要在跨语言调用,lua层的费用主要就是newtable的费用。其实对于越复杂的计算,cs表现会更好一点,因为cs的计算力要比lua强。实验结果:针对于_add来说,lua层的速度是cs层的1.5~2倍。
Push到cs层
cs层的vector3
to_userdata + unpack一共两次跨语言调用。
lua层的vector3
一共16次跨语言调用。。。(取个table的单独的值要3次,还有什么pop的)
分开x,y,z push
3次跨语言调用(取x,y,z)
对比
分开xyz和cs_vector速度相差不大,而lua_vector的时间是前者的3-4倍(因为16次跨语言调用真的是太多了。)
取值
cs层的vector3
先调用元表,然后跨语言调用到cs,然后cs再跨语言调用C api进行取值,费用是1次元表调用,5次元语言调用(元表1+读udata2+pushnumber1+返回1)
lua层的vector3
就是个表的键值对获取。
对比
二者的差距相当之大,10多倍。lua表获取键值对还是很快的,虚拟机也没有多余的指令,表那边也就是个hash获取。
思考
从前面的对比可以发现,cs_vector在计算,取值上要比lua_vector占劣势,而push到cs层,cs_vector要远远快于lua_vector,lua_vector已经没有优化空间了,有没有什么方法把cs_vector的速度加快呢?有!
之前所提到的cs_vector的花费,最常见的一个词就是”跨语言调用”。那么有没有可能不产生这个跨语言调用的费用?可以!vector就是一个简单的值类型,而xlua中的cs层在c层生成的这个udata,也只用到了非元表的部分,而且取值也都是在C层实现的。那么如果我在C层实现元表的?那么之前的所有的所有的跨语言调用都不存在了。而且可以直接无损push到cs层。
实践
push_struct以及pack_float3和unpack_float3和xlua的操作保持一直不再赘述(也是为了无缝衔接xlua的push)。代码不复杂直接贴在下面了,实现了元表相加,函数相加,创建。
int C_vector(lua_State* L) { int x = lua_tonumber(L, -3); int y = lua_tonumber(L, -2); int z = lua_tonumber(L, -1); lua_pop(L, 3); void* p = xlua_pushstruct(L, 12); xlua_pack_float3(p, 0, x, y, z); lua_rawgeti(L, LUA_REGISTRYINDEX, cm); lua_setmetatable(L, -2); return 1; } int C_self_vector_a_add_b(lua_State* L) { void* v1 = lua_touserdata(L, -2); void* v2 = lua_touserdata(L, -1); float f1, f2, f3, f4, f5, f6; xlua_unpack_float3(v1, 0, &f1, &f2, &f3); xlua_unpack_float3(v2, 0, &f4, &f5, &f6); f1 = f1 + f4; f2 = f2 + f5; f3 = f3 + f6; lua_pop(L, 1); xlua_pack_float3(v1, 0, f1, f2, f3); return 1; } int C_vector_a_add_b(lua_State* L) { void* v1 = lua_touserdata(L, -2); void* v2 = lua_touserdata(L, -1); float f1,f2,f3,f4,f5,f6; xlua_unpack_float3(v1, 0, &f1, &f2, &f3); xlua_unpack_float3(v2, 0, &f4, &f5, &f6); lua_pop(L, 2); f1 = f1 + f4; f2 = f2 + f5; f3 = f3 + f6; void* p = xlua_pushstruct(L, 12); xlua_pack_float3(p, 0, f1, f2, f3); return 1; } int C_vector_indexer(lua_State* L) { const char* name = lua_tostring(L, -1); if (strcmp(name, "x") == 0) { void* v = lua_touserdata(L, -2); float f1, f2, f3; xlua_unpack_float3(v, 0, &f1, &f2, &f3); lua_pop(L, 2); lua_pushnumber(L, f1); return 1; }else if(strcmp(name, "y") == 0) { void* v = lua_touserdata(L, -2); float f1, f2, f3; xlua_unpack_float3(v, 0, &f1, &f2, &f3); lua_pop(L, 2); lua_pushnumber(L, f2); return 1; }else if(strcmp(name, "z") == 0) { void* v = lua_touserdata(L, -2); float f1, f2, f3; xlua_unpack_float3(v, 0, &f1, &f2, &f3); lua_pop(L, 2); lua_pushnumber(L, f3); return 1; } else if (strcmp(name, "selfadd") == 0) { lua_pop(L, 2); lua_pushcclosure(L, C_self_vector_a_add_b, 0); return 1; } return 0; } void _init_C_vector_metatable(lua_State* L) { lua_createtable(L, 0, 10); lua_pushcclosure(L, C_vector_a_add_b, 0); lua_setfield(L, -2, "__add"); lua_pushcclosure(L, C_vector_indexer, 0); lua_setfield(L, -2, "__index"); cm = luaL_ref(L, LUA_REGISTRYINDEX); } //lua中的使用 local a = C_vector(1,2,3.3) //C vector 创建 local v3 = C_vector_a_add_b(v1, v2) //C vector 函数add local v3 = v1 + v2 //C vector 元表add local v3 = C_self_vector_a_add_b(v1, v2) //C vector 函数selfadd 不新建结构 v1:selfadd(v2) //C vector 元表selfadd 不新建结构 local a = v1.x //C vector 取值
如何把
实验结果
c_vector用时(1e6) (s) | lua_vector用时(1e6) (s) | |
创建 | 0.42 | 0.929 |
函数add | 0.366 | 1.293 |
元表add | 0.465 | 1.334 |
函数selfadd 不新建结构 | 0.1 | 0.339 |
C vector 元表selfadd 不新建结构 | 0.484 | 0.533 |
取值 | 0.108 | 0.017 |