[当我用-Ofast -fnest-loop-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和clang的作者可能会在这种情况下做出这样的决定,如果他们付出了足够的努力,但是他们可能认为这样做是不值得的。
[请注意,如果代码使用n
的几个特定值远多于其他任何值,让代码显式检查这些值并使用针对它们定制的循环,则编译器可能能够为这些循环生成效率更高的代码比可以使用任意n
的循环所能达到的要好。由于许多现实世界中的问题n
的使用价值可能会比其他问题高得多,因此对于编译器作者来说,假设对性能感兴趣的程序员可能会使用此类特殊用途的循环,这并非没有道理,并且花费一定的精力来改善任意n
循环所带来的收益可能比在其他地方花费相同的精力要少。