主要参考:https://www.lua.org/gems/sample.pdf
更多内容可参考:http://lua-users.org/wiki/OptimisationTips
知乎-Lua性能优化(二):CPU性能优化
table的频繁查找,尽可能使用local变量,不要在循环中做大量表的查找,例如不要在循环中用全局函数,不要使用math.floor这种,而是将它的值提前存储在局部变量,因为即使是hash表的查找也会比较耗时;
频繁的创建新的对象,不要在循环中频繁创建新表,如a = {}这种操作等,要有缓存池的概念;
了解table的组成,分为两部分数组和哈希,当lua需要向表中插入一个哈希值而hash数组又满了的时候,lua将会重新计算哈希,而新的hash计算的第一步就是要确定新的table和hash部分的size大小,这就是重新分配表中数组和哈希表部分的流程;lua会分别计算两个部分的size,然后选择一个size(size为2的n次方)使其满足curCount<=size<=2*curCount,参考如下程序,在一开始创建一个新表的时候,数组和hash的size都是0,在第一次循环,数组size扩充为1,第二段,数组size为2,第三段,数组size为4;如果是a.x=1,a.y=1,a.z=1,那么分配的逻辑也和上面类似;所以这里可以思考,如果依次插入8个元素,需要几次rehash(4次),如果是64个元素是(size为64,2的6次方),需要几次rehash(7次);
local t = {}
for i = 1, 3 do
t[i] = true
end
lua创建表的时候不分配内存,创建一个拥有一百万个元素的表要比创建一百万个表(每个拥有一个元素)要快的多,因为创建一百万个表,他们做执行的rehash操作就是一百万次,在旧版本的lua中会自动为空表预先分配内存,size为4,但是这很可能造成内存的浪费,比如很多表用不到这么些;在lua中编程的时候,你可以使用构造函数来避免初始化时的rehash操作,比如使用{true,true,true},lua就知道在数组部分预先需要三个槽位,它就会分配内存size为3,注意这里不是四,因为这个走的不是新增元素的rehash逻辑;举例如下,
for i = 1, 1000000 do
local a = {}
a[1] = 1
a[2] = 2
a[3] = 3
end
for i = 1, 1000000 do
local a = {true, true, true}
a[1] = 1
a[2] = 2
a[3] = 3
end
而如果写出了如下代码,{[1] = true, [2] = true, [3] = true},lua是不会将如上table的元素放入到数组段的,所以他就会分配size为4,就会导致大量的内存的浪费,以及CPU的时间;两个部分的size重新计算只会在table满了以后而且需要重新添加新的元素;也就是说,即使把一个大的table都置为nil,它所占用的内存也不会压缩;
不会压缩的两个原因:
- 遍历所有的nil是很耗的,那样每次插入都会耗时过多;
- nil也是一种值类型,table允许nil类型的复制;
字符串
lua中的字符串是内置的,它持有每一个字符串的单独拷贝,当需要一个新字符串的时候,lua会检查是否已经有了一个字符串的拷贝,如果有就重新利用这个拷贝;所以,lua中的字符串是值类型,任何变量只是持有字符串的引用,而不能去修改这个字符串;
配置表(减少数据量)
如下所示,一百万个顶点数据从95k到65k,到24k;
导出的配表的数据结构不要求插入和删除,只要求查找块,所以尽可能用顺序存储即可,也就是用数组型table;
polyline = {
{x = 10.1, y = 5.2},
{x = 10.1, y = 5.3},
{x = 12.1, y = 8.2},
...
}
polyline = {
{10.1, 5.2},
{10.1, 5.3},
{12.1, 8.2},
...
}
polyline = {
x = {10.1, 10.1, 12.1},
y = {5.2, 5.3, 8.2},
}
提取默认值配置;