assembly - CMOVcc 是否被视为分支指令?
问题描述
我有这段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 选择,它对两个输入都有数据依赖,而不是控制依赖。(对于内存源,它无条件加载内存源,这与真正 NOPed 的 ARM 谓词加载指令不同。因此,您不能将它与可能错误的指针一起用于无分支边界或 NULL 检查。这可能是最清楚的说明,它绝对是不是一个分支。)
但无论如何,它不是以任何方式预测或推测的。就 CPU 调度程序而言,它就像一条adc
指令:2 个整数输入 + FLAGS 和 1 个整数输出。adc
(与/的唯一区别sbb
是它不写 FLAGS。当然,它运行在具有不同内部结构的执行单元上)。
这是好是坏完全取决于用例。另请参阅gcc 优化标志 -O3 使代码比 -O2 慢,以了解更多关于cmov
上行/下行的信息
请注意,这repne scasb
并不快。 “快速字符串”仅适用于 rep stos / movs。
repne scasb
在现代 CPU 上每个时钟周期运行大约 1 个计数,即通常比简单的 SSE2 // 循环差大约pcmpeqb
16倍pmovmskb
。test+jnz
通过巧妙的优化,您可以走得更快,每个时钟最多 2 个向量使负载端口饱和。
(例如,请参阅 glibc 的memchr
ORingpcmpeqb
结果,将整个缓存行放在一起以提供一个pmovmskb
IIRC。然后返回并找出实际命中的位置。)
repne scasb
也有启动开销,但微码分支与常规分支不同:它不是 Intel CPU 上的分支预测。所以这不能错误预测,但是对于除了非常小的缓冲区之外的任何东西的性能来说都是垃圾。
SSE2 是 x86-64 和高效未对齐负载的基线 +pmovmskb
使其成为memchr
您可以检查长度 >= 16 以避免进入未映射页面的明智之举。
快速 strlen:
- 为什么启用优化后这段代码会慢 6.5 倍?显示了使用 SSE2 的 16 字节对齐输入的简单未展开 strlen。
- 为什么glibc的strlen需要这么复杂才能快速运行?链接到有关 glibc 中手动优化的 asm strlen 函数的更多内容。(以及如何在 GNU C 中进行 bithack strlen 避免严格混叠 UB。)
- https://codereview.stackexchange.com/a/213558 标量 bithack strlen,包括 glibc 问题所涉及的相同的 4-byte-at-a-time bithack。比一次字节好,但对 SSE2 毫无意义(x86-64 保证)。但是,@CodyGray 的教程式答案可能对初学者有用。请注意,它没有考虑在 x86 和 x64 上的同一页面内读取缓冲区末尾是否安全?
推荐阅读
- linkedin - 连接 API - “权限不足”
- android - 如何在与 Room 的多对多关系中实现模型的单个实例
- jmeter - Script6.groovy: : 意外输入:Jmeter JSR233 预处理器错误
- leaflet - React Leaflet - 将 pluscode 区域绘制为网格
- vue.js - eslint 正在检查 node_modules Ionic Vue 项目
- javascript - 用javascript标记像素的连接组件?
- r - 在两个向量中的值之间过滤数据框并将结果添加到 R 中的列表
- macos - Mac OS终端中的问号标志
- mysql - 如何使用选择查询使用excel文件中的数据在数据库中搜索?
- python - Matplotlib 阶梯图旋转