当我编译此代码段(使用 -Ofast -floop-nest-optimize
)时,gcc 生成按源顺序遍历数组的程序集。
但是,如果我取消注释行 //n = 32767
并将 any 数字分配给 n
,它将索引顺序交换为 x[i * n + j]
。以连续的行优先顺序遍历内存比向下跨列对缓存更友好。
float matrix_sum_column_major(float* x, int n) {
// n = 32767;
float sum = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
sum += x[j * n + i];
return sum;
}
为什么 GCC 或 clang 不能做 loop interchange使用运行时变量 int
大小? 实际代码通常不会显式声明大小。
PD:我已经用不同版本的 gcc 和 clang-9 尝试过这个,它似乎在两个版本中都发生了。
PD2:即使我将 x
设为函数内部的局部变量 malloc
,它仍然会发生。
最佳答案
编译器通常将他们的努力(并且应该集中他们的努力)放在那些结构可能被对效率感兴趣的程序员使用可以用其他结构替换的地方,这些结构很容易被证明在所有预期重要的情况下都是等效的。如果 n
是常量,编译器可以确定将在循环中使用的确切数组索引集,然后找出如何处理所有这些索引。如果 n
不是常数,编译器可能能够确定当 n
为正时,代码将使用从 0
到 n*n-1
,但这可能需要更多的努力。如果 clang 和 的作者足够努力的话,他们可能能够在这种情况下做出这样的决定,但他们可能认为这种努力是不值得的。
请注意,如果代码使用的 n
的一些特定值比其他任何值都多,让代码明确检查这些值并使用为它们量身定制的循环,编译器可能能够生成远与使用任意 n
的循环相比,这些循环的代码效率更高。因为许多现实世界的问题可能有一些 n
的值比其他值使用得更多,编译器作者假设对性能感兴趣的程序员可能会使用这样的值并不是不合理的特殊用途的循环,并且花费一定的努力改进任意-n
循环可能比在其他地方花费相同的努力提供更少的好处。
关于c - 为什么 GCC 只能在 int 大小是编译时常量时进行循环交换优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61016358/