9-27 459 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
转载时必须以链接形式注明原始出处及本声明。