gpt4 book ai didi

c++ - 设置进位标志的最快方法

转载 作者:行者123 更新时间:2023-11-28 00:06:43 25 4
gpt4 key购买 nike

我正在做一个循环来对两个数组求和。我的目标是通过避免进位检查来实现 c = a + b; carry = (c<a) .我丢了 CF当我进行循环测试时,使用 cmp说明。

目前,我正在使用 JESTC测试并设置先前保存的状态 CF .但是跳跃需要更多更少的 7 个周期,这对我想要的来说已经很多了。

   //This one is working
asm(
"cmp $0,%0;"
"je 0f;"
"stc;"
"0:"
"adcq %2, %1;"
"setc %0"

: "+r" (carry), "+r" (anum)
: "r" (bnum)
);

我已经尝试使用 SAHF (2 + 2(mov)个周期),但这不起作用。

   //Do not works
asm(
"mov %0, %%ah;"
"sahf;"
"adcq %2, %1;"
"setc %0"

: "+r" (carry), "+r" (anum)
: "r" (bnum)
);

任何人都知道设置 CF 的方法更快速?就像直接移动或类似的东西..

最佳答案

Looping without clobbering CF will be faster .请参阅该链接以获得更好的 asm 循环。

不要尝试在 C 循环中只编写带有内联 asm 的 adc。这是不可能的,因为你不能要求 gcc 不破坏标志。尝试使用 GNU C 内联 asm 学习 asm 比编写独立函数更难,尤其是。在这种情况下,您试图保留进位标志。

您可以使用 setnc %[carry] 保存并使用 subb $1, %[carry] 恢复。 (或者我猜是 cmpb $1, %[carry]。)或者正如 Stephen 指出的那样,negb %[carry]

0 - 1 产生进位,但 1 - 1 不产生进位。

使用 uint8_t 变量来保存进位,因为您永远不会将它直接添加到 %[anum]。这避免了 partial-register slowdowns 的任何机会.例如

uint8_t carry = 0;
int64_t numa, numb;

for (...) {
asm ( "negb %[carry]\n\t"
"adc %[bnum], %[anum]\n\t"
"setc %[carry]\n\t"
: [carry] "+&r" (carry), [anum] "+r" (anum)
: [bnum] "rme" (bnum)
: // no clobbers
);
}

您还可以为寄存器源 reg/mem dest 提供替代约束模式。我用了an x86 "e" constraint而不是 "i",因为 64 位模式仍然只允许 32 位符号扩展立即数。 gcc 必须自己将更大的编译时常量放入寄存器中。进位被早期破坏,所以即使它和 bnum 都是 1 开始,gcc 也不能对两个输入使用相同的寄存器。

这仍然很糟糕,并且将循环携带的依赖链的长度从 2c 增加到 4c(Intel pre-Broadwell),或者从 1c 增加到 3c(Intel BDW/Skylake 和 AMD)。

所以你的循环以 1/3 的速度运行,因为你使用的是一个 kludge,而不是用 asm 编写整个循环。


此答案的先前版本建议直接添加进位,而不是将其恢复到 CF 中。这种方法有一个致命的缺陷:它混淆了进入本次迭代的传入进位和进入下一次迭代的传出进位。


此外,sahf 是从标志中设置 AH。 lahf 是将 AH 加载到标志中(它对标志的整个低 8 位进行操作。将这些指令配对;不要在你得到的 0 或 1 上使用 lahf来自 setc

阅读 insn set 引用手册,了解任何似乎没有按照您期望的方式工作的 insn。参见 https://stackoverflow.com/tags/x86/info

关于c++ - 设置进位标志的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35298875/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com