装配跳转与多次加或跳转前做加号(性能)
Assembly Jump with Multiple plus or do plus before jump (performance)
在 Assembly 中,如果我有一个 JUMP table,地址超过 2000 个标签:
.TABLE:
DD .case0
DD .case1
DD .case2
DD .case3
DD .case4
...
...
...
DD .case2000
哪种方式更适合跳转:
方式一:
mov r12d, .TABLE ; r12d or any other registers
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
add ebx, r12d ; ebx = address for Jumping
jmp ebx
方法 2:(与方法 1 相同,但 'add ebx, r12d'
已删除并更改为 'jmp [ebx+r12d]'
)
mov r12d, .TABLE ; r12d or any other registers
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
jmp [ebx+r12d]
方式 3:
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
jmp [ebx + .TABLE]
在'way 1'中,由于额外的功能,我们有源代码大小问题,但我认为它比其他跳跃方式有更好的性能,因为我将有大约 2000 次跳跃(不规则跳跃(可能来自case0 到 case1000 或 ...)
那么对于跳跃性能,在有很多跳跃的源代码中哪种方式更好?
如果您可以使用 32 位地址大小来压缩跳转 table 相对于在 64 位模式下使用 qword 指针,则使用 32 位地址大小是一个不错的选择。
否则你想要加载 16 位或 32 位偏移量(movzx
或 mov
)并从 RIP 相对 LEA 添加到 64 位的一些 64 位基地址位代码。 (这也使其与位置无关)。
fewest instructions is not always a solution !
但在这种情况下,最少的指令也是最少的 uops。 [disp32 + reg]
寻址模式很有效。
如果您要考虑使用更多指令,那么将指针加载到 jmp reg
的寄存器中而不是使用 jmp [mem]
,而不是进一步简化寻址模式。
https://agner.org/optimize/表明Intel Sandybridge family上的jmp mem
仍然只有1个fused-domain uop,负载微融合到端口6跳uop。
因此,单独的 mov
负载实际上会在前端花费 更多 微指令。
(索引寻址模式可能会取消层压;jmp [.TABLE + ebx*4]
在 issue/rename 阶段会花费 2 微指令,但在解码器和微指令缓存中仍然只有 1 微指令。但看起来你有一个字节偏移量由于某种原因存储在内存中,因此您不需要缩放索引。)
在 Assembly 中,如果我有一个 JUMP table,地址超过 2000 个标签:
.TABLE:
DD .case0
DD .case1
DD .case2
DD .case3
DD .case4
...
...
...
DD .case2000
哪种方式更适合跳转:
方式一:
mov r12d, .TABLE ; r12d or any other registers
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
add ebx, r12d ; ebx = address for Jumping
jmp ebx
方法 2:(与方法 1 相同,但 'add ebx, r12d'
已删除并更改为 'jmp [ebx+r12d]'
)
mov r12d, .TABLE ; r12d or any other registers
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
jmp [ebx+r12d]
方式 3:
mov ebx, [r13d] ; r13d holds the id of case * 4 so we don't need to '4 * ebx'
jmp [ebx + .TABLE]
在'way 1'中,由于额外的功能,我们有源代码大小问题,但我认为它比其他跳跃方式有更好的性能,因为我将有大约 2000 次跳跃(不规则跳跃(可能来自case0 到 case1000 或 ...)
那么对于跳跃性能,在有很多跳跃的源代码中哪种方式更好?
如果您可以使用 32 位地址大小来压缩跳转 table 相对于在 64 位模式下使用 qword 指针,则使用 32 位地址大小是一个不错的选择。
否则你想要加载 16 位或 32 位偏移量(movzx
或 mov
)并从 RIP 相对 LEA 添加到 64 位的一些 64 位基地址位代码。 (这也使其与位置无关)。
fewest instructions is not always a solution !
但在这种情况下,最少的指令也是最少的 uops。 [disp32 + reg]
寻址模式很有效。
如果您要考虑使用更多指令,那么将指针加载到 jmp reg
的寄存器中而不是使用 jmp [mem]
,而不是进一步简化寻址模式。
https://agner.org/optimize/表明Intel Sandybridge family上的jmp mem
仍然只有1个fused-domain uop,负载微融合到端口6跳uop。
因此,单独的 mov
负载实际上会在前端花费 更多 微指令。
(索引寻址模式可能会取消层压;jmp [.TABLE + ebx*4]
在 issue/rename 阶段会花费 2 微指令,但在解码器和微指令缓存中仍然只有 1 微指令。但看起来你有一个字节偏移量由于某种原因存储在内存中,因此您不需要缩放索引。)