现在,我已经在matlab, fortran中遇到过几次这个术语…其他的…但我从来没有找到一个解释,它是什么意思,它是什么?所以我在这里问,什么是向量化,例如,“一个循环是向量化的”是什么意思?


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


许多cpu都有“矢量”或“SIMD”指令集,可以同时对两个、四个或更多的数据应用相同的操作。现代x86芯片有SSE指令,许多PPC芯片有“Altivec”指令,甚至一些ARM芯片有一个矢量指令集,称为NEON。

“矢量化”(简化)是重写循环的过程,这样它就不是处理数组中的单个元素N次,而是同时处理数组中的4个元素N/4次。

我选择4是因为它是现代硬件最可能直接支持的32位浮点数或整型数。


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

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”是一个占位符,用于任何内在(s)你的编译器用来指定向量指令。


术语:

请注意,大多数现代提前编译器都能够像这样自动向量化非常简单的循环,这通常可以通过compile选项启用(在现代C和c++编译器中,默认情况下是完全优化的,如gcc -O3 -march=native)。openmp# pragma omp simd有时有助于提示编译器,特别是对于“约简”循环,例如对FP数组求和,其中向量化需要假装FP数学是关联的。

更复杂的算法仍然需要程序员的帮助来生成良好的矢量代码;我们称之为手动矢量化,通常使用像x86 _mm_add_ps这样的intrinsic来映射到单个机器指令,例如在Intel cpu上的SIMD前缀和或如何使用SIMD计算字符出现次数。或者甚至使用SIMD短的非循环问题,如最疯狂的最快方式将9字符数字转换为int或无符号int或如何将二进制整数转换为十六进制字符串?

The term "vectorization" is also used to describe a higher level software transformation where you might just abstract away the loop altogether and just describe operating on arrays instead of the elements that comprise them. e.g. writing C = A + B in some language that allows that when those are arrays or matrices, unlike C or C++. In lower-level languages like that, you could describe calling BLAS or Eigen library functions instead of manually writing loops as a vectorized programming style. Some other answers on this question focus on that meaning of vectorization, and higher-level languages.


请看上面的两个答案。我只是想补充一下,想要做向量化的原因是,这些操作可以很容易地由超级计算机和多处理器并行执行,从而产生巨大的性能增益。在单处理器计算机上不会有性能提升。


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

从维基百科:

标量的方法:

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

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

在真正的编程应用中,我知道它在NUMPY中使用(不确定其他)。

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

尽管有大量的解释,以下是NUMPY文档页中矢量化的定义

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

向量化的代码更简洁,更易于阅读 更少的代码行通常意味着更少的错误 代码更接近于标准的数学符号 (这使得正确编写数学代码更容易 构造) 向量化会产生更多的“python式”代码。没有 向量化,我们的代码就会充斥着低效的和 难以读取循环。


简而言之,矢量化意味着优化算法,使其能够利用处理器中的SIMD指令。

AVX, AVX2 and AVX512 are the instruction sets (intel) that perform same operation on multiple data in one instruction. for eg. AVX512 means you can operate on 16 integer values(4 bytes) at a time. What that means is that if you have vector of 16 integers and you want to double that value in each integers and then add 10 to it. You can either load values on to general register [a,b,c] 16 times and perform same operation or you can perform same operation by loading all 16 values on to SIMD registers [xmm,ymm] and perform the operation once. This lets speed up the computation of vector data.

在向量化中,我们利用了这一点,通过重构数据,我们可以对其执行SIMD操作,并加快程序的速度。

矢量化的唯一问题是处理条件。因为条件分支了执行流。这可以通过屏蔽来处理。通过将条件建模成算术运算。如。如果我们想在value上加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,它允许你使用pragmas向量化代码。我认为这是一个很好的起点。OpenACC也是如此。


由英特尔人我想是很容易把握的。

Vectorization is the process of converting an algorithm from operating on a single value at a time to operating on a set of values at one time. Modern CPUs provide direct support for vector operations where a single instruction is applied to multiple data (SIMD). For example, a CPU with a 512 bit register could hold 16 32- bit single precision doubles and do a single calculation. 16 times faster than executing a single instruction at a time. Combine this with threading and multi-core CPUs leads to orders of magnitude performance gains.

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

在Java中,有一个选项可以包含在2020年的JDK 15或2021年的JDK 16中。请参阅此正式版本。


我会将向量化定义为给定语言的一个特性,其中如何遍历某个集合的元素的责任可以从程序员委托给语言提供的一些方法(例如,显式的元素循环)。

我们为什么要这么做呢?

Code readeability. For some (but not all!) cases operating over the entire collection at once rather than to its elements is easier to read and quicker to code; Some interpreted languages (R, Python, Matlab.. but not Julia for example) are really slow in processing explicit loops. In these cases vectorisation uses under the hood compiled instructions for these "element order processing" and can be several orders of magnitude faster than processing each programmer-specified loop operation; Most modern CPUs (and, nowadays, GPUs) have build-in parallelization that is exploitable when we use the vectorisation method provided by the language rather than our self-implemented order of operations of the elements; In a similar way our programming language of choice will likely use for some vectorisation operations (e.g. matrix operations) software libraries (e.g. BLAS/LAPACK) that exploit multi-threading capabilities of the CPU, another form of parallel computation.

请注意,对于第3点和第4点,一些语言(特别是Julia)允许这些硬件并行也使用程序员定义的顺序处理(例如for循环),但这是自动发生的,在使用语言提供的向量化方法时。

现在,虽然向量化有许多优点,但有时算法使用显式循环比向量化更直观地表达(可能我们需要求助于复杂的线性代数运算、单位和对角矩阵……所有这些都是为了保留我们的“向量化”方法),如果使用显式排序形式没有计算上的缺点,那么这个应该是首选。