我正在寻找一个描述提高 Lua 性能的各种技术的文档 http://www.lua.org/gems/sample.pdf脚本代码,我很震惊竟然需要这样的技巧。 (虽然我引用的是 Lua,但我在 Javascript 中也见过类似的 hack)。
为什么需要这种优化:
例如,代码
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
他们重新宣告sin
本地运行。
为什么这会有帮助?无论如何,这是编译器的工作。为什么程序员必须做编译器的工作?
我在 Javascript 中看到过类似的东西;所以显然必须有一个very这是解释编译器没有完成其工作的充分理由。它是什么?
我在我正在摆弄的 Lua 环境中反复看到它;人们将变量重新声明为局部变量:
local strfind = strfind
local strlen = strlen
local gsub = gsub
local pairs = pairs
local ipairs = ipairs
local type = type
local tinsert = tinsert
local tremove = tremove
local unpack = unpack
local max = max
local min = min
local floor = floor
local ceil = ceil
local loadstring = loadstring
local tostring = tostring
local setmetatable = setmetatable
local getmetatable = getmetatable
local format = format
local sin = math.sin
这是怎么回事,人们必须做编译器的工作?编译器是否对如何查找感到困惑format
?为什么这是程序员必须处理的问题?为什么 1993 年这个问题没有得到解决呢?
我似乎也遇到了一个逻辑悖论:
- 优化不应在没有分析的情况下进行
- Lua没有能力被profile
- Lua不应该优化
为什么这会有帮助?无论如何,这是编译器的工作。为什么程序员必须做编译器的工作?
Lua 是一种动态语言。编译器可以用静态语言进行很多推理,例如将常量表达式从循环中取出。在动态语言中,情况有点不同。
Lua 的主要(也是唯一)数据结构是表。math
也只是一个表,尽管它在这里用作命名空间。没有人可以阻止你修改math.sin
在循环中的某个地方运行函数(甚至认为这是一件不明智的事情),并且编译器在编译代码时无法知道这一点。因此,编译器完全按照您的指示执行:在循环的每次迭代中,查找sin
函数在math
表并调用它。
现在,如果您知道您不会修改math.sin
(即您将调用相同的函数),您可以将其保存在循环外的局部变量中。由于没有表查找,因此生成的代码速度更快。
LuaJIT 的情况有点不同 - 它使用跟踪和一些高级魔法来查看代码正在做什么在运行时,因此它实际上可以通过将表达式移到循环之外来优化循环,以及其他优化,除了实际将其编译为机器代码之外,使其速度快得惊人。
关于“将变量重新声明为本地变量”——很多时候在定义模块时,您希望使用原始函数。访问时pairs
, max
或任何使用全局变量的东西,没有人可以向您保证每次调用都是相同的函数。例如stdlib http://luaforge.net/projects/stdlib重新定义了很多全局函数。
通过创建与全局同名的局部变量,您实际上将函数存储到局部变量中,并且因为局部变量(它们是词法作用域的,这意味着它们在当前作用域和任何嵌套作用域中都可见)优先于全局变量,请确保始终调用相同的函数。如果稍后有人修改全局,也不会影响您的模块。更不用说它也更快,因为全局变量是在全局表中查找的(_G
).
Update: 我刚读完Lua 性能技巧 http://www.lua.org/gems/sample.pdf作者是 Lua 作者之一 Roberto Ierusalimschy,它几乎解释了您需要了解的有关 Lua、性能和优化的所有内容。 IMO 最重要的规则是:
Rule #1: 不要这样做。
Rule #2: 还别做。 (仅供专家参考)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)