9-27 548 views
往往我们进行性能优化, 首先去搜索别人成果,然后想当然去应用, 优秀点的测试一下, 然后改完收工.
这里吐槽一句, 搜索优化类文章大多直接贴结论或者罗列测试数据,看见能有理有据深入浅出娓娓道来的真是快乐无比.(一个趟过的坑)
如果想进行知识累计, 最好知其然知其所以然, 浅尝辄止不但会因为一则莫名其妙的新”规则” 扼杀了兴趣, 而且所谓优化还可能是错误的 .
这对于Lua一些常用优化, 对比源码来看会清楚许多, 如:用local, 提前填充table元素, insert效率, 字符串拼接…和table.concat选择, pairs和ipairs选择……
使用local保存全局变量的引用
Lua使用的是基于寄存器的虚拟机(使用一个栈来提供寄存器),所有的local变量都会保存在寄存器里,因此访问local变量的速度会快于全局变量。在Lua的源码中可以看到对局部变量的最大数量有200的限制:
/* @@ LUAI_MAXVARS is the maximum number of local variables per function @* (must be smaller than 250). */ #define LUAI_MAXVARS 200
table.insert,还是t[#t+1] = xxx
/* ltablib.c */
static int tinsert (lua_State *L) {
int e = aux_getn(L, 1) + 1; /* first empty element */
int pos; /* where to insert new element */
switch (lua_gettop(L)) {
case 2: { /* called with only 2 arguments */
pos = e; /* insert new element at the end */
break;
}
case 3: {
int i;
pos = luaL_checkint(L, 2); /* 2nd argument is the position */
if (pos > e) e = pos; /* grow array if necessary */
for (i = e; i > pos; i--) { /* move up elements */
lua_rawgeti(L, 1, i-1);
lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
}
break;
}
default: {
return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
}
}
luaL_setn(L, 1, e); /* new size */
lua_rawseti(L, 1, pos); /* t[pos] = v */
return 0;
}
在向其末尾插入数据时,每次插入数据都会调用aux_getn方法(会调用luaH_getn),用于获取table的长度。事实上#这个运算符(OP_LEN)也是调用的luaH_getn。也就是说其实table.insert的内部会有一次类似于t[#t+1] = xxx这样的操作。以下是luaH_getn的实现:
/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
/* there is a boundary in the array part: (binary) search for it */
unsigned int i = 0;
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(&t->array[m - 1])) j = m;
else i = m;
}
return i;
}
/* else must find a boundary in hash part */
else if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */
else return unbound_search(t, j);
}
首先是使用二分查找来获取array部分的边界(array部分长度总是2的倍数,没有数据的部分会被nil填充),否则如果没有hash部分(array部分为空或者array部分刚好填满),直接返回array长度,再否则就调用unbound_search(查找hash部分)。整体来看这个获取长度的开销还是不小的,因此缓存下来长度,并且在插入数据时直接以n+1为key,是一种很高效的做法。
本文持续更新…
参考 :
https://aillieo.cn/post/2018-09-13-lua-notes-03/
https://www.cnblogs.com/zwywilliam/
版权属于: CrazyStone Entertainment
转载时必须以链接形式注明原始出处及本声明。