使用SIMD指令可以批处理相同的功能吗?

时间:2018-09-21 10:51:39

标签: gcc x86 compiler-construction llvm simd

我有一个场景,许多完全相同的功能(为简单起见,我们这里只考虑C / C ++和python)将在我的机器上同时执行。直观地讲,我只是使用多线程将函数的每个实例视为利用并行性的线程,它们并不争用相同的资源,但会执行许多分支操作(例如for循环)。但是,由于它们实际上是相同的功能,因此我正在考虑使用一些SIMD指令(例如AVX-512)对它们进行批处理。当然,它应该是自动的,以便用户不必修改其代码。

原因?因为每个线程/进程/容器/ VM都占用资源,但是AVX只需要一条指令。因此,我可以使用相同的硬件容纳更多的用户。

我在网上找到的大多数文章都集中在使用该函数内部的AVX指令,例如,以加速流数据处理或处理一些大型计算。他们都没有提到批处理同一功能的不同实例。

我知道存在一些挑战,例如由不同的输入引起的不同的执行路径,并且将正常功能自动转换为批处理版本并不容易,但是我认为从技术上来说确实可行。

这是我的问题

  1. 是否很难(或不可能)将普通功能自动更改为批处理版本?
  2. 如果1为否,我应该对函数进行哪些限制以使其成为可能?例如,如果函数仅具有一条路径而不管数据如何?
  3. 是否还有其他技术可以更好地解决问题?我认为GPU不是我的好选择,因为尽管SIMT非常适合我的目标,但GPU无法支持IO或分支指令。

谢谢!

2 个答案:

答案 0 :(得分:2)

SSE / AVX本质上是向量单位,它允许一次对多个元素的数组进行简单的操作(如+-* /和,或XOR等)。 AVX1和2具有256字节的寄存器,因此您可以执行例如一次8个32位单打,或4个双打。 AVX-512即将到来,但非常罕见。atm。

因此,如果您的函数都是对基本类型数组的所有操作,那自然是合适的。如果操作非常简单,则可以使用AVX内在函数重写函数。复杂的事情(例如不匹配矢量宽度)甚至在汇编器中进行都是一个挑战。

如果您的函数未在向量上运行,则将变得很困难,并且可能性大都是理论上的。自动矢量化的编译器有时可以做到这一点,但这是罕见的,有限的,而且极其复杂。

答案 1 :(得分:2)

有两种解决方法:向量化(SIMD)和并行化(线程)。

只要功能是内联的,并且类型和操作兼容(只要您不要求,它将自动内嵌较小的功能),GCC就可以执行所需的SIMD向量化。

例如

inline void func (int i) {
   somearray[i] = someotherarray[i] * athirdarray[i];
}

for (int i = 0; i < ABIGNUMBER; i++)
   func (i);

-O3启用了矢量化和内联。

如果功能太复杂,并且/或者GCC尚未将其向量化,那么您可以使用OpenMP或OpenACC对其进行并行化。

OpenMP使用特殊的标记来告诉编译器在何处产生线程。

例如

#pragma omp parallel
#pragma omp for
for (int i = 0; i < ABIGNUMBER; i++)
    ....

是的,您也可以在GPU上做到这一点!您必须多做一些输入才能正确地复制和复制数据。仅标记区域在GPU上运行。其他所有内容都在CPU上运行,因此I / O等不是问题。

#pragma omp target map(somearray,someotherarray,athirdarray)
#pragma omp parallel
#pragma omp for
for (int i = 0; i < ABIGNUMBER; i++)
    ....

OpenACC是一个类似的想法,但更专注于GPU。

您可以在许多地方找到OpenMP和OpenACC编译器。 GCC和LLVM均支持NVidia GPU。 LLVM对AMD GPU有所支持,并且也有非官方的GCC构建(即将提供官方支持)。