将4个1字节变量合并为4字节字:两种移位OR方法的汇编性能对比
性能差异结论
在主流x86-64架构CPU上,这两种实现的性能几乎没有可观测的差异,二者的执行耗时差距会小于纳秒级别的测量精度,属于可忽略的范畴。
为什么二者性能接近
- 现代CPU普遍支持超标量乱序执行,只要指令之间没有数据依赖,就可以同时调度多个指令执行:
- method1的指令序列看起来是串行的
sal>or交替,但每条指令的输入依赖只有前一步的计算结果,单条sal和or在x86上都是单周期延迟、多发射吞吐量的指令,整个依赖链的总延迟只有6个周期左右。 - method2的三次
sal指令互相没有数据依赖,CPU可以在同一个周期内并行执行这三条移位,后续的三次or的依赖链长度也只有3个周期,理论上延迟略低于method1,但这种差异远小于CPU前端取指、分支预测等带来的波动,实际运行中几乎测不出来。
- method1的指令序列看起来是串行的
- 二者的指令总数量一致,都没有访存指令,全部是寄存器操作,CPU前端解码、发射的开销完全相同。
指令顺序的影响
你观察到的指令排列差异,本质是两种实现的数据流逻辑不同:method1是迭代式的逐步移位合并,method2是先对每个字节单独移位再统一合并。
但你看到的汇编指令顺序只是编译器生成的静态排列,实际执行时CPU的乱序调度器会重新安排指令的执行顺序,只要没有数据依赖就会尽可能并行,所以静态的指令顺序对最终执行速度没有显著影响。
更大位宽下的差异
如果是合并更大的位宽(比如把8个1字节合并为8字节、16个1字节合并为16字节),这时候二者的性能差异会逐渐显现:
- method1的迭代逻辑会导致依赖链长度线性增长:合并N个字节的依赖链长度是
2*(N-1)个周期。 - method2的移位全部可以并行执行,后续的
or操作也可以两两并行合并,依赖链长度是log2(N)级别的增长,位宽越大性能优势越明显。
额外优化建议
其实这两种写法都不是最优的,你可以直接用内存拼接的写法:
int combine(unsigned char a, unsigned char b, unsigned char c, unsigned char d) { unsigned char buf[4] = {a, b, c, d}; return *(int*)buf; }
gcc开-O2优化后会直接把这段代码优化成单条移位合并的指令,比你现在的两种实现指令数更少。
内容的提问来源于stack exchange,提问作者0xdeadbeef
相关产品推荐
相关产品推荐

