基础
1 尽量使用local
使用变量的效率:local > upvalue > global
local变量存放在栈中,upvalue存放在链表中,global变量存放在全局的表中。
例子:
使用local
- function Add()
- local x, y
- return x + y
- nd
function Add()
local x, y
return x + y
end
使用upvalue
- local x, y
- function Add()
- return x + y
- end
local x, y
function Add()
return x + y
end
使用global
- function Add()
- return x + y
- end
function Add()
return x + y
end
2 尽量不使用loadstring等函数来动态调用代码
编译工作是很繁重的,动态调用代码时Lua虚拟机需要动态地编译Lua代码,效率很低。
表
3 减少表的插入次数
Lua中的表由一个数组和一个哈希表组成,每一次从表中插入数据,Lua都会重新计算每个元素新的存放位置,导致插入的效率很低。
- local a = {}
- a[1] = 1 -- 重新哈希,数组部分大小为1
- a[2] = 2 -- 重新哈希,数组部分大小为2
- a[3] = 3 -- 重新哈希,数组部分大小为4
local a = {}
a[1] = 1 -- 重新哈希,数组部分大小为1
a[2] = 2 -- 重新哈希,数组部分大小为2
a[3] = 3 -- 重新哈希,数组部分大小为4
以上的代码产生三次重新哈希计算以及三次数组部分扩容,相比以下代码只执行了一次哈希和扩容。
- local a = {true, true, true} -- 重新哈希,数组部分大小为4
- a[1] = 1
- a[2] = 2
- a[3] = 3
local a = {true, true, true} -- 重新哈希,数组部分大小为4
a[1] = 1
a[2] = 2
a[3] = 3
4 不要试图通过删除表元素来节省内存空间
对表中的元素赋nil值不会导致表元素被删除,但是在插入新元素时,表会根据现有的非nil元素的数量来更改表的大小。
字符串
5 避免字符串连接
Lua在虚拟机内部对于相同的字符串只保留一份copy,保存在一个哈希表中,字符串变量保存这个copy的引用。这种实现机制导致Lua中字符串的查找和比较效率比较高,创建的效率比较低。
减少
6 减少表的创建
- polyline = { { x = 10.3, y = 98.5 },
- { x = 10.3, y = 18.3 },
- { x = 15.0, y = 98.5 },
- ...
- }
polyline = { { x = 10.3, y = 98.5 },
{ x = 10.3, y = 18.3 },
{ x = 15.0, y = 98.5 },
...
}
同样表达点的序列的表,以上代码每个点都创建一个表,相比以下代码只创建了两个表。
- polyline = { x = { 10.3, 10.3, 15.0, ...},
- y = { 98.5, 18.3, 98.5, ...}
- }
polyline = { x = { 10.3, 10.3, 15.0, ...},
y = { 98.5, 18.3, 98.5, ...}
}
7 避免不必要的创建
Lua虚拟机每执行到local就创建一个变量,执行到function就创建一个闭包。参考 Lua 5.0实现 4 函数和闭包
重用
8 重用表是一个比较有效的手段
- local t = {}
- for i = 1970, 2000 do
- t[i] = os.time({year = i, month = 6, day = 14})
- end
local t = {}
for i = 1970, 2000 do
t[i] = os.time({year = i, month = 6, day = 14})
end
以下是与之等价的代码,但是重用了表:
- local t = {}
- local aux = {year = nil, month = 6, day = 14}
- for i = 1970, 2000 do
- aux.year = i
- t[i] = os.time(aux)
- end
local t = {}
local aux = {year = nil, month = 6, day = 14}
for i = 1970, 2000 do
aux.year = i
t[i] = os.time(aux)
end
9 使用记忆化方法
- function memoize (f)
- local mem = {} --记忆化的表,存储每一个输入的计算结果
- setmetatable(mem, {__mode = "kv"}) -- 定义成弱表
- return function (x) -- 这个函数就是记忆化的f
- local r = mem[x]
- if r == nil then -- 如果没有对应的计算结果
- r = f(x) -- 调用f计算
- mem[x] = r -- 保存在记忆化表中
- end
- return r -- 已经计算过,直接返回结果
- end
- end
function memoize (f)
local mem = {} --记忆化的表,存储每一个输入的计算结果
setmetatable(mem, {__mode = "kv"}) -- 定义成弱表
return function (x) -- 这个函数就是记忆化的f
local r = mem[x]
if r == nil then -- 如果没有对应的计算结果
r = f(x) -- 调用f计算
mem[x] = r -- 保存在记忆化表中
end
return r -- 已经计算过,直接返回结果
end
end
假如有以下函数:
- function fab(x)
- if x == 1 or x == 2 then
- return 1
- end
- return fab(x-1) + fab(x-2)
- end
function fab(x)
if x == 1 or x == 2 then
return 1
end
return fab(x-1) + fab(x-2)
end
对比不使用记忆化方法和记忆化方法:
fab(35) -- 执行8s
fab = memoize(fab)
fab(35) -- 不超过1s