编辑:
ICC(在添加-qopt-report = 5 -qopt-report-phase:vec之后):
LOOP BEGIN at 4.c(107,2)
remark #15344: loop was not vectorized: vector dependence prevents vectorization
remark #15346: vector dependence: assumed OUTPUT dependence between c[i][j] (110:5) and c[i][j] (110:5)
remark #15346: vector dependence: assumed OUTPUT dependence between c[i][j] (110:5) and c[i][j] (110:5)
LOOP BEGIN at 4.c(108,3)
remark #15344: loop was not vectorized: vector dependence prevents vectorization
remark #15346: vector dependence: assumed OUTPUT dependence between c[i][j] (110:5) and c[i][j] (110:5)
remark #15346: vector dependence: assumed OUTPUT dependence between c[i][j] (110:5) and c[i][j] (110:5)
LOOP BEGIN at 4.c(109,4)
remark #15344: loop was not vectorized: vector dependence prevents vectorization
remark #15346: vector dependence: assumed FLOW dependence between c[i][j] (110:5) and c[i][j] (110:5)
remark #15346: vector dependence: assumed ANTI dependence between c[i][j] (110:5) and c[i][j] (110:5)
LOOP END
LOOP BEGIN at 4.c(109,4)
<Remainder>
LOOP END
LOOP END
LOOP END
似乎C [i] [j]在写入之前被读取,如果是矢量化的(因为我正在进行缩减)。问题是为什么允许减少是引入局部变量(temp)?
原始问题:
我下面有一个C片段,用于矩阵乘法。 a,b - 操作数,c - a * b结果。 n - 行和列长度。
double ** c = create_matrix(...) // initialize n*n matrix with zeroes
double ** a = fill_matrix(...) // fills n*n matrix with random doubles
double ** b = fill_matrix(...) // fills n*n matrix with random doubles
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (k = 0; k < n; k++) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
ICC(版本18.0.0.1)无法对内循环进行向量化(提供-O3标志)。
ICC输出:
LOOP BEGIN at 4.c(107,2)
remark #25460: No loop optimizations reported
LOOP BEGIN at 4.c(108,3)
remark #25460: No loop optimizations reported
LOOP BEGIN at 4.c(109,4)
remark #25460: No loop optimizations reported
LOOP END
LOOP BEGIN at 4.c(109,4)
<Remainder>
LOOP END
LOOP END
LOOP END
尽管如此,通过以下更改,编译器会对内部循环进行矢量化。
// OLD
for (k = 0; k < n; k++) {
c[i][j] += a[i][k] * b[k][j];
}
// TO (NEW)
double tmp = 0;
for (k = 0; k < n; k++) {
tmp += a[i][k] * b[k][j];
}
c[i][j] = tmp;
ICC矢量化输出:
LOOP BEGIN at 4.c(119,2)
remark #25460: No loop optimizations reported
LOOP BEGIN at 4.c(120,3)
remark #25460: No loop optimizations reported
LOOP BEGIN at 4.c(134,4)
<Peeled loop for vectorization>
LOOP END
LOOP BEGIN at 4.c(134,4)
remark #15300: LOOP WAS VECTORIZED
LOOP END
LOOP BEGIN at 4.c(134,4)
<Alternate Alignment Vectorized Loop>
LOOP END
LOOP BEGIN at 4.c(134,4)
<Remainder loop for vectorization>
LOOP END
LOOP END
LOOP END
不是在矩阵C单元格中累加向量乘法结果,而是将结果累积在一个单独的变量中并稍后分配。
为什么编译器不优化第一个版本?可能是由于a或/和b到c元素的潜在混叠(写入后读取问题)?
答案 0 :(得分:1)
利用您的编译器
您不会显示您用于获取矢量化报告的标记。我建议:
-qopt-report=5 -qopt-report-phase:vec
文档说:
对于n = 1到n = 5的级别,每个级别包括前一级别的所有信息,以及可能的一些附加信息。 5级产生最大程度的细节。如果未指定n,则默认值为2级,这将产生中等级别的详细信息。
随着更高级别的细节,编译器可能告诉你(用神秘的术语)为什么它没有矢量化。
我怀疑编译器担心内存是别名。您找到的解决方案允许编译器证明它不是,因此它执行矢量化。
便携式解决方案
如果您使用的是OpenMP,则可以使用:
#pragma omp simd
for (k = 0; k < n; k++)
c[i][j] += a[i][k] * b[k][j];
完成同样的事情。我认为英特尔还有一组特定于编译器的指令,这些指令将以非可移植的方式执行此操作。
其他注释
这一行:
double ** c = create_matrix(...)
让我紧张它表明在某个地方你有这样的东西:
for(int i=0;i<10;i++)
c[i] = new double[20];
也就是说,你有一个数组数组。
问题是,这并不保证您的子数组在内存中是连续的。结果是次优缓存利用率。您想要一个像2D数组一样寻址的一维数组。制作一个2D数组类来执行此操作或使用函数/宏来访问元素将允许您保留大致相同的语法,同时从更好的缓存性能中受益。
您可能还会考虑使用-align
标记进行编译并适当地修改代码。通过允许对内存进行对齐访问,这将提供更好的SIMD性能。