解释如何对循环进行矢量化

时间:2017-10-08 21:02:38

标签: loops optimization dependencies vectorization

我有一个作业,给出了以下循环:

for (i = 0; i < 7030; i++) {
    a[7031 * i + 703] = b[i] * c[i];       // S1
    d[i] = a[7031 * i + 703 * 7030] + e;   // S2
}

首先,我被要求通过使用GCD测试和Banerjee的不完整和完整测试来确定数据依赖性。

  • 从GCD测试中我得出结论,这个循环中没有依赖关系。
  • 从Banerjee的不完整测试我确定存在依赖。
  • 从Banerjee的完整测试中,我确定循环中存在True和Anit-Dependency。

GCD测试与Banerjee测试之间的结果之间的差异是否更弱/更不准确?如果是这样,我是否应该始终接受Banerjee完整测试的结果?

其次,我被要求解释如何对循环进行矢量化并描述循环实现的向量运算。

我可以简单地说你可以将S1和S2分成两个独立的for循环,包含S1的循环在包含S2的循环之前完全执行吗?

for (i = 0; i < 7030; i++) {
    a[7031 * i + 703] = b[i] * c[i];
}

for (i = 0; i < 7030; i++) {
    d[i] = a[7031 * i + 703 * 7030] + e;
}

就“描述循环实现的向量操作”而言,我对这里写的内容感到很遗憾。

2 个答案:

答案 0 :(得分:1)

因为这是一个任务,你可能想了解矢量化过程,我不提供可以编译的源代码(你应该在我的答案后做一些编码)。希望你能够自己解决它。

//The loop counter should be suitable for Vectorization Factor (VF) 
//In this case VF=4 (assume your processor has 128-bit SIMD register and data are 32-bit. 
//1757×4 = 7028 --> you will have 2 values that can not be put in vectos or you must pad the array to fit the vector.

for (i = 0; i < 7028; i+=4) {
    a[7031 * i + 703] = b[i] * c[i];
    a[7031 * (i+1) + 703] = b[i+1] * c[i+1];
    a[7031 * (i+2) + 703] = b[i+2] * c[i+2];
    a[7031 * (i+3) + 703] = b[i+3] * c[i+3];
}
a[7031 * i + 703] = b[i] * c[i];
i++;
a[7031 * i + 703] = b[i] * c[i];

//vec_b = (b[i], b[i+1], b[i+2], b[i+3]); // are adjacent -> thus can be loaded
//vec_c = (c[i], c[i+1], c[i+2], c[i+3]); // are adjacent -> thus can be loaded
//index = 7031*i + 703
//vec_a = (a[index], a[index + 7031], a[index + 7031*2], a[index + 7031*3]; //not adjacent!

vec_b = __mm_loadu_ps(&b[i]);将相邻元素的向量加载到vec_c的向量中,您也可以使用从相邻元素intrinsic instruction加载的加载指令。但关键是你应该将数据存储到非继续地址。如果处理器支持AVX-512,您可以使用scatter指令将向量存储到非连续地址。 如果您没有scatter指令,则可能需要提取元素并将它们放在不同的目标地址中。 _mm_extract_epi32_mm_cvtss_f32并转移等等

for (i = 0; i < 7030; i++) {
     d[i] = a[7031 * i + 703 * 7030] + e;
}

再次需要进行矢量化,您需要了解数据位置:

Index = 7031 * i + 703 * 7030
for (i = 0; i < 7028; i+=4) {
     d[i] = a[Index] + e;
     d[i+1] = a[Index + 7031] + e;
     d[i+2] = a[Index + 7031*2] + e;
     d[i+3] = a[Index + 7031*3] + e;
}
//extra computations for i = 7028, 7029;
//vec_a = (a[Index], a[Index + 7031], a[Index + 7031*2], a[Index + 7031*3]) 
//vec_a can be loaded with _mm_set_ps (a3, a2, a1, a0), etc but `gather` instruction is also use full to load from different addresses.
//vec_e = (e, e, e, e) : you can use  _mm_set_ps1, _mm_set1... 

最后如何相乘或添加?轻松使用矢量运算

vec_a = _mm_mul_ps(vec_b, vec_c);
vec_d = _mm_add_ps(vec_a, vec_e);

如何将矢量存储到继续位置?

_mm_store_ps(d[i],vec_d); //i=i+4 for the next store I mean your loop counter must be appropriate. 

因此,对于矢量化循环,您可以使用内部函数作为显式矢量化,或者您可以依赖隐式矢量化,例如在-O3优化级别使用gcc / clang或启用适当的标记gcc -ftree-vectorize -ftree-slp-vectorize

答案 1 :(得分:0)

我认为您通过GCD测试和部分Baneergy测试获得的结果是错误的。我们执行依赖关系测试的顺序如下。

  1. GCD测试
  2. Banerjee测试
  3. 完成Banerjee测试

GCD是依赖性测试的基本健全性测试。如果GCD测试证明不存在依赖关系,那么您就别走了。您可以说语句之间不存在依赖关系,因此可以对循环进行矢量化处理。如果GCD测试失败(意味着您看到依赖项存在),则可以进行更详细的分析。那就是Banerjee测试。如果使用Banerjee测试无法确定S1和S2之间不存在依赖关系,那么您将进行完整的Banerjee测试,这是更详细和深入的分析。

我将GCD,Banerjee测试和完整的Banerjee测试应用于您的循环,并且在所有情况下都证明存在依赖关系,并且循环无法并行化/矢量化。

(正如您在问题中所述,完整的Banerjee检验证明S1和S2之间具有真实的依赖关系和反依赖关系。