CMOVcc 是否被视为分支指令?

Is CMOVcc considered a branching instruction?

我有这个 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是否是分支指令。是吗?如果是这样,我该如何重新排列我的代码以使其不分支?

不,这不是一个分支,这就是 cmovcc 的重点。

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

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

这是好是坏完全取决于用例。另见 了解更多关于 cmov 上行/下行


请注意 repne scasb 并不快。 "Fast Strings" 仅适用于 rep stos / movs。

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

(例如,请参阅 glibc 的 memchr 将整个缓存行的 ORing pcmpeqb 结果一起提供给一个 pmovmskb,IIRC。然后返回并找出实际命中的位置.)

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

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

快速strlen:

  • 显示了使用 SSE2 的 16 字节对齐输入的简单未展开 strlen。
  • 链接到更多关于 glibc 中手动优化的 asm strlen 函数的内容。 (以及如何在 GNU C 中使 bithack strlen 避免严格别名 UB。)
  • https://codereview.stackexchange.com/a/213558 scalar bithack strlen, including the same 4-byte-at-a-time bithack that the glibc question was about. Better than byte-at-a-time but pointless with SSE2 (which x86-64 guarantees). However, @CodyGray's tutorial-style answer may be a useful for beginners. Note that it doesn't take into account