【问题标题】:Is CMOVcc considered a branching instruction?CMOVcc 是否被视为分支指令?
【发布时间】:2019-12-22 18:22:04
【问题描述】:

我有这个 memchr 代码,我正在尝试使其不分支:

.globl memchr
memchr:
        mov %rdx, %rcx
        mov %sil, %al
        cld
        repne scasb
        lea -1(%rdi), %rax
        test %rcx, %rcx
        cmove %rcx, %rax
        ret

我不确定cmove 是否是分支指令。是吗?如果是这样,我该如何重新排列我的代码以使其不分支?

【问题讨论】:

  • 你不需要cld;所有标准调用约定保证/要求 DF=0 调用/ret。此外,movzbl %sil, %eax 将比合并到 RAX 的低字节更有效。或者只是 mov %esi, %eax 很好,除非你的调用者只在 P6 系列 CPU 上编写 AL。
  • 我认为由于缺乏研究努力而被否决。例如google for is cmov a branch 有几个命中率,这一切都很明显,包括 Why is a conditional move not vulnerable for Branch Prediction Failure?(可能是重复的)。我认为没有任何真正的方法可以改善这个问题。包含任何具体的错误主张或误导性来源只会导致反驳它们的更臃肿的答案。

标签: assembly x86-64 cpu-architecture micro-optimization branch-prediction


【解决方案1】:

不,这不是一个分支,这就是cmovcc 的全部意义所在。

这是一个 ALU 选择,它对两个输入都有数据依赖,而不是控制依赖。 (使用内存源it unconditionally loads the memory source,与真正 NOPed 的 ARM 谓词加载指令不同。因此,您不能将它与可能错误的指针一起用于无分支边界或 NULL 检查。这可能是最清楚的说明,它绝对不是分支.)

但无论如何,这不是以任何方式预测或推测的;就 CPU 调度程序而言,它就像一条adc 指令:2 个整数输入 + FLAGS 和 1 个整数输出。 (与adc/sbb 的唯一区别是它不写 FLAGS。当然,它运行在具有不同内部结构的执行单元上)。

这是好是坏完全取决于用例。另请参阅gcc optimization flag -O3 makes code slower than -O2 了解更多关于cmov 上行/下行的信息


请注意,repne scasb 并不快。“快速字符串”仅适用于 rep stos / movs。

repne scasb 在现代 CPU 上每个时钟周期运行大约 1 个计数,即通常比简单的 SSE2 pcmpeqb/pmovmskb/test+jnz 循环差 16 倍左右。通过巧妙的优化,您可以走得更快,每个时钟最多 2 个向量使负载端口饱和。

(例如,查看 glibc 的 memchr 对整个高速缓存行进行 ORing pcmpeqb 结果以提供一个 pmovmskb,IIRC。然后返回并找出实际命中的位置。)

repne scasb 也有启动开销,但微码分支与常规分支不同:它在 Intel CPU 上不是分支预测的。所以这不能误判,但对于性能来说,除了非常小的缓冲区之外,这完全是垃圾。

SSE2 是 x86-64 和高效未对齐负载的基线 + pmovmskb 使其成为 memchr 的明智之选,您可以在其中检查长度 >= 16 以避免进入未映射的页面。

快速strlen

【讨论】:

  • "注意repne scasb 并不快。" -- 我知道。我打算稍后用更快的东西替换它,但现在它很小而且可以工作。
  • 我也没有任何 SSE/2/3/4.1 方面的经验。
  • @JL2210: memchr 是学习使用 SSE2 进行简单元素匹配搜索的好方法;知道大小作为函数 arg(而不是隐式长度)使其比 strchr 简单得多。
  • @JL2210 您可能对this answer 感兴趣,它讨论了使用基本x86 指令(即不使用任何SSE)优化strlen 的方法。 memchr 可以使用类似的方法,但您无法围绕搜索 0 字节进行如此大量的优化。当然,正如彼得所说,SSE 是让它真正快速的方法。我也编写了该代码并对其进行了基准测试。如果您想了解更多,请询问更多有关它的问题。我可以将该答案再扩展 10 页,但我最终会用完空间,人们会厌倦阅读。
  • @JL2210:更新了我的答案,其中包含一些指向 SIMD strlen 的链接,如本答案中所述。 memchr 使用字中零字节的 bithack 通常只对正在搜索的字节执行 xor。您可以使用c * 0x01010101UL 或其他方式进行广播。
猜你喜欢
  • 2020-06-30
  • 2014-02-07
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
相关资源
最近更新 更多