在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1. lua_call / lua_pcall
对于这两个函数,对栈底是没有影响的——调用的时候,参数会被从栈中移除,当函数返
回的时候,其返回值会从函数处开始压入,可以通过新的栈顶减去旧的栈顶得到返回值数
量。
2. lua_yield
对 Lua 函数来说, yield相当于只是在调用一个函数,对C函数也是这样。yield的参数
是返回值的数量,这些返回值会被返回给resume,见下。
2. lua_resume
resume是最复杂的一个函数:首先,第一次调用的时候,它相当于是个lua_call,也就是
说,被resume的那个函数只能看到自己的参数在栈顶,更低的就看不见了。其次,当函数
被yield之后,resume返回,这时resume的栈顶只有yield返回的参数,其他的栈元素是看
不到的。
在这个基础上,coroutine的状态是LUA_YIELD,如果需要继续执行,就需要再次 resume
,这里就有一些微妙的区别了。在这种情况下,resume的nargs参数是无用的——也就是
说,无论你传递的是多少,所有栈顶的元素都会被返回给yield,也就是说,如果需要在
返回之前清除掉栈,那么就需要你自己手动去清除,然后再resume。
下面是测试的示例代码:
在Lua里面声明小数组的最好方法是什么?
在很早以前,就看到“闭包比表要快”的这么一个言论。一直没有验证过,只是心里就这么觉得了。所以自己第一次写的Lua网游,大量利用了闭包,最后估计还是有很严重的内存问题……在我的对象模型里面,对象构造函数通常是这样的:
这个设计可以保证,在访问对象的函数的时候,速度可以达到最快——因为没有元表查询。但是,这样做恰好就违背了这样的原则:“在设计的初期,不要过早地考虑优化”。是的,因为任何优化都是有代价的,这里的代价理所当然就是内存了。 另外,在Lua-5.1中,我后来自己测试的结果是,表貌似还是比闭包要快一点,关键点是,这样貌似占用内存还小很多,自从这么摆了一道以后,反正至少对于“看见大括号就有点恐惧内存分配”的心理上是好过多了。不过至少对于闭包的大小和速度什么的心里头反而就没底了。 今天突然想到了一个叫做lua-vararg的lua库。这个库可以对vararg进行包装,提供对用户来说“比较自然”的vararg的体验——说白了,这就是用vararg去实现了元组(tuple)嘛。这个库很早以前就知道了,我还自己亲自改过,但是性能到底怎么样呢?心里没底,所以决定今天写个伪测试来看看效果。 首先,我们看一下Lua源代码里面表和闭包的描述:
OK,代码有了,那么第一个问题是:表和闭包分别占多大的大小呢? 额= =这个问题不好搞啊,看起来好复杂的样子,还得考虑对齐……额,不好搞的话,直接写个程序不就可以了吗?
恩,输出结果是32和24……(别打偶……) 好吧,闭包居然比表要小!恩,这是很正常的。而且,这里的表可是“裸表”哦,一点数据都没有的,而这个被声明的闭包是默认带上了一个upvalue的! 那么,对于有十个元素的表和闭包,大小又是怎么样的呢?注意到表保存数组元素用的是TValue指针,而闭包里面的upvalue(主要指C闭包)也同样是TValue,我们只需要分别给表和闭包的大小增加10个TValue的大小即可——额,闭包只需要加9,因为前面已经有个元素了,最后得到的大小是:112字节和96字节……恩~ 好了,那么在大小方面,的确闭包(特指C闭包)是保存小数组的最好途径了,那么,除开大小,在速度上面,它们之间有什么变化吗?我们继续测试一下!首先声明一下,这里的测试对于真实的环境是没有意义的,通常来说,我们并不需要考虑像分配小数组之间的效率问题,这里的测试,只是在于检验“闭包比表快”这个说法罢了,这样的检验本身,可以当作是使用vararg模块的依据,对做出某些设计决策是有好处的。 我们准备测试四种情况下的速度情况
下面是测试结果:
我们来分析一下这个数据,最简单的分析方法是排序,对于时间的顺序是c closure < lua table < lua closure < c table,对于内存剩余的顺序是lua closure < c table == lua table < c closure。 好奇怪的结果!!我们试着来分析一下吧!看起来内存里面有一项是相同的:即用c和用lua创建表,占用内存的大小是近似相同的。而用C创建闭包,占用内存最大,但是也大不了多少。这里面是什么情况呢?我们关闭一下垃圾回收试试?对了!原来是这样……关掉垃圾回收以后,内存的占用非常非常夸张,原来,这是垃圾回收以后的结果(顺便说一下,关闭垃圾回收,所有的测试都变慢了,不过相对结果不变)。 那么,内存的意义上就不大了。只能说是垃圾回收的策略不同罢了。我们来分析一下时间问题好了。 首先,这个数据实在是太奇怪了了:如果说C的API比Lua代码快,那么为什么c table是最慢的呢?如果说闭包比表快,那么为什么lua闭包不如lua的表呢?这是怎么回事呢? 很容易发现问题的是lua闭包,这是倒数第二慢的……因为Lua5.2出了一个新功能:在创建lua闭包的时候,会和之前缓存的进行比较,看看是新的闭包还是以前的,因为我们创建的闭包都是新的函数调用,显然和以前闭包的upvalue不可能一样,因此这个比较始终是会失败的……看来也只能是这个地方会占用时间了。当然了,关联upvalue、关闭upvalue等等都需要占据时间。lua闭包比较慢几乎是可以预见到的了。这一点我们不奇怪。 奇怪的是,为什么用C API写的新建表比lua代码还要慢呢?注意到,Lua代码是不需要再创建新的“整数”的,它们是直接从常量表(K表)载入的,所以pushnumber会拖慢一点速度,其次,lua_rawseti会比luaH_setint多做一些检查,除了这两点以外,还有一个很容易忽视的地方是API调用开销:因为Lua是在DLL里面的,因此API调用相对较慢,如果静态链接的话,对其他测试来说,结果影响不大,但是c table测试瞬间就快了20%——也许是因为每次推入新的包,c table测试比别的方法多了10个C API的原因吧……Lua里面没有一次给表设置多个值的方法,必须一个一个地设置,每次设置都必须压入新值,所以即使是相对较快的lua_rawseti,也架不住每次设置的两次API啊……当然,从这一点上也可以看出来,我们的测试是很不合理的,因为即使是API效率,都可以极大地影响测试结果。当然这里需要说明的是,即使是将API效率的原因去除,c table仍然是最慢的。 然后,c cloure和lua table就比较符合我们的期望了。看来的确是闭包会快一点啊…… 这里只比较了创建的效率,对于取值的效率,大家可以自己去比较。但是从这里可以看出来,如果希望交换大量小数据块,那么closure的确是一个合理的选择:它体积较小,没有多余的功能(比如表支持的哈希查找或者增加大小或者独立的元表等等)。作为需要快速保存少量数据,lua-vararg的确是一个很好的选择——另外,尽量选择C实现的版本,如果无法使用C库,那就还是用表来实现吧……这样比较快……
最后,附上我自己写的vararg的C实现:https://gist.github.com/starwing/5893607
#define LUA_LIB #include <lua.h> #include <lauxlib.h> static lua_Integer posrelat(lua_Integer pos, size_t len) { if (pos >= 0) return pos; else if (0u - (size_t)pos > len) return 0; else return (lua_Integer)len + pos + 1; } static int tuple(lua_State *L) { int top, n = (int)lua_tointeger(L, lua_upvalueindex(1)); lua_Integer i, j; switch (lua_type(L, 1)) { case LUA_TNIL: /* as iterator */ i = lua_tointeger(L, 2) + 1; if (i <= 0 || i > n) return 0; lua_pushinteger(L, i); lua_pushvalue(L, lua_upvalueindex(i + 1)); return 2; case LUA_TSTRING: /* as length operator */ if (*lua_tostring(L, 1) == '#') { lua_pushinteger(L, n); return 1; } break; case LUA_TNONE: /* get all varargs */ luaL_checkstack(L, n, "too many values"); for (i = 1; i <= n; ++i) lua_pushvalue(L, lua_upvalueindex(i+1)); return n; case LUA_TNUMBER: /* get/set a range */ i = posrelat(luaL_checkinteger(L, 1), n); j = posrelat(luaL_optinteger(L, 2, i), n); if (i > j) return 0; n = (int)(j-i+1); luaL_checkstack(L, n, "too many values"); if ((top = lua_gettop(L)) <= 2) { /* get */ for (; i <= j; ++i) lua_pushvalue(L, lua_upvalueindex(i+1)); } else { int idx; lua_settop(L, top = n + 2); for (idx = 3; idx <= top; ++idx) { lua_pushvalue(L, idx); lua_replace(L, lua_upvalueindex(i+idx-2)); } } return n; } return luaL_argerror(L, 1, "invalid argument"); } static int Lpack(lua_State *L) { int n = lua_gettop(L); if (n >= 255) luaL_error(L, "too many values to pack"); lua_pushinteger(L, n); lua_insert(L, 1); lua_pushcclosure(L, tuple, n+1); return 1; } static int Lrange(lua_State *L) { int n = lua_gettop(L) - 2; lua_Integer i, j; if (n < 0) return 0; i = posrelat(luaL_checkinteger(L, 1), n); j = posrelat(luaL_checkinteger(L, 2), n); if (i > j || j == 0) return 0; if (j > n) luaL_checkstack(L, j-n, "range is too big"); lua_settop(L, j + 2); return j-i+1; } static int Linsert(lua_State *L) { int n = lua_gettop(L) - 2; lua_Integer i = posrelat(luaL_checkinteger(L, 2), n); if (i > n) { luaL_checkstack(L, i-n, "index is too big"); lua_settop(L, i + 1); lua_pushvalue(L, 1); return i; } lua_pushvalue(L, 1); lua_insert(L, i + 2); return n + 1; } static int Lremove(lua_State *L) { int n = lua_gettop(L) - 1; lua_Integer i = posrelat(luaL_checkinteger(L, 1), n); if (i <= n) { lua_remove(L, i + 1); --n; } return n; } static int Lreplace(lua_State *L) { int n = lua_gettop(L) - 2; lua_Integer i = posrelat(luaL_checkinteger(L, 2), n); if (i > n) { luaL_checkstack(L, i-n, "index is too big"); lua_settop(L, i + 1); lua_pushvalue(L, 1); return i; } lua_pushvalue(L, 1); lua_replace(L, i + 2); return n; } static int Lpush(lua_State *L) { lua_pushvalue(L, 1); return lua_gettop(L) - 1; } static int Lpop(lua_State *L) { lua_pop(L, 1); return lua_gettop(L); } static int Ltake(lua_State *L) { int n = lua_gettop(L) - 1; lua_Integer i = posrelat(luaL_checkinteger(L, 1), n); if (i > n) return 0; lua_pop(L, n-i); return i; } static int Ltail(lua_State *L) { int n = lua_gettop(L) - 1; lua_Integer i = posrelat(luaL_checkinteger(L, 1), n); if (i > n) return 0; return n-i+1; } static int Lshift(lua_State *L) { return lua_gettop(L) - 1; } static int Lmap(lua_State *L) { int i, n = lua_gettop(L); luaL_checkany(L, 1); for (i = 2; i <= n; ++i) { lua_pushvalue(L, 1); lua_pushvalue(L, i); lua_call(L, 1, 1); lua_replace(L, i); } return n-1; } static int Lfilter(lua_State *L) { int i, n = lua_gettop(L); luaL_checkany(L, 1); for (i = 2; i <= n; ++i) { lua_pushvalue(L, 1); lua_pushvalue(L, i); lua_call(L, 1, 1); if (!lua_toboolean(L, -1)) { lua_remove(L, i); --i, --n; } lua_pop(L, 1); } return n-1; } static int Lreduce(lua_State *L) { int i, n = lua_gettop(L); luaL_checkany(L, 1); if (n <= 3) { lua_call(L, n-1, 1); return 1; } lua_pushvalue(L, 1); lua_pushvalue(L, 2); lua_pushvalue(L, 3); lua_call(L, 2, 1); for (i = 4; i <= n; ++i) { lua_pushvalue(L, 1); lua_insert(L, -2); lua_pushvalue(L, i); lua_call(L, 2, 1); } return 1; } static int Lunpack(lua_State *L) { int i, n = lua_gettop(L); for (i = 1; i <= n; ++i) { lua_pushvalue(L, i); lua_call(L, 0, LUA_MULTRET); } return lua_gettop(L)-n; } static int Lrotate(lua_State *L) { int n = lua_gettop(L) - 1; lua_Integer i, c = luaL_checkinteger(L, 1) % n; #if LUA_VERSION_NUM >= 503 (void)i; /* unused */ lua_rotate(L, 2, (int)c); #else c = -c + ((c > 0) ? n+1 : 1); if (c > 1) luaL_checkstack(L, c-1, "too many values"); for (i = 2; i <= c; ++i) lua_pushvalue(L, i); #endif return n; } static int Lreverse(lua_State *L) { int i, j, n = lua_gettop(L); for (i = 1, j = n; i < j; ++i, --j) { lua_pushvalue(L, i); lua_pushvalue(L, j); lua_replace(L, i); lua_replace(L, j); } return n; } static int Lrep(lua_State *L) { int top = lua_gettop(L), n = top - 1; lua_Integer i, j, count = luaL_checkinteger(L, 1); if (count <= 0) return 0; if (n == 0) { luaL_checkstack(L, count, "too many values 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论