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

Is batching same functions with SIMD instruction possible?

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

原因?因为每个thread/process/container/VM都占用资源,而AVX只需要一条指令。所以我可以用相同的硬件容纳更多的用户。

我在网上找到的大部分文章都着重于在函数内部使用AVX指令,例如加速流数据处理,或者处理一些大型计算。 None 其中提到批处理相同功能的不同实例。

我知道有一些挑战,比如输入不同导致执行路径不同,普通函数自动变成批处理版本也不容易,但我觉得技术上确实是可以的。

这是我的问题

  1. 是否很难(或可能)将普通功能自动更改为批处理版本?
  2. 如果 1 为否,我应该对函数施加什么限制才能使其成为可能?比如函数不管数据只有一条路径?
  3. 有没有其他技术可以更好的解决问题?我认为 GPU 对我来说不是一个好的选择,因为 GPU 不能支持 IO 或分支指令,尽管它的 SIMT 完全符合我的目标。

谢谢!

SSE/AVX 基本上是一个向量单元,它允许一次对多个元素的数组进行简单的操作(如 +-*/ and,or,XOR 等)。 AVX1 和 2 有 256 字节寄存器,所以你可以做,例如一次 8 个 32 位单打,或 4 个双打。 AVX-512 即将推出,但很少见。

所以如果你的函数都是对基本类型数组的操作,那是天作之合。如果操作非常简单,使用 AVX 内在函数重写函数是可行的。复杂的事情(比如不匹配矢量宽度)或者甚至在汇编器中完成它都是一个挑战。

如果你的函数不是在向量上运行,那么它就变得困难了,而且可能性大多是理论上的。 Autovectorizing 编译器有时可以做到这一点,但它很少见且有限,而且极其复杂。

有两种方法可以解决此问题:矢量化 (SIMD) 和并行化(线程)。

GCC 已经可以做你想要的 SIMD 向量化,只要函数是内联的,并且类型和操作是兼容的(并且它会自动内联小函数而不需要你要求)。

例如

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

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

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

如果函数太复杂,and/orGCC还没有向量化,那你可以用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 版本可用(官方支持即将推出)。