什么是“向量化”?


Answers:


222

许多CPU具有“向量”或“ SIMD”指令集,这些指令集将相同的操作同时应用于两个,四个或更多数据。现代的x86芯片具有SSE指令,许多PPC芯片具有“ Altivec”指令,甚至某些ARM芯片也具有称为NEON的矢量指令集。

“向量化”(简化)是重写循环的过程,以便与其同时处理(例如)数组的4个元素N / 4次,而不是处理数组的单个元素N次。

(我之所以选择4,是因为这是现代硬件最有可能直接支持的功能;“向量化”一词也用于描述更高级别的软件转换,您可以在其中完全抽象出循环并仅描述对数组而不是元素的操作。组成它们)


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

for (int i=0; i<16; ++i)
    C[i] = A[i] + B[i];

展开此循环会将其转换为如下形式:

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];
}

另一方面,将其向​​量化会生成如下内容:

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

其中“ addFourThingsAtOnceAndStoreResult”是您的编译器用来指定矢量指令的任何内部函数的占位符。请注意,某些编译器能够自动矢量化非常简单的这样的循环,通常可以通过compile选项启用。更复杂的算法仍然需要程序员的帮助才能生成良好的矢量代码。


11
与循环展开/展开之间有什么区别?
杰里米·鲍威尔

1
编译器自动展开展开的循环会更轻松吗?
Nikos Athanasiou 2015年

@NikosAthanasiou:这是合理的,但是一般来说,编译器应该能够自动向量化任何一个循环,因为它们都非常简单。
斯蒂芬·佳能

1
@StephenCanon如何检查某些行是否已向量化?如果使用objdump,那么在objdump的输出中会寻找什么?
user1823664

3
@Shuklaswag:向量化是编译器可以为您完成的工作,但它也是程序员明确地自己完成的工作。不涉及操作系统。
斯蒂芬·佳能

32

向量化是用于将标量程序转换为向量程序的术语。向量化程序可以从一条指令执行多个操作,而标量只能一次对成对的操作数进行操作。

来自维基百科

标量方法:

for (i = 0; i < 1024; i++)
{
   C[i] = A[i]*B[i];
}

向量化方法:

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

这在本质上与Scalar方法不一样吗?您的语法和循环前进是不同的,但是在下面您仍然将其乘以4倍。但是以某种方式,它可能会更快,CPU可能会执行一些称为“向量化”的技巧。
mskw '17

看来我会在这里回答我自己的问题。当编译器看到矢量化方法中的语法时,会将其转换为乘以矢量的优化CPU指令。就像SIMD。
mskw

10

它指的是在单个步骤中对数字列表或“向量”执行单个数学运算的能力。您经常在Fortran中看到它,因为它与科学计算相关联,科学计算与超级计算相关联,超级计算最早出现在矢量化算法上。如今,几乎所有台式机CPU都通过Intel SSE等技术提供某种形式的矢量化算术。GPU还提供了一种矢量化算术形式。


7

向量化在科学计算中被大量使用,在科学计算中,大量数据需要得到有效处理。

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

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

尽管有大量的解释,但此处的“ 数字化”定义如下:

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

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

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

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

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


4

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

AVX,AVX2和AVX512是在一条指令中对多个数据执行相同操作的指令集(英特尔)。例如 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.

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

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

但是,这是一个非常琐碎的示例...因此,c是我们的屏蔽向量,我们根据其值执行二进制运算。这避免了执行流的分支,并使向量化成为可能。

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

我想谈谈OpenMP,它可以让您使用编译指示对代码进行矢量化处理。我认为这是一个很好的起点。对于OpenACC也可以这样说。


0

我认为,通过英特尔人员,您很容易掌握。

向量化是将算法从一次对一个值进行运算转换为一次对一组值进行运算的过程。现代CPU直接支持将单个指令应用于多个数据(SIMD)的向量运算。

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

比一次执行一条指令快16倍。将此与线程和多核CPU结合使用,可将性能提高几个数量级。

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

在Java中,可以选择将此选项包含在2020年的Jdk 15中或2021年的JDK 16中。

https://bugs.openjdk.java.net/browse/JDK-8201271


-4

请参阅上面的两个答案。我只是想补充一下,要进行矢量化的原因是,这些操作可以由超级计算机和多处理器轻松地并行执行,从而获得很大的性能提升。在单处理器计算机上,不会提高性能。


12
“在单处理器计算机上不会有性能提升”:不是。大多数现代处理器都对矢量化(SSE,Altivec等,由stephentyrone命名)具有(有限的)硬件支持,使用时可以大大提高速度。
sleske

谢谢,我忘记了并行化也可以在该级别上完成。
拉里·渡边
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.