本地声明(内置)Lua函数以减少开销



人们常说,应该在本地重新声明(某些)Lua函数,这样可以减少开销。但这背后的确切规则/原则是什么?我怎么知道这应该为哪些职能做,对哪些职能来说是多余的?还是应该为每一个函数都这样做,甚至是你自己的函数?

不幸的是,我无法从Lua手册中找到它。

原理是,例如,每次编写table.insert时,Lua解释器都会在名为table的表中查找"insert"条目。实际上,它意味着_ENV.table.insert-_ENV是";全局变量";在Lua 5.2+中。Lua 5.1有类似的东西,但它不叫_ENV。解释器在_ENV中查找字符串"table",然后在该表中查找字符串"insert"。每次调用table.insert时,在实际调用函数之前,都要进行两次表查找。

但是,如果你把它放在一个局部变量中,那么解释器直接从局部变量中获取函数,这会更快。它仍然需要查找它,以填充局部变量。

如果只在局部变量的范围内调用函数一次,这是多余的,但这种情况非常罕见。对于已经声明为local的函数,没有理由这样做。它还使代码更难阅读,所以通常情况下,除非它真的很重要(在运行了很多次的代码中),否则你不会这么做。

我最喜欢的Lua加速工具是将表的所有可用内容放在名为:__index的元表中
这方面的一个常见示例是数据类型:string
它在他的__index元表中有所有字符串函数作为方法
因此,您可以直接在字符串上执行类似操作。。。

print(('istaqsinaayok'):upper():reverse())
-- Output: KOYAANISQATSI

上面的逻辑
在字符串中查找方法直接失败,因此将查找该方法的__index元方法。

我喜欢对数据类型数字实现相同的行为。。。

-- do debug.setmetatable() only once for all further defined/used numbers
math.pi = debug.setmetatable(math.pi, {__index = math})
-- From now numbers are objects ;-)
-- Lets output Pi but not using Pi this time
print((180):rad()) -- Pi calcing with method rad()
-- Output: 3.1415926535898

逻辑:如果不存在,则查找__index
仅落后一步:local
。。。imho。

另一个例子,使用这种方法。。。

-- koysenv.lua
_G = setmetatable(_G,
{ -- Metamethods
__index = {}, -- Table constructor
__name = 'Global Environment'
})
-- Reference whats in _G into __index
for key, value in pairs(_G) do
getmetatable(_G)['__index'][key] = value
end
-- Remove all whats in __index now from _G
for key, value in pairs(getmetatable(_G)['__index']) do
_G[key] = nil
end
return _G

当作为最后一个需求启动时,它将_G中的所有内容移动到新创建的元表方法__index中
之后_G看起来完全是空的-P
。。。但环境正在像什么都没发生一样运转。

添加到@user253751已经说过的内容:

代码质量

Lua是一种非常灵活的语言。其他语言要求您导入您使用的标准库的部分;Lua没有。Lua通常提供一个不受污染的全球环境。如果您使用环境_ENV(Lua 5.1/LuaJIT上的setfenv/getfenv),您将希望仍然能够访问Lua库。为此,您可以在更改环境之前对其进行本地化;然后您可以使用您的";"干净";您的模块/API表/类/任何内容的环境。这里的另一个选择是使用元表;元表链可能会很快变得棘手,并可能损害性能,因为每次触发索引元方法都需要失败的表查找。因此,CCD_ 15对其他全局变量进行实例化可以被视为导入它们的一种方式;以给出最小&粗略示例:

local print = print -- localize ("import") everything we need first
_ENV = {} -- set environment to clean table for module
function hello() -- this writes to _ENV instead of _G
print("Hello World!")
end
hello() -- inside the environment, all variables set here are accessible
return _ENV -- "export" the API table

性能

非常小的挑剔:局部变量并不严格地总是更快。在非常极端的情况下(即有很多upvalues),对表进行索引(如果是环境、字符串元表等,则不需要upvalue)实际上可能会更快。

我认为本地化变量是优化编译器的许多优化所必需的,例如LuaJIT;否则Lua生成的代码很少。像print这样的全局可能会在深层代码路径中的某个地方被覆盖,因此每次都必须重复索引操作;另一方面,对于本地人来说,口译员对其范围将有更多的保证。因此,它能够检测只写入一次的常数,例如在初始化时;对于globals来说,几乎不可能进行代码分析。

最新更新