您的位置:首页 > 编程语言 > Lua

编写高效Lua代码的方法 - 1 - 基本知识

2011-11-15 17:39 399 查看
翻译自《Lua Programming Gems》Chapter 2:Lua Performance Tips:Basic fact By Roberto Ierusalimschy



编写高效Lua代码的方法

基本知识

Lua在运行代码之前,会先把源码翻译(预编译)成一种内部编码,这种编码由一连串的虚拟机能够识别指令构成,与CPU的机器码很相似。接下来由C代码中的一个while循环负责解释这些内部编码,这个while循环中有一个很大的switch,一种指令就有对应的一个case。

可能你已经从其他地方得知,自5.0版本开始,Lua就使用一个基于寄存器的虚拟机。但是这些“寄存器”跟CPU中的寄存器没有任何关联,因为这种关联会使Lua失去可移植性,并且会使Lua受限于可用的寄存器数量。Lua使用一个栈(由一个数组加上一些索引实现)来存放它的寄存器。每一个运行中的函数都有各自的一份活动记录,这些活动记录保存在栈中,内部存放着每个函数对应的寄存器。所以每个函数都有一组各自的寄存器。每条指令中只有8个bit用来标志寄存器,所以每个函数最多能够使用250个寄存器。

由于Lua有如此大量的寄存器,所以在预编译时能够将所有的局部变量(local)存放到寄存器中。所以,在Lua中,访问局部变量是很快的。举个例子,如果a和b是局部变量,语句a= a + b只生成一条指令:ADD 0 0 1 (假设a和b分别在寄存器0和1中)。对比一下如果a和b是全局变量,生成上述加法运算的中间代码会像这样:

GETGLOBAL 0 0 ; a

GETGLOBAL 1 1 ; b

ADD 0 0 1

SETGLOBAL 00 ; a

所以,很明显我们可以得出Lua编程里面其中一条最重要的改进性能的规则:使用局部变量(uselocals)!

如果你需要尽可能的提升程序的性能,你可以使用局部变量,比如,如果你在一个很长的循环里调用一个函数,你可以先将函数赋值给一个局部变量。比如以下代码

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


访问外层局部变量(也就是外一层函数的局部变量)并没有访问局部变量快,但是还是比访问全局变量快。看看以下代码片段:

function foo(x)
for i =1, 1000000 do
x =x + math.sin(i)
end
return x
end
print(foo(10))
我们通过在foo函数外面声明一次sin来优化它:

local sin = math.sin
function foo(x)
for i =1, 1000000 do
x =x + sin(i)
end
return x
end
print(foo(10))


第二段代码比第一段快30%。

比起其他编译器,Lua的编译器是比较高效的,尽管如此,编译还是一项比较繁重的任务。所以,无论何时都要尽量避免在程序中编译代码(比如,调用loadstring函数)。除非你需要真正地动态地执行你的代码,比如代码是由用户输入的,否则你很少需要编译动态的代码。

考虑以下例子,下面的代码创建一个存放了10000函数的table,这些存放在table中的函数分别返回常量1到10000:
local lim = 10000
local a = {}
for i = 1, lim do
a[i] =loadstring(string.format("return %d", i))
end
print(a[10]()) --> 10


这份代码运行了1.4秒。

我们通过使用闭包来避免动态编译。下面的代码在1/10的时间里(0.14)创建了同样的10000个函数:
function fk (k)
returnfunction () return k end
end
local lim = 100000
local a = {}
for i = 1, lim do a[i] = fk(i) end
print(a[10]()) --> 10
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: