【问题标题】:Explaining how a loop can be vectorized解释如何对循环进行矢量化
【发布时间】:2020-09-01 04:22:46
【问题描述】:

我有一个任务,并得到以下循环:

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 测试之间的结果差异是否会导致 GCD 测试较弱/不太准确?如果是这样,我是否应该始终接受 Banerjee 的完整测试的结果?

其次,我被要求解释如何将循环向量化并描述循环实现的向量操作。

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

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;
}

关于“描述循环实现了哪些向量操作”,我在这里写的东西迷失了。

【问题讨论】:

    标签: loops optimization dependencies vectorization


    【解决方案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(&amp;b[i]); 将向量从相邻元素加载到vec_c 的向量中,您也可以像这样使用从相邻元素intrinsic instruction 加载的加载指令。但关键是您应该将数据存储到非继续地址。如果处理器支持AVX-512,您可能可以使用scatter 指令将向量存储到非连续地址。 如果您没有scatter 指令,您可能需要提取元素并将它们放在不同的目标地址中。 _mm_extract_epi32_mm_cvtss_f32 和 shift 等。

    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

    【讨论】:

      【解决方案2】:

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

      1. GCD 测试
      2. 班纳吉测试
      3. 完成 Banerjee 测试

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

      我将 GCD、Banerjee 测试和完整的 Banerjee 测试应用到您的循环中,并且在所有情况下都证明依赖项退出并且循环无法并行化/矢量化。

      (正如您在问题中所说,完整的 Banerjee 测试证明 S1 和 S2 之间具有真正的依赖和反依赖。

      【讨论】:

        猜你喜欢
        • 2017-09-26
        • 1970-01-01
        • 1970-01-01
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-07
        • 2015-04-07
        相关资源
        最近更新 更多