编写高性能的Lua代码
只要实战不说废话
变量
因为 Lua 的寄存器很多,预编译时便能将所有的局部变量存到寄存器中。所以,在 Lua 中访问局部变量是很快的。所以解决Lua性能问题,最重要的是用 local
去修饰变量。不使用 local
修饰的变量都是全局变量,在查询局部变量的时候要去全局表 _G
中查询,很耗时间。
除了这个明显的地方,另有几处也可使用局部变量,可以助你挤出更多的性能。比如,如果在很长的循环里调用函数,可以先将这个函数赋值给一个局部变量。这个代码:
for i = 1, 1000000 do
local x = math.sin(i)
end
下面的方法比上面的方法快30%
local sin = math.sin
for i = 1, 1000000 do
local x = sin(i)
end
访问外层局部变量(也就是外一层函数的局部变量 up Value)并没有访问局部变量快,但是仍然比访问全局变量快。访问速度是这样的:first Value > up Value > global
考虑如下代码:
function foo(x)
for i = 1, 1000000 do
x = x + math.sin(i)
end
return x
end
我们可以通过在 foo函数外面定义一个 sin来优化它:(local sin
就相当于 function foo
的 up Value
)
local sin = math.sin
function foo(x)
for i = 1, 1000000 do
x = x + sin(i)
end
return x
end
print(foo(10))
表
当 Lua 想在表中插入一个新的键值而哈希数组已满时,Lua 会做一次重新哈希(rehash),我们以往写程序的习惯是这么初始化表 a = {}
这个时候 Lua 是没有给 table
分配空间的。如果是下面的代码会发生什么:
local a = {}
for i =1, 3 do
a[i] = true
end
这段代码触发了两次 rehash (一次分配2的指数值大小),如果是处理一个大数据,需要几万次的循环,可想而知 rehash 的次数将非常大,所以这样大大浪费了空间和性能。以后初始化表的时候如果如果知道表中有固定的值了,就直接构造进去。比如:
a = {true, true, true}
for i = 1, 1000000 do
local a = {}
a [1] = 1; a[2] = 2; a[3] = 3
end
这段代码运行了2.0s
其他
可以把不变的操作放在循环外
function foo (...)
for i = 1, n do
local t = {1, 2, 3, "hi"}
-- 做一些不改变 t 的操作
...
end
end
local t = {1, 2, 3, "hi"} -- 一次性地创建 t
function foo (...)
for i = 1, n do
-- 做一些不改变 t 的操作
...
end
end
很多字符串的处理,都可以通过在现有字符串上使用下标,来避免创建不必要的新字符串。例如,函数 string.find
返回的是给定模式出现的位置,而不是一个与之匹配的字符串。返回下标,就避免了在成功匹配时创建一个新的子字符串。若有需要,可以再通过函数 string.sub
来获取匹配的子字符串。