装配跳转与多次加或跳转前做加号(性能)

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 位偏移量(movzxmov)并从 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 微指令。但看起来你有一个字节偏移量由于某种原因存储在内存中,因此您不需要缩放索引。)