如何对循环进行矢量化?循环可以实现哪些向量运算

时间:2014-09-28 17:46:23

标签: loops vectorization

你能举个例子说明如何对一个循环进行矢量化吗?例如,我有以下循环:

for (i=1; i < N; i++) {
     a[i] = b[i]*c[i];
     d[i] = a[i-1] + 7;
}

我知道在向量化循环之前不应该存在任何依赖关系,但在显示没有依赖关系之后会有什么依赖关系。它究竟是如何被矢量化的,步骤是什么?

1 个答案:

答案 0 :(得分:2)

如果你问的是如何手动对其进行矢量化,或者如果你在询问矢量化编译器是如何做的那样,那么我对你的问题并不十分清楚。因此,我将解释编译器如何执行 1 ,如果需要,您可以随时手动重复相同的步骤。让我们使用矢量化宽度为4的示例。

原始代码:

for (i=1; i < N; i++) {
  a[i] = b[i]*c[i];
  d[i] = a[i-1] + 7;
}

首先,编译器需要确定迭代n依赖于迭代n-1,这是一个错误依赖,因为没有数据流依赖性 - a[n]不依赖于a[n-1]。一旦识别出来,编译器就可以执行loop fission 2

for (i=1; i < N; i++) {
  a[i] = b[i]*c[i];
}
for (i=1; i < N; i++) {
  d[i] = a[i-1] + 7;
}

这两个现在都可以进行矢量化;让我们专注于第一个,但对另一个则是一样的。所以我们的代码是:

for (i=1; i < N; i++) {
  a[i] = b[i]*c[i];
}

对于我们的例子,我们假设矢量化宽度为4.矢量化的关键是loop unrolling。所以编译器展开大小为4:

int i = 1;
// Unrolled loop:
for (; i < N-3; i+=4) {
  a[i]   = b[i]  *c[i];
  a[i+1] = b[i+1]*c[i+1];
  a[i+2] = b[i+2]*c[i+2];
  a[i+3] = b[i+3]*c[i+3];
}
// Remainder loop:
for (; i < N; i++) {
  a[i] = b[i]*c[i];
}

现在很明显如何进行向量化 - 编译器将展开的循环中相同的指令序列更改为单个向量指令:

int i = 1;
// Unrolled loop:
for (; i < N-3; i+=4) {
  a[i:i+3] = b[i:i+3]*c[i:i+3];
}
// Remainder loop:
for (; i < N; i++) {
  a[i] = b[i]*c[i];
}

之后,在编译器的后期“降低”阶段,它将为该操作分配实际指令 - 例如,如果这些是启用SSE的体系结构上的单精度浮点数组,则可能使用mulps

<子> 1当然,以简化的方式 2请记住,在这种情况下,循环裂变实际上会伤害数据局部性 3编译器实际上不必展开,然后只查找重复项 - 这些步骤通常一起使用。