首页 > 解决方案 > lua:全局变量与表条目变量

问题描述

在lua中,当您在表中有一个函数时,在函数中声明全局变量与将变量声明为表中的条目(如果有)有什么区别?在下面的示例中,变量是 x。

IE

dog={x=33,
func=function(self)
self.x=self.x*self.x
end
}

cat={func=function()
x=33
x=x*x
end
}

在 dog 中,我可以使用 self 的属性通过 dog:func() 而不是 dog.func(dog) 调用函数。但除此之外,在两者之间进行选择时,是否有任何性能方面需要考虑的因素?这些示例在循环中调用时会有所不同,但除此之外呢?

标签: lualua-table

解决方案


好吧,我听说关于优化的第一条规则是“不要这样做!” “不要这样做!”。

有一个官方文档公开了一些优化 Lua 代码的方法,我推荐它。最重要的规则是更喜欢局部变量而不是全局变量,因为全局变量比局部变量慢 30%。

我们可以对前面的代码做的第一件事是编译它并检查字节码指令以了解执行时发生的情况。我将第一个函数存储在“test-1.lua”中,第二个函数存储在“test-2.lua”中。

> cat test-1.lua
dog={x=33,
func=function(self)
self.x=self.x*self.x
end
}

function TEST ()
  dog:func()
end
> luac54 -l -s test-1.lua
#
#(part of output omitted for clarity)
#
# Function: dog.func
#
function <test-1.lua:2,4> (6 instructions at 0000000000768740)
1 param, 3 slots, 0 upvalues, 1 local, 1 constant, 0 functions
        1       [3]     GETFIELD        1 0 0   ; "x"
        2       [3]     GETFIELD        2 0 0   ; "x"
        3       [3]     MUL             1 1 2
        4       [3]     MMBIN           1 2 8   ; __mul
        5       [3]     SETFIELD        0 0 1   ; "x"
        6       [4]     RETURN0
#
# Function: TEST (function to call dog.func)
#
function <test-1.lua:7,9> (4 instructions at 00000000000a8a90)
0 params, 2 slots, 1 upvalue, 0 locals, 2 constants, 0 functions
        1       [8]     GETTABUP        0 0 0   ; _ENV "dog"
        2       [8]     SELF            0 0 1k  ; "func"
        3       [8]     CALL            0 2 1   ; 1 in 0 out
        4       [9]     RETURN0

所以,如果我们要执行TEST10 次,我们至少需要执行 10*(4+6) 条字节码指令,也就是 100 条字节码指令。

> cat test-2.lua
cat={func=function()
x=x*x
end
}

x=33

function TEST ()
  cat.func()
end
> luac54 -l -s test-2.lua
#
#(part of output omitted for clarity)
#
# Function: cat.func
#
function <test-2.lua:1,3> (6 instructions at 00000000001b87f0)
0 params, 2 slots, 1 upvalue, 0 locals, 1 constant, 0 functions
        1       [2]     GETTABUP        0 0 0   ; _ENV "x"
        2       [2]     GETTABUP        1 0 0   ; _ENV "x"
        3       [2]     MUL             0 0 1
        4       [2]     MMBIN           0 1 8   ; __mul
        5       [2]     SETTABUP        0 0 0   ; _ENV "x"
        6       [3]     RETURN0
#
# Function: TEST (function to call cat.func)
#
function <test-2.lua:8,10> (4 instructions at 00000000001b8a80)
0 params, 2 slots, 1 upvalue, 0 locals, 2 constants, 0 functions
        1       [9]     GETTABUP        0 0 0   ; _ENV "cat"
        2       [9]     GETFIELD        0 0 1   ; "func"
        3       [9]     CALL            0 1 1   ; 0 in 0 out
        4       [10]    RETURN0

所以,如果我们要执行TEST10 次,我们至少需要执行 10*(4+6) 条字节码指令,也就是 100 条字节码指令......这和第一个版本完全一样!

显然,所有字节码指令的执行时间并不相同。C一些指令将在运行时花费更多时间,而其他指令将花费更多时间。两个整数相加可能比分配新表和初始化一些字段要快得多。那时,我们可以尝试做一个肮脏和毫无意义的事情microbenchmark来给我们一个想法。

可以将这段代码复制并粘贴到Lua解释器中:

> cat dirty-and-pointess-benchmark.lua
dog={x=33,
func=function(self)
self.x=self.x*self.x
end
}

cat={func=function()
x=x*x
end
}

x=33

function StartMeasure ()
  StartTime = os.clock()
end

function StopMeasure (TestName)
  local Duration = os.clock() - StartTime
  print(string.format("%s: %f sec", TestName, Duration))
end

function DoTest1 (Count)
  for Index = 1, Count do
    dog:func()
  end
end

function DoTest2 (Count)
  for Index = 1, Count do
    cat.func()
  end
end

COUNT = 5000000000

StartMeasure()
DoTest1(COUNT)
StopMeasure("VERSION_1")

StartMeasure()
DoTest2(COUNT)
StopMeasure("VERSION_2")

这段代码在我的电脑上给出了这个结果:

VERSION_1: 246.816000 sec
VERSION_2: 250.412000 sec

显然,对于大多数程序而言,差异可能可以忽略不计。我们应该始终尝试将更多时间花在编写正确的程序上,而花更少的时间来做微基准测试。


推荐阅读