使用本地函数优化 Lua 代码

所以,我知道将全局函数赋值给本地变量可以显著提高它们的运行速度,就像编写以下代码一样:

local _cos = math.cos
for i = 1, 100000 do
    _cos(i)
end

比不先将函数本地化的代码快得多:

for i = 1, 100000 do
    math.cos(i)
end

然而,根据我的测试,似乎对 tonumbertypepairs 等函数并不适用。

有人能解释一下为什么一些全局函数会显著加快,而另一些则似乎根本没有受到影响吗?

点赞
用户4984564
用户4984564

将全局函数本地化不会使它们显著变快,只会使它们稍微快一点,因为你不必在_ENV里查找它们,尤其是当它们在另一个类似 _ENV.math.cos 的表中时。对于直接在 _ENV 中的函数,这种效果显然较小,如果函数本身运行非常缓慢,本地化的微小差异不会特别明显。

事实上,你永远不应该只是因为本地化函数而本地化函数。如果你有一个非常小的循环体,其中包含大量调用非常小的函数的迭代,则应该考虑将其本地化(并将其放在尽可能靠近循环的位置),但仅当你确信这会产生差异时才应该这样做(测试是你的好朋友)。

似乎仍然存在这种误解,认为仅将要使用的函数本地化到整个库中会自动使它们变快,这是完全错误的。

甚至LuaJIT本身在大多数情况下都会这样做;检测循环体内重复表访问并在循环体外部进行本地化。

还要记住的是,虽然 math.cos 永远不应该被更改,但你可能正在访问来自某个其他模块的函数,而该函数实际上会在幕后发生更改以反映模块中的某些状态更改。在这种情况下,本地化函数将意味着你正在使用过时的函数。这不是非常常见,但确实会发生。


只是为了好玩;在PUC Lua和LuaJIT中尝试运行以下代码:

local function bench(n, fn, ...)
    local t1 = os.clock()
    for i=1,n do
        fn(...)
    end
    local t2 = os.clock()
    return t2 - t1
end

local n = 1e7

print(bench(n, function() return math.cos(20) end))
print(bench(n, function() return tostring(20) end))
print(bench(n, function() return tonumber("20") end))
print("---------")
print(bench(n, math.cos, 20))
print(bench(n, tostring, 20))
print(bench(n, tonumber, "20"))
2020-04-10 07:46:01