在循环内部或外部声明局部更好吗?

Man*_*ill 6 performance lua

我习惯这样做:

do
    local a
    for i=1,1000000 do
        a = <some expression>
        <...> --do something with a
    end
end
Run Code Online (Sandbox Code Playgroud)

代替

for i=1,1000000 do
    local a = <some expression>
    <...> --do something with a
end
Run Code Online (Sandbox Code Playgroud)

我的理由是,创建局部变量1000000次的效率低于仅创建一次并在每次迭代时重用它.

我的问题是:这是真的还是我缺少另一个技术细节?我在问,因为我没有看到有人这样做,但不确定原因是因为优势太小还是因为事实上更糟.更好的意思是使用更少的内存和更快的运行.

rya*_*son 11

像任何表现问题一样,先测量.在unix系统中,您可以使用时间:

time lua -e 'local a; for i=1,100000000 do a = i * 3 end'
time lua -e 'for i=1,100000000 do local a = i * 3 end'
Run Code Online (Sandbox Code Playgroud)

输出:

 real   0m2.320s
 user   0m2.315s
 sys    0m0.004s

 real   0m2.247s
 user   0m2.246s
 sys    0m0.000s
Run Code Online (Sandbox Code Playgroud)

在Lua中,本地版本看起来更快一点,因为它没有初始化a为零.但是,没有理由使用它,使用最本地的范围,因为它更具可读性(这是所有语言的好样式:请参阅这个问题C, JavaC#)

如果您正在重用表而不是在循环中创建表,那么可能会有更显着的性能差异.无论如何,只要有可能,就要衡量并支持可读性.

  • 发现.可读性比_insignificant_性能调整重要100倍. (5认同)

Dra*_*rgy 6

我认为编译器处理变量的方式存在一些混淆.从高级别的人类角度来看,考虑定义和销毁变量以获得与之相关的某种"成本"是很自然的.

然而,优化编译器并不一定如此.您在高级语言中创建的变量更像是内存中的临时"句柄".编译器查看这些变量,然后将其转换为中间表示(更靠近机器的地方)并找出存储所有内容的位置,主要是为了分配寄存器(CPU最直接的内存形式).然后它将IR转换为机器代码,其中"变量"的概念甚至不存在,只存储数据(寄存器,缓存,dram,磁盘).

此过程包括为多个变量重复使用相同的寄存器,前提是它们不会相互干扰(前提是它们不需要同时进行:不能同时"实时").

换句话说,代码如下:

local a = <some expression>
Run Code Online (Sandbox Code Playgroud)

生成的程序集可能类似于:

load gp_register, <result from expression>
Run Code Online (Sandbox Code Playgroud)

...或者它可能已经从寄存器中的某个表达式得到了结果,并且变量最终完全消失(只是为它使用相同的寄存器).

......这意味着变量的存在没有"成本".它只是直接转换为始终可用的寄存器.由于寄存器始终存在,因此"创建寄存器"没有"成本".

当您开始在更广泛(更少本地)的范围内创建变量时,与您的想法相反,您实际上可能会减慢代码速度.当你表面上这样做时,你会对编译器的寄存器分配起作用,并使编译器更难以找出要为什么分配的寄存器.在这种情况下,编译器可能会将更多变量泄漏到堆栈中,这样做效率较低,实际上会附加成本.智能编译器仍然可以发出同样有效的代码,但实际上你可以放慢速度.在这里帮助编译器通常意味着在较小的范围内使用更多的局部变量,在这些范围内,您最有效地提高效率.

在汇编代码中,尽可能重用相同的寄存器可以有效避免堆栈溢出.在带有变量的高级语言中,它恰恰相反.减少变量的范围有助于编译器找出它可以重用的寄存器,因为使用更局部的变量作用域有助于告知编译器哪些变量不是同时存在的.

现在,当你开始在C++这样的语言中使用用户定义的构造函数和析构函数逻辑时会有异常,其中重用对象可能会阻止可重用的对象的冗余构造和破坏.但这并不适用于像Lua这样的语言,其中所有变量基本上都是普通的旧数据(或处理垃圾收集数据或用户数据).

使用较少局部变量可能会看到改进的唯一情况是,这会以某种方式减少垃圾收集器的工作.但是,如果您只是重新分配给同一个变量,情况就不是这样了.为此,您必须重用整个表或用户数据(无需重新分配).换句话说,重用表的相同字段而不重新创建一个新表可能在某些情况下有所帮助,但重用用于引用表的变量不太可能有所帮助,实际上可能会阻碍性能.