我正在Lua写一个非常简单的循环,用于我正在开发的LÖVE游戏。我知道我会浪费更多的时间来担心这个问题,而不是花在任何CPU时钟周期上,这个问题的答案可以拯救我,但我想要更深入地了解它是如何工作的。
循环的当前主体是这样的:
local low = mid - diff
local high = mid + diff
love.graphics.line(low, 0, low, wheight)
love.graphics.line(high, 0, high, wheight)
我想知道将它保持原样或将其更改为更高的计算效率:
love.graphics.line(mid - diff, 0, mid - diff, wheight)
love.graphics.line(mid + diff, 0, mid + diff, wheight)
对于第二个身体,我必须每次计算两次低和高差异。第一个,我必须将它们存储在内存中,并且每次访问它们两次。
哪个更有效?
答案 0 :(得分:2)
简短的回答是它根本不可能有任何区别。例如,即使存在任何差异,您旁边的代码也会绘制一条线。与加法和减法相比,使用本机代码实现非常优化的Bresenham绘制一个别名行是非常昂贵的。即使是单独的函数调用也可能使这个成本相形见绌。
对于第二个身体,我必须计算低和高的差异 每次两次。首先,我必须将它们存储在内存和访问中 他们两次。
不一定是这种情况。变量不一定以表达式不存储的方式“存储内存”。他们可以直接映射到寄存器。同样,避免变量不一定“避免记忆”。表达式同样会被计算并存储在寄存器中,无论您是否明确地将中间结果分配给变量。
因此,从内存的角度来看,两个版本的代码都需要使用寄存器来存储计算的中间结果。
当您只是涉及简单变量时,Memoization不一定会有这种内存开销,主要是因为类型直接映射到没有堆栈溢出的寄存器。当你开始提前计算整个数组/表时,如果memoization意味着更多的DRAM访问(在这种情况下,内存开销可能超过节省),有时进行额外的计算将比memoization更快。但是像数字这样的简单POD类型变量没有DRAM开销,它们直接映射到寄存器。换句话说,它们通常是免费的:无论是否将表达式的结果分配给局部变量,编译器都会发出相同的机器代码 - 需要相同数量的寄存器。
直接映射到GP寄存器的数据类型的局部变量最好只考虑在您处于高级编码区时才存在。当JIT或解释器将您的代码编译成机器理解的形式时,无论您是否创建了这些变量,它们都会消失并变成寄存器。
可能最终的问题是,如果存在任何差异,是否可以消除冗余计算。只需要最简单的优化器就可以确定在完全相同的语句中写入两次的mid - diff
只需要计算一次。如果在到达IR指令选择和寄存器分配阶段时没有得到优化,我会感到惊讶。
但是,即使它被证明是一个惊喜,并且Lua解释器是如此低效,以至于无法识别完全冗余的计算并且无论如何都执行它,再次,你有代码旁边的代码呈现一条线(涉及到loopy rasterization)。相对而言,即使冗余,这实际上也是免费的。在这里,不值得为这些小东西出汗,这是来自痴迷于剃须时钟周期的人。