OpenMP 4.0引入了一种称为“omp simd”的新结构。与旧的“parallel for”相比,使用此构造有什么好处?什么时候每个人都比另一个人更好?
编辑:这是一篇与 SIMD 指令相关的有趣论文。
OpenMP 4.0引入了一种称为“omp simd”的新结构。与旧的“parallel for”相比,使用此构造有什么好处?什么时候每个人都比另一个人更好?
编辑:这是一篇与 SIMD 指令相关的有趣论文。
一个简单的答案:
OpenMP 仅用于为多个内核利用多个线程。这个新simd
扩展允许您在现代 CPU 上显式使用SIMD 指令,例如 Intel 的 AVX/SSE 和 ARM 的 NEON。
(请注意,SIMD 指令在设计上是在单线程和单核中执行的。但是,对于 GPGPU,SIMD 的含义可以相当扩展。但是,但我认为您不需要考虑 OpenMP 4.0 的 GPGPU。 )
因此,一旦您了解 SIMD 指令,就可以使用这个新结构。
在现代 CPU 中,大致有三种类型的并行:(1) 指令级并行 (ILP),(2) 线程级并行 (TLP),以及 (3) SIMD 指令(我们可以说这是向量级或者)。
ILP 由您的乱序 CPU 或编译器自动完成。parallel for
您可以使用 OpenMP和其他线程库来利用 TLP 。那么,SIMD 呢?内在函数是使用它们的一种方式(以及编译器的自动矢量化)。OpenMPsimd
是一种使用 SIMD 的新方法。
举一个非常简单的例子:
for (int i = 0; i < N; ++i)
A[i] = B[i] + C[i];
上面的代码计算两个 N 维向量的和。如您所见,数组没有(循环携带的)数据依赖性A[]
。这个循环是令人尴尬的并行。
可以有多种方法来并行化这个循环。例如,在 OpenMP 4.0 之前,这可以仅使用parallel for
构造进行并行化。每个线程将N/#thread
在多个核心上执行迭代。
但是,您可能会认为使用多个线程来进行这种简单的加法将是一种矫枉过正的做法。这就是为什么会有向量化,这主要是通过 SIMD 指令实现的。
使用 SIMD 将是这样的:
for (int i = 0; i < N/8; ++i)
VECTOR_ADD(A + i, B + i, C + i);
此代码假定 (1) SIMD 指令 ( VECTOR_ADD
) 是 256 位或 8 路(8 * 32 位);(2)N
是 8 的倍数。
8 路 SIMD 指令意味着一个向量中的 8 个项目可以在一条机器指令中执行。请注意,英特尔最新的 AVX 提供了这样的 8 路(32 位 * 8 = 256 位)向量指令。
在 SIMD 中,您仍然使用单核(同样,这仅适用于传统 CPU,不适用于 GPU)。但是,您可以在硬件中使用隐藏的并行性。现代 CPU 将硬件资源专用于 SIMD 指令,其中每个 SIMD通道可以并行执行。
您可以同时使用线程级并行。上面的例子可以进一步并行化parallel for
。
(但是,我怀疑有多少循环可以真正转换为 SIMDized 循环。OpenMP 4.0 规范对此似乎有点不清楚。因此,实际性能和实际限制将取决于实际编译器的实现。)
总而言之,simd
construct 允许您使用 SIMD 指令,反过来,可以利用更多的并行性以及线程级并行性。但是,我认为实际的实现很重要。
链接到的标准相对清晰(第 13 页,第 19+20 行)
当任何线程遇到 simd 构造时,与该构造关联的循环的迭代可以由该线程可用的 SIMD 通道执行。
SIMD
是一个子线程的东西。更具体地说,在 CPU 上,您可以想象使用simd
指令专门请求对单独属于同一线程的循环迭代块进行矢量化。它以独立于平台的方式暴露了单个多核处理器中存在的多级并行性。例如,请参阅此英特尔博客文章中的讨论(以及加速器内容) 。
因此,基本上,您将希望使用omp parallel
将工作分配到不同的线程,然后可以迁移到多个内核;并且您将希望使用omp simd
每个内核中的矢量管道(例如)。通常omp parallel
会在“外部”处理更粗粒度的并行工作分配,并omp simd
会绕过内部的紧密循环以利用细粒度并行性。
编译器不需要以 simd 子句的存在为条件在并行区域中进行 simd 优化。我熟悉的编译器继续以与以前相同的方式支持嵌套循环、并行外部、向量内部。
过去,通常采用 OpenMP 指令来防止涉及外部并行化循环(带有折叠子句的多个循环)的循环切换优化。这似乎在一些编译器中发生了变化。当设置了 omp parallel do [for] simd 时,OpenMP 4 开辟了新的可能性,包括通过一种带状挖掘优化具有不可矢量化内循环的并行外循环。ifort 有时在没有 simd 子句的情况下将其报告为外循环矢量化。然后,它可能会针对比 omp parallel do simd 更少的线程进行优化,这似乎需要比 simd 矢量宽度更多的线程才能得到回报。可以推断出这样的区别,因为没有 simd 子句,编译器被隐式要求优化循环计数,例如 100 或 300,而 simd 子句请求无条件的 simd 优化。当我有一个 24 核平台时,用于 simd 的 gcc 4.9 omp 并行看起来非常有效。