在轮班操作后使用携带标志
Using carry flag after shift operation
对于以下代码的div/mod 部分:
int pow(int x, unsigned int n)
{
int y = 1;
while (n > 1)
{
auto m = n%2;
n = n/2;
if (m)
y *= x;
x = x*x;
}
return x*y;
}
我希望组装像
shr n
cmovc y, yx
但是 gcc/clang 甚至 icc 在这里不使用进位标志(使用 2 个寄存器和 and/test 代替(:https://godbolt.org/z/L6VUZ1
所以我想知道如果你手动编码它的最佳方法是什么以及为什么(ILP,依赖项,...(。
test/je
可以在主流 Intel 和 AMD CPU 上宏融合成单个 uop,因此在分支代码中,您只会通过使用班次的 CF 输出而不是更早的test/je
来节省代码大小并花费 1 个周期的分支错误检测延迟。
(不幸的是,gcc 在这里真的很愚蠢,并且在and edx,1
的结果上使用test edx,edx
,而不仅仅是使用test dl,1
或更好的test sil,1
来保存mov
。test esi,1
将使用imm32编码,因为test
没有test r/m32, imm8
编码,所以编译器知道读取窄寄存器以获得test
。
但是在像 clang 使用的无分支实现中,是的,您可以通过使用 CF 输出来保存 uopcmovc
而不是单独计算输入以进行cmove
test
。 你仍然没有缩短关键路径,因为test
和shr
可以并行运行,而像 Haswell 或 Ryzen 这样的主流 CPU 拥有足够宽的管道来充分利用所有 ILP,只是imul
循环承载依赖链上的瓶颈。 (https://agner.org/optimize/(。
实际上,这是cmov
->imul
->下一个迭代 dep 链y
这是瓶颈。 在 Haswell 及更早版本上,cmov
是 2 个周期延迟 (2 uops(,因此总 dep 链为 2+3 = 5 个周期。 (流水线乘法器意味着做额外的y*=1
乘法不会减慢x*=x
部分的速度,反之亦然;它们可以同时在飞行中,只是不会在同一周期中开始。
如果你对不同的碱基重复使用相同的n
,分支版本应该预测得很好,并且非常好,因为分支预测 + 推测执行将数据依赖链解耦。
否则,最好吃掉无分支版本的更长延迟,而不是遭受分支未命中。
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 重载操作程序时出错>>用于类中的字符串 memebr
- 对字符串进行位操作
- 我可以在 C++ 中的函数体之外进行操作吗?
- MPI突然停止了对多个核心的操作
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 对字符数组中的元素执行逐位操作
- 如何在directx/c++中进行平移/缩放操作
- 逐位操作的隐式类型转换
- 为什么一个向量上的多线程操作很慢
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 即使使用调试编译标志,表达式也是"optimized out"
- 在 CMake 中为每个目标设置编译器/链接器标志
- 位移操作和位掩码未检测到重复字符
- 如何进行特定的位操作?
- 当我们进行一些操作时,应该使用什么'std::string'或'std::stringstream'?
- 字符串操作 - 字符计数
- 在轮班操作后使用携带标志
- 如何正确设置 ios 标志以进行流操作?
- C 很容易使用枚举进行标志和位操作