对于 Xlua / UnityVector的优化的设想

前面写了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.420.929
函数add0.3661.293
元表add0.4651.334
函数selfadd 不新建结构0.10.339
C vector 元表selfadd 不新建结构0.4840.533
取值0.1080.017
总结来说,除了取值,C_vector要比lua_vector快,而且对于xlua的push来说,c_vector具有天然的优势。而正常的业务一般都是整体计算,除了push到cs的时候,一般不会单独取值。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

大纲

Share the Post:
滚动至顶部