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

Posted

技术标签:

【中文标题】是否可以使用 SIMD 指令批量处理相同的功能?【英文标题】:Is batching same functions with SIMD instruction possible? 【发布时间】:2018-09-21 10:51:38 【问题描述】:

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

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

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

我知道有一些挑战,比如不同的输入会导致不同的执行路径,将一个正常的函数自动变成批处理版本并不容易,但我认为在技术上确实是可能的。

这是我的问题

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

谢谢!

【问题讨论】:

【参考方案1】:

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

因此,如果您的函数都是对基本类型数组的操作,那么它很自然。如果操作非常简单,使用 AVX 内部函数重写函数是可行的。复杂的事情(比如不匹配向量宽度),甚至在汇编程序中完成都是一个挑战。

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

【讨论】:

谢谢,我想这也很难。我只是想知道有没有人试图探索这个方向?这似乎很直观,虽然很难,也许有人对此进行了一些调查?但是我在网上没有找到。 最常见的错误是考虑 AVX(处理器本身。它们是更多的扩展,并且用于特殊目的。您无法优化 AVX 的任何问题 感谢和抱歉耽搁了这么久【参考方案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 版本可用(官方支持即将推出)。

【讨论】:

感谢 SIMD 和 omp 示例,但我认为这与我需要的有点不同。让我把我的问题说得更清楚。假设有一个master-worker程序,每次有请求来,master就启动一个worker来处理。这些工作人员具有完全相同的代码指令,但输入数据不同。我想知道是否有一种方法可以自动“重写”工作代码,以便我可以在一个线程或 VM 中处理 8 个请求,而不是启动 8 个线程。 Worker 是否在其代码中使用 SIMD 并不重要。 我不知道有什么神奇的解决方案。我已经使用状态机手动编写了您描述的内容。更现代的方法是使用协程。他们计划用于 C++,但仅此而已。我不知道Python。老实说,这就是 Go 为之而生的工作。 感谢和抱歉耽搁了

以上是关于是否可以使用 SIMD 指令批量处理相同的功能?的主要内容,如果未能解决你的问题,请参考以下文章

是否有 SIMD 指令来实现批量数组内存索引映射?

使用 simd 指令时,32 位图像处理是不是比 24 位图像处理快?

是否可以使用 SIMD 指令进行 3x3 矩阵求逆?

使用 ARM SIMD 指令优化掩码功能

SSE3指令有啥功能?

指令级并行与 SIMD