A[i]+=A[i+1] 是循环中的一种数据依赖性吗?可以矢量化吗?

Posted

技术标签:

【中文标题】A[i]+=A[i+1] 是循环中的一种数据依赖性吗?可以矢量化吗?【英文标题】:Is A[i]+=A[i+1] kind of data dependency in a loop ? Can it be vectorized? 【发布时间】:2019-08-08 11:07:41 【问题描述】:

据我所知,无法向量化具有串行数据依赖关系的循环,例如 A[i] += A[i-1]

但我不确定A[i] += A[i+1] 是否是原始数据依赖关系,是否可以对这个循环进行矢量化?

for(i = 0; i < n - 1; i++) 
    A[i] += A[i + 1];

【问题讨论】:

可以根据***进行矢量化:en.wikipedia.org/wiki/Automatic_vectorization 【参考方案1】:

您的循环计数器正在增加 (i++),因此您正在向前查看 A[i+1],而不是后面。 这意味着您只是读取相同的原始输入元素两次,而不是重新读取任何最近的输出。因此没有串行依赖。

只需使用编译器尝试它并查看向量加载/存储而不是标量。 (在为 x86 编译时,很容易用整数而不是 FP 来区分)。例如在 https://godbolt.org/ 上使用 gcc 或 clang -O3

在具有高效未对齐负载的机器(如现代 x86)上,您的编译器可能只会加载 A[ i +0..3]A[ i+1 + 0..3 ],但另一个选项是改组以创建偏移向量,例如使用专门为此设计的 x86 SSSE3 palignr,对 Core 2 非常有用(没有具有高效的 SIMD 未对齐负载)。

GCC 和 clang 都使用 SSE2 对 x86-64 进行矢量化(这是 x86-64 的基线) https://godbolt.org/z/HdNsvC - x86-64 的 GCC9.1(默认 -mtune=generic 并且只有 SSE2 可用)选择执行 2x 加载 + 添加 + 存储。 clang8.0 选择展开(像往常一样)并从A[i+1 +4*unroll + 0..3] 加载并使用shufps 创建A[i + 0..3] 向量。中端优化器可能使用了一个对palignr 很好的配方,但是一旦它进入代码生成,就必须模拟它,并且只有 SSE2,而不是 SSSE3。此外,输入指针很可能是 16 字节对齐的,因此从 16*n + 4 字节加载向量是不幸的。除非它无论如何都会在最近的 Intel CPU 上成为 shuffle 吞吐量的瓶颈。

带有 AVX1 但不带有 AVX2(例如 -march=sandybridge)的 Clang 会造成可笑的混乱:在多个步骤中使用 256 位 FP shuffle 来模拟 256 位 palignr,然后解包为整数 SIMD @ 的 128 位向量987654342@(压缩 32 位添加),然后 vinsertf128 回到 256 位存储 256 位。 SnB 甚至没有 256 位加载/存储单元,因此这些微指令需要 2 个周期才能运行,并且对未对齐数据的惩罚比通常要大得多。


A[i] += A[i-1] 更难矢量化,但通过有效的洗牌可以提高速度,尤其是在浮点数下,串行依赖的延迟会受到更大的伤害。

SIMD prefix sum on Intel cpu

parallel prefix (cumulative) sum with SSE

或者一般来说,如果有一个封闭式公式可以让 n 向前迈出一步,您可以在 SIMD 向量的元素中并行运行它,例如 Is it possible to use SIMD on a serial dependency in a calculation, like an exponential moving average filter?

【讨论】:

附带问题。您似乎对优化有点了解,有没有一两本书可以推荐? @tay10r:Agner Fog 的优化指南非常棒。 (agner.org/optimize)。一旦您知道机器可以高效地做什么,编写高效编译的 C 或 C++ 就是您的目标,并通过查看编译器的 asm 输出来检查。另请参阅***.com/tags/x86/info中的其他链接 您的回答对我很有帮助。非常感谢您,先生。【参考方案2】:

下面的这个循环可以向量化。

for(i = 0; i < n - 1; i++) 
    A[i] += A[i + 1];

我们可以用另一种方式编写操作:

Vector A = ...;           // 1, 2, 3, 4, 5
Vector B = ShiftLeft(A);  // 2, 3, 4, 5, 0 you dont create new array, no performance loss
Vector C = A + B;         // 3, 5, 7, 9, 5

矢量化并不难,所以.. 作为彼得·科德斯,什么?嗨彼得^^。正如彼得所说,A[i] += A[i - 1]; 更难,而且会是不同的场景。

【讨论】:

以上是关于A[i]+=A[i+1] 是循环中的一种数据依赖性吗?可以矢量化吗?的主要内容,如果未能解决你的问题,请参考以下文章

迭代器VS生成器

js中for循环使用方法详解

for循环 与 for in 循环

python中for in的用法

顺序表查找的优化

排序算法专题之冒泡排序