为何Clang对两个uint64_t整数做除法时会生成特殊汇编输出?
Clang 生成更长汇编代码的原因
这是Clang针对x86平台除法指令的性能差异做的特化速度优化,核心逻辑是优先使用更快的32位除法指令完成符合条件的64位除法运算。
x86 除法指令的性能差异是优化的根本前提
x86架构中,div除法指令的性能随操作数位宽提升会出现大幅下跌,以主流Intel Skylake架构为例:
- 32位
div指令平均延迟为10个时钟周期,每4个周期即可发起一次新的除法请求 - 64位
div指令平均延迟高达3588个时钟周期,最快也要21个周期才能发起一次新请求,性能差距可达48倍
分支逻辑的具体作用
Clang新增的几行代码是做了一层极低开销的场景判断:
- 将被除数
num和除数den做按位或操作,结果存入rcx寄存器 - 将
rcx右移32位,判断结果是否为0:如果为0,说明num和den的高32位全为0,两个数的实际值都小于2^32,此时用32位div计算的结果和64位div完全一致 - 符合条件时走32位除法分支,否则走原本的64位除法分支
这个判断的额外成本可以忽略:or、shr、跳转指令都是单周期指令,且实际业务中绝大多数64位无符号除法的操作数都不会超过32位范围,分支预测成功率极高,整体性能远高于直接使用64位div。
其他相关现象的解释
- 当使用
uint32_t类型时,编译器从类型层面即可确定两个操作数都是32位,不需要额外判断,直接生成32位div指令即可 - Clang 10及更早版本没有加入这个特化优化,所以生成的汇编和GCC一致
- 如果不需要这个优化,可以给Clang添加编译参数
-mno-avoid-64bit-div,即可生成和GCC完全相同的无分支64位除法代码
内容的提问来源于stack exchange,提问作者Gary Allen
相关产品推荐
相关产品推荐

