首页 > 解决方案 > 小阵列的最快偏移读取

问题描述

为了速度,我想读取第 9 个寄存器中的值引用的 8 个寄存器之一。我看到的最快方法是使用 3 个条件跳转(检查第 9 个寄存器中的 3 位)。这应该比使用偏移内存读取执行此操作的标准方法具有更短的延迟,但这仍然需要至少 6 个时钟周期(每个位检查至少一个测试加上一个条件 jmp)。

是否有任何商业 CPU(最好是 x86/x64)具有执行此“偏移寄存器读取”的内在特性,延迟仅为一个时钟周期?

从理论上讲,一个优化的 CPU 可以通过一次加法和一次移动来做到这一点,所以两个或一个时钟周期似乎很容易……架构不关心加速小型阵列的偏移读取是否有一些普遍的原因?

标签: performanceassemblyx86cpu-architectureintrinsics

解决方案


如今,将 CPU 寄存器视为一个数组确实不是一种常见的方法。我知道的最后一个允许这样做的架构是 PDP11,它在 80 年代后期消失了。为什么不像其他数组一样将数组放入某个内存位置?

也就是说,您可以使用计算跳转。这也用控制依赖替换了数据依赖(索引寻址模式),因此乱序 exec 不必等待索引输入甚至准备好就可以开始运行使用最终 RAX 的代码。当然,这假设了正确的分支预测,如果索引经常变化,这是不太可能的。一个分支错误预测会花费许多周期的小工作来完成,但是在 L1d 缓存中命中的负载的小延迟很容易与独立工作重叠。

吞吐量成本高于内存中的数组:一些地址计算,一次跳转,一次移动和 a ret,而不仅仅是 amov甚至是具有索引寻址模式的内存操作数。

要内联此代码,只需将 替换为jmp *%raxcall *%rax花费另一个 uop。或者将ret指令替换jmp为底部的标签,并将跳转表的步幅增加到 8 以考虑更长的编码。

    # select a register from r8...r15 according to the value in rdi
select:
    lea labels-4*8(%rip),%rax # rdi = 8 is the first jump table entry
    lea (%rax,%rdi,4),%rax    # pointer to the appropriate entry
    jmp *%rax                 # computed jump

    .align 4
labels:
    mov %r8, %rax
    ret

    .align 4
    mov %r9, %rax
    ret

    .align 4
    mov %r10, %rax
    ret

    .align 4
    mov %r11, %rax
    ret

    .align 4
    mov %r12, %rax
    ret

    .align 4
    mov %r13, %rax
    ret

    .align 4
    mov %r14, %rax
    ret

    .align 4
    mov %r15, %rax
    ret

虽然这可能比三个条件跳转快(取决于访问模式),但它肯定不会仅仅使用数组。


推荐阅读