编写高效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)!
如果你需要尽可能的提升程序的性能,你可以使用局部变量,比如,如果你在一个很长的循环里调用一个函数,你可以先将函数赋值给一个局部变量。比如以下代码
访问外层局部变量(也就是外一层函数的局部变量)并没有访问局部变量快,但是还是比访问全局变量快。看看以下代码片段:
第二段代码比第一段快30%。
比起其他编译器,Lua的编译器是比较高效的,尽管如此,编译还是一项比较繁重的任务。所以,无论何时都要尽量避免在程序中编译代码(比如,调用loadstring函数)。除非你需要真正地动态地执行你的代码,比如代码是由用户输入的,否则你很少需要编译动态的代码。
考虑以下例子,下面的代码创建一个存放了10000函数的table,这些存放在table中的函数分别返回常量1到10000:
这份代码运行了1.4秒。
我们通过使用闭包来避免动态编译。下面的代码在1/10的时间里(0.14)创建了同样的10000个函数:
编写高效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
相关文章推荐
- 编写高效Lua代码的方法 - 总结
- 编写高效Lua代码的方法(整理)
- 编写高效Lua代码的方法 - 2 - 表相关
- 编写高效Lua代码的方法 - 3 - 字符串
- 编写高效Lua代码的方法 - 4 - 减少,重用,回收
- 编写高效Lua代码的方法(整理)
- 转: 编写高效lua代码的方法
- 编写高效Lua代码的方法
- 利用on和off方法编写高效的js代码
- android-基础知识:编写高效的android代码
- 编写高性能Lua代码的方法
- [编写高质量代码:改善java程序的151个建议]建议132 提升JAVA性能的基本方法
- [编写高质量代码:改善java程序的151个建议]建议132 提升JAVA性能的基本方法
- LESS的基本使用方法(用于快速编写CSS代码)
- [编写高质量代码:改善java程序的151个建议]建议56:自由选择字符串拼接方法
- Ubuntu系统下Xen虚拟机的基本安装方法(代码创建)
- 深入JavaScript(1)编写高质量JavaScript代码的基本要点
- 编写高质量代码改善C#程序的157个建议——建议2: 使用默认转型方法
- 编写高效的android代码
- 编写高质量代码改善C#程序的157个建议——建议6: 区别readonly和const的使用方法