为什么 clang++ 比 adc 更喜欢 adcx
Why does clang++ prefer adcx over adc
我使用以下代码实现了多精度加法:
bool carry{};
std::array<uint64_t, N> r{};
for (auto i = 0; i < N; ++i) {
uint64_t aa = a[i];
__uint128_t res = static_cast<__uint128_t>(aa) + b[i] + carry;
carry = res >> 64;
r[i] = res;
}
并且 clang++6.0 生成了以下程序集:
400a49: 4c 01 c1 add %r8,%rcx
400a4c: 66 49 0f 38 f6 c1 adcx %r9,%rax
400a52: 66 49 0f 38 f6 f2 adcx %r10,%rsi
400a58: 66 48 0f 38 f6 d7 adcx %rdi,%rdx
谁能解释一下为什么 clang 选择使用 adcx 而不是 adc?
据我所知,boto 具有相同的执行时间,但 adc 的编码为 3 个字节,而 adcx 为 6 个字节。
更新:我对它进行了更多的尝试,它的行为似乎很随机。
如果 args 作为 const 引用传递,我得到 adcx
https://godbolt.org/g/noFZNS
如果我按值传递,我会得到 adc:
如果代码不在函数内部,只是内联在 main 中,那就一团糟。
在我看来,这似乎是一个优化失误。我认为 adc
是更好的选择。在 Skylake 上,根据一些快速吞吐量测试(循环中有 xor eax,eax
/ times 4 adcx eax,edx
),它们具有相同的性能特征。奇怪的是,Agner Fog 没有在他的指令表 (http://agner.org/optimize/) 中列出 adox/adcx,在 SKL ADC/ADCX/ADOX 上,p0/p6 都是 1 uop,延迟为 1c。
如果有的话,写入所有标志而不是仅写入 CF 不太可能导致性能问题。
您应该在 https://bugs.llvm.org/buglist.cgi 上报告此事。
直到 clang 知道当有两个并行 dep 链时如何实际交错 ADOX,在 ADCX 上花费额外的代码大小是没有意义的。
我可以想象一种罕见的情况,在这种情况下保留其他标志很有用,而且最近的 Intel CPU 似乎在部分标志方面非常有效,甚至不需要合并 uop。但这非常小众,而不是这里发生的事情(add
破坏所有标志)。
我使用以下代码实现了多精度加法:
bool carry{};
std::array<uint64_t, N> r{};
for (auto i = 0; i < N; ++i) {
uint64_t aa = a[i];
__uint128_t res = static_cast<__uint128_t>(aa) + b[i] + carry;
carry = res >> 64;
r[i] = res;
}
并且 clang++6.0 生成了以下程序集:
400a49: 4c 01 c1 add %r8,%rcx
400a4c: 66 49 0f 38 f6 c1 adcx %r9,%rax
400a52: 66 49 0f 38 f6 f2 adcx %r10,%rsi
400a58: 66 48 0f 38 f6 d7 adcx %rdi,%rdx
谁能解释一下为什么 clang 选择使用 adcx 而不是 adc? 据我所知,boto 具有相同的执行时间,但 adc 的编码为 3 个字节,而 adcx 为 6 个字节。
更新:我对它进行了更多的尝试,它的行为似乎很随机。 如果 args 作为 const 引用传递,我得到 adcx https://godbolt.org/g/noFZNS 如果我按值传递,我会得到 adc:
如果代码不在函数内部,只是内联在 main 中,那就一团糟。
在我看来,这似乎是一个优化失误。我认为 adc
是更好的选择。在 Skylake 上,根据一些快速吞吐量测试(循环中有 xor eax,eax
/ times 4 adcx eax,edx
),它们具有相同的性能特征。奇怪的是,Agner Fog 没有在他的指令表 (http://agner.org/optimize/) 中列出 adox/adcx,在 SKL ADC/ADCX/ADOX 上,p0/p6 都是 1 uop,延迟为 1c。
如果有的话,写入所有标志而不是仅写入 CF 不太可能导致性能问题。
您应该在 https://bugs.llvm.org/buglist.cgi 上报告此事。
直到 clang 知道当有两个并行 dep 链时如何实际交错 ADOX,在 ADCX 上花费额外的代码大小是没有意义的。
我可以想象一种罕见的情况,在这种情况下保留其他标志很有用,而且最近的 Intel CPU 似乎在部分标志方面非常有效,甚至不需要合并 uop。但这非常小众,而不是这里发生的事情(add
破坏所有标志)。