什么是"矢量化"?

Tho*_*zma 154 vectorization

好几次,我在matlab,fortran ......中遇到过这个术语......但是我从来没有找到解释是什么意思,它有什么作用?所以我在这里问,什么是矢量化,例如,"循环是矢量化的"是什么意思?

Ste*_*non 182

许多CPU具有"向量"或"SIMD"指令集,它们同时对两个,四个或更多个数据应用相同的操作.现代x86芯片具有SSE指令,许多PPC芯片都有"Altivec"指令,甚至一些ARM芯片都有一个矢量指令集,称为NEON.

"矢量化"(简化)是重写循环的过程,因此它不是处理数组的单个元素N次,而是同时处理(比如说)阵列的4个元素N/4次.

(我之所以选择4是因为它是现代硬件最有可能直接支持的;术语"矢量化"也用于描述更高级别的软件转换,您可能只是完全抽象出循环而只是描述在数组上操作而不是元素构成他们)


矢量化和循环展开之间的区别: 考虑以下非常简单的循环,它添加两个数组的元素并将结果存储到第三个数组.

for (int i=0; i<16; ++i)
    C[i] = A[i] + B[i];
Run Code Online (Sandbox Code Playgroud)

展开此循环会将其转换为如下所示:

for (int i=0; i<16; i+=4) {
    C[i]   = A[i]   + B[i];
    C[i+1] = A[i+1] + B[i+1];
    C[i+2] = A[i+2] + B[i+2];
    C[i+3] = A[i+3] + B[i+3];
}
Run Code Online (Sandbox Code Playgroud)

另一方面,它的矢量化产生了这样的东西:

for (int i=0; i<16; i+=4)
    addFourThingsAtOnceAndStoreResult(&C[i], &A[i], &B[i]);
Run Code Online (Sandbox Code Playgroud)

其中"addFourThingsAtOnceAndStoreResult"是编译器用于指定向量指令的内在函数的占位符.请注意,有些编译器能够像这样自动向量化非常简单的循环,这通常可以通过编译选项启用.更复杂的算法仍然需要程序员的帮助才能生成良好的矢量代码.

  • 这个和循环展开/展开有什么区别? (8认同)
  • 编译器自动矢量化展开的循环不是更容易吗? (3认同)
  • @Shuklaswag:矢量化是编译器可以为你做的事情,但它也是程序员明确自己做的事情.操作系统不参与. (3认同)
  • @StephenCanon 如何检查某些行是否已矢量化?如果使用 objdump,您会在 objdump 的输出中寻找什么? (2认同)

And*_*ers 30

矢量化是将标量程序转换为矢量程序的术语.向量化程序可以从单个指令运行多个操作,而标量只能同时对一对操作数进行操作.

来自维基百科:

标量方法:

for (i = 0; i < 1024; i++)
{
   C[i] = A[i]*B[i];
}
Run Code Online (Sandbox Code Playgroud)

矢量化方法:

for (i = 0; i < 1024; i+=4)
{
   C[i:i+3] = A[i:i+3]*B[i:i+3];
}
Run Code Online (Sandbox Code Playgroud)

  • 这本质上不是与标量方法相同吗?你的语法和循环推进是不同的,但在下面你仍然将它乘以 4 倍。但不知怎的,它会更快,可能 CPU 有指令执行一些称为矢量化的技巧。 (4认同)
  • 看来我会在这里回答我自己的问题。当编译器看到向量化方法中的语法时,它会将其转换为乘以向量的优化 CPU 指令。就像SIMD一样。 (3认同)

Mar*_*eue 12

简单来说,矢量化意味着优化算法,以便它可以在处理器中使用 SIMD 指令。

AVX、AVX2 和 AVX512 是在一条指令中对多个数据执行相同操作的指令集(intel)。例如。AVX512 意味着您可以一次操作 16 个整数值(4 个字节)。这意味着,如果您有 16 个整数的向量,并且您想在每个整数中将该值加倍,然后将其加 10。您可以将值加载到通用寄存器 [a,b,c] 16 次并执行相同的操作,也可以通过将所有 16 个值加载到 SIMD 寄存器 [xmm,ymm] 并执行一次操作来执行相同的操作。这可以加快矢量数据的计算。

在矢量化中,我们利用这一优势,通过重构我们的数据,以便我们可以对其执行 SIMD 操作并加速程序。

矢量化的唯一问题是处理条件。因为条件分支了执行流程。这可以通过屏蔽来处理。通过将条件建模为算术运算。例如。如果我们想将 10 添加到值,如果它大于 100。我们也可以。

if(x[i] > 100) x[i] += 10; // this will branch execution flow.
Run Code Online (Sandbox Code Playgroud)

或者我们可以将条件建模为算术运算,创建条件向量 c,

c[i] = x[i] > 100; // storing the condition on masking vector
x[i] = x[i] + (c[i] & 10) // using mask
Run Code Online (Sandbox Code Playgroud)

这是一个非常简单的例子……因此,c 是我们的掩码向量,我们用它来根据它的值执行二元运算。这避免了执行流程的分支并启用矢量化。

矢量化与并行化一样重要。因此,我们应该尽可能地利用它。所有现代处理器都有用于繁重计算工作负载的 SIMD 指令。我们可以通过向量化优化我们的代码以使用这些 SIMD 指令,这类似于并行化我们的代码以在现代处理器上可用的多个内核上运行。

我想提一下 OpenMP,它可以让您使用 pragma 对代码进行矢量化。我认为这是一个很好的起点。OpenACC 也是如此。


War*_*ung 10

它指的是在单个步骤中对数字列表(或"向量")进行单个数学运算的能力.你经常在Fortran中看到它,因为它与科学计算有关,科学计算与超级计算相关,其中矢量化算法首次出现.如今,几乎所有桌面CPU都通过英特尔的SSE等技术提供某种形式的矢量化算法.GPU还提供了一种矢量化算法.


chi*_*tiz 7

英特尔的人我觉得很容易掌握。

矢量化是将算法从一次操作单个值转换为一次操作一组值的过程。现代 CPU 为向量操作提供直接支持,其中将单个指令应用于多个数据 (SIMD)。

例如,具有 512 位寄存器的 CPU 可以保存 16 个 32 位单精度双精度数并进行单次计算。

比一次执行一条指令快 16 倍。将此与线程和多核 CPU 相结合可带来数量级的性能提升。

链接https://software.intel.com/en-us/articles/vectorization-a-key-tool-to-improve-performance-on-modern-cpus

在 Java 中,有一个选项可以将其包含在 2020 年的 JDK 15 或 2021 年的 JDK 16 中。请参阅此官方问题


bad*_*mer 6

向量化在科学计算中得到了很大的应用,在科学计算中,大量数据需要得到有效处理。

在实际的编程应用程序中,我知道它已在NUMPY中使用(不确定其他方式)。

Numpy(Python中用于科学计算的软件包),使用矢量化来快速处理n维数组,如果使用内置的python选项来处理数组,这通常会比较慢。

虽然万吨的解释是在那里,这里是矢量化定义为numpy的文档页面

矢量化描述了代码中没有任何显式的循环,索引等操作-当然,这些事情是在优化的预编译C代码中“在幕后”发生的。向量化代码具有许多优点,其中包括:

  1. 向量化的代码更简洁,更易于阅读

  2. 更少的代码行通常意味着更少的错误

  3. 该代码更类似于标准数学符号(通常更容易正确地编码数学结构)

  4. 向量化产生更多的“ Pythonic”代码。没有向量化,我们的代码将效率低下,并且难以阅读循环。