【发布时间】:2021-07-11 09:25:07
【问题描述】:
这让我感到困惑/好奇,为什么是这个代码
void maxArray(double* x, double* y) {
for (int i = 0; i < 65536; i++) {
x[i] = ((y[i] > x[i]) ? y[i] : x[i]);
}
}
...比这段代码更快?
void maxArray(double* x, double* y) {
for (int i = 0; i < 65536; i++) {
if (y[i] > x[i]) x[i] = y[i];
}
}
并且记录在案,第一个生成的程序集与扩展版本相同:
inline double fn(double a, double b) {
if (a > b) {
return a;
} else {
return b;
}
}
void maxArray(double* x, double* y) {
for (int i = 0; i < 65536; i++) {
x[i] = fn(y[i], x[i]);
}
}
我明白了。第一个是将x[i]设置为条件,中间是有条件地设置x[i]。两者都有条件,所以都有分支?是不是因为后者的扩展 if 语句被优化为向量程序集max 命令,而前者由于某种原因未被识别为 max 函数?
gcc 10.3 x86_64 -Ofast -march=native
【问题讨论】:
-
如果您点击任何链接,您可以看到确切的编译器和标志。
-
看起来这严重依赖于目标 CPU。您使用了
-march=native,这是用于托管 godbolt.org 的随机 CPU -
在第二个变体中,编译器要么没有将源代码识别为最大计算的实现,要么它试图更接近源代码。在第一个和第三个变体中,您在两个分支中都有分配,在第二个变体中,您仅在一个分支中进行分配。
-
我怀疑编译器不愿意在您不要求的情况下生成存储。例如,假设
x数组在只读内存中,但您知道它的条目都大于y中的条目。严格阅读,第二个代码应该可以工作(并且什么也不做),因为测试永远不会为真,并且永远不会分配x[i]。但是第一个或第三个版本会崩溃。因此,编译器不应将 #2 优化为与 #1 等效的内容,即使它知道 #1 会更快。 -
@NateEldredge:是的,确切地说,C 编译器不得发明写入,如果没有别的,为了线程安全。 (因此,即使在对齐的向量中检查至少一个要更新的元素也无法将它们全部存储起来)。 Is it possible to use SIMD instruction for replace?,另请参阅此 ICC 错误:Crash with icc: can the compiler invent writes where none existed in the abstract machine?
标签: c gcc x86 x86-64 compiler-optimization