Lua 编译器是否优化局部变量?
Does the Lua compiler optimize local vars?
当前的 Lua 编译器是否足够聪明,可以优化掉为清晰起见而使用的局部变量?
local top = x - y
local bottom = x + y
someCall(top, bottom)
还是手动内联 运行 更快?
someCall(x - y, x + y)
由于 Lua 经常将源代码编译成字节码,它被设计成一个快速的单遍编译器。它确实做了一些常量折叠,但除此之外没有太多优化。您通常可以通过执行 luac -l -l -p file.lua
并查看生成的(反汇编的)字节码来检查编译器做了什么。
在你的情况下 Lua 代码
function a( x, y )
local top = x - y
local bottom = x + y
someCall(top, bottom)
end
function b( x, y )
someCall(x - y, x + y)
end
当 运行 到 luac5.3 -l -l -p file.lua
时,结果为以下字节码列表(跳过一些不相关的部分):
function <file.lua:1,5> (7 instructions at 0xcd7d30)
2 params, 7 slots, 1 upvalue, 4 locals, 1 constant, 0 functions
1 [2] SUB 2 0 1
2 [3] ADD 3 0 1
3 [4] GETTABUP 4 0 -1 ; _ENV "someCall"
4 [4] MOVE 5 2
5 [4] MOVE 6 3
6 [4] CALL 4 3 1
7 [5] RETURN 0 1
constants (1) for 0xcd7d30:
1 "someCall"
locals (4) for 0xcd7d30:
0 x 1 8
1 y 1 8
2 top 2 8
3 bottom 3 8
upvalues (1) for 0xcd7d30:
0 _ENV 0 0
function <file.lua:7,9> (5 instructions at 0xcd7f10)
2 params, 5 slots, 1 upvalue, 2 locals, 1 constant, 0 functions
1 [8] GETTABUP 2 0 -1 ; _ENV "someCall"
2 [8] SUB 3 0 1
3 [8] ADD 4 0 1
4 [8] CALL 2 3 1
5 [9] RETURN 0 1
constants (1) for 0xcd7f10:
1 "someCall"
locals (2) for 0xcd7f10:
0 x 1 6
1 y 1 6
upvalues (1) for 0xcd7f10:
0 _ENV 0 0
如您所见,第一个变体(a
函数)有两个额外的 MOVE
指令和两个额外的局部变量。
如果您对操作码的详细信息感兴趣,可以查看 lopcodes.h 中 OpCode
枚举的注释。
例如。 OP_ADD
的操作码格式为:
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
所以上面的 2 [3] ADD 3 0 1
从寄存器 0 和 1 中获取值(在这种情况下是本地 x
和 y
),将它们相加,并将结果存储在register 3,是这个函数中的第二个操作码,对应的源码在第3行。
当前的 Lua 编译器是否足够聪明,可以优化掉为清晰起见而使用的局部变量?
local top = x - y
local bottom = x + y
someCall(top, bottom)
还是手动内联 运行 更快?
someCall(x - y, x + y)
由于 Lua 经常将源代码编译成字节码,它被设计成一个快速的单遍编译器。它确实做了一些常量折叠,但除此之外没有太多优化。您通常可以通过执行 luac -l -l -p file.lua
并查看生成的(反汇编的)字节码来检查编译器做了什么。
在你的情况下 Lua 代码
function a( x, y )
local top = x - y
local bottom = x + y
someCall(top, bottom)
end
function b( x, y )
someCall(x - y, x + y)
end
当 运行 到 luac5.3 -l -l -p file.lua
时,结果为以下字节码列表(跳过一些不相关的部分):
function <file.lua:1,5> (7 instructions at 0xcd7d30)
2 params, 7 slots, 1 upvalue, 4 locals, 1 constant, 0 functions
1 [2] SUB 2 0 1
2 [3] ADD 3 0 1
3 [4] GETTABUP 4 0 -1 ; _ENV "someCall"
4 [4] MOVE 5 2
5 [4] MOVE 6 3
6 [4] CALL 4 3 1
7 [5] RETURN 0 1
constants (1) for 0xcd7d30:
1 "someCall"
locals (4) for 0xcd7d30:
0 x 1 8
1 y 1 8
2 top 2 8
3 bottom 3 8
upvalues (1) for 0xcd7d30:
0 _ENV 0 0
function <file.lua:7,9> (5 instructions at 0xcd7f10)
2 params, 5 slots, 1 upvalue, 2 locals, 1 constant, 0 functions
1 [8] GETTABUP 2 0 -1 ; _ENV "someCall"
2 [8] SUB 3 0 1
3 [8] ADD 4 0 1
4 [8] CALL 2 3 1
5 [9] RETURN 0 1
constants (1) for 0xcd7f10:
1 "someCall"
locals (2) for 0xcd7f10:
0 x 1 6
1 y 1 6
upvalues (1) for 0xcd7f10:
0 _ENV 0 0
如您所见,第一个变体(a
函数)有两个额外的 MOVE
指令和两个额外的局部变量。
如果您对操作码的详细信息感兴趣,可以查看 lopcodes.h 中 OpCode
枚举的注释。
例如。 OP_ADD
的操作码格式为:
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
所以上面的 2 [3] ADD 3 0 1
从寄存器 0 和 1 中获取值(在这种情况下是本地 x
和 y
),将它们相加,并将结果存储在register 3,是这个函数中的第二个操作码,对应的源码在第3行。