我在Matlab、Fortran或其他一些编程语言中多次遇到了“向量化”这个术语,但我从未找到过它的解释以及它的作用。因此,我在这里问一下,“向量化”是什么意思?例如,“循环被向量化”是什么意思?
我在Matlab、Fortran或其他一些编程语言中多次遇到了“向量化”这个术语,但我从未找到过它的解释以及它的作用。因此,我在这里问一下,“向量化”是什么意思?例如,“循环被向量化”是什么意思?
许多CPU都有“向量”或“SIMD”指令集,可以同时对两个、四个或更多数据块应用相同的操作。现代x86芯片具有SSE指令,许多PPC芯片具有“Altivec”指令,甚至一些ARM芯片也有一个向量指令集,称为NEON。
“向量化”(简化)是将循环重写的过程,使其处理数组的4个元素(例如)而不是单独处理一个元素N次,同时进行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”是一个占位符,用于指定编译器使用的任何内置向量指令。
请注意,现代大多数静态编译器能够自动向量化非常简单的循环,例如此处的循环,这通常可以通过编译选项启用(在现代C和C++编译器中,如gcc -O3 -march=native
,默认情况下开启)。 OpenMP #pragma omp simd
有时对提示编译器有帮助,特别是对于“reduction”循环,例如对FP数组求和,在这种情况下,向量化需要假装FP数学是可结合的。
更复杂的算法仍然需要程序员的帮助才能生成良好的向量代码;我们称之为手动矢量化,通常使用类似x86 _mm_add_ps
的内在函数,它们映射到单个机器指令,如SIMD prefix sum on Intel cpu或How to count character occurrences using SIMD。甚至可以使用SIMD解决短小的非循环问题,如Most insanely fastest way to convert 9 char digits into an int or unsigned int或How to convert a binary integer number to a hex string?。
“向量化”这个术语也用于描述更高级的软件转换,其中您可能会完全抽象掉循环并仅描述对数组而不是组成它们的元素进行操作。例如,在某些允许的语言中,当A和B是数组或矩阵时,编写C = A + B
。在低级语言中,您可以通过调用BLAS或Eigen库函数来描述向量化编程风格,而不是手动编写循环。这个问题的其他答案关注于向量化的那种含义和更高级的语言。
向量化是将标量程序转换为向量程序的术语。向量化程序可以从单个指令中运行多个操作,而标量程序只能一次对一对操作数进行操作。
来源于 维基百科:
标量方法:
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 DOCUMENTATION PAGE 中定义的 向量化:
向量化描述了代码中没有任何明确的循环、索引等,当然这些事情仍然在优化的预编译 C 代码中发生。向量化的代码具有许多优点,其中包括:
向量化的代码更简洁,更易读
更少的代码行通常意味着更少的错误
代码更接近标准数学表示法(通常更容易正确编写数学结构)
向量化会产生更“Pythonic”的代码。没有向量化,我们的代码将会充斥着低效和难以阅读的 for 循环。
简单来说,向量化是指优化算法,使它可以利用处理器中的SIMD指令。
AVX、AVX2和AVX512是(英特尔)执行同一操作的指令集,可以在一个指令中对多个数据进行操作。例如,AVX512意味着您可以一次操作16个整数值(4字节)。这意味着,如果您有一个包含16个整数的向量,并且想将每个整数的值加倍然后再加上10,您可以将这些值加载到通用寄存器[a、b、c]中16次并执行相同的操作,也可以将所有16个值加载到SIMD寄存器[xmm、ymm]中并一次性执行操作,从而加速向量数据的计算。
我们通过重新设计数据,利用向量化来优化程序的运行。
唯一的问题是如何处理条件语句,因为条件语句会分支执行流程。这可以通过屏蔽处理来解决,即将条件建模为算术操作。例如,如果我们想将值加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。
矢量化算术是指在单个步骤中对数字列表或“向量”执行单个数学运算的能力。由于科学计算与超级计算有关,而矢量化算术最初出现在超级计算机上,因此我们经常在Fortran中看到它的身影。如今,几乎所有台式机CPU都通过技术(如Intel的SSE)提供某种形式的矢量化算术。GPU也提供了一种形式的矢量化算术。
我认为,由英特尔的人员提出的思想很容易理解。
矢量化是将算法从逐个处理单个值转变为同时处理一组值的过程。现代CPU提供直接支持向量运算的功能,即一条指令应用于多个数据(SIMD)。
例如,一个具有512位寄存器的CPU可以容纳16个32位单精度double值并执行单个计算。
比逐个执行指令快16倍。结合线程和多核CPU,将带来数量级的性能提升。
在Java中,从JDK 15(2020年)开始,已添加了此选项,或者在JDK 16(2021年)后添加。请参阅官方问题。
希望你一切都好!
向量化指的是将标量实现转换为向量实现的所有技术,其中单个操作同时处理多个实体,而不是一次处理一个实体。
向量化是一种优化代码以高效处理大量数据的技术。在NumPy、pandas等科学应用中可以看到向量化的应用,同时在Matlab、图像处理、自然语言处理等领域也可以使用这种技术。总体来说,它优化了程序的运行时间和内存分配。
希望你已经得到了答案!
谢谢。
我会将向量化定义为给定语言的一个特性,其中如何迭代某个集合的元素的责任可以从程序员(例如显式循环元素)委托给语言提供的某个方法(例如隐式循环)。
那么,我们为什么要这样做呢?
for
循环)来利用这些硬件并行化,但当使用语言提供的向量化方法时,这会自动且在后台发生。请参考上面的两个答案。我想要补充一点,进行向量化的原因是这些操作可以轻松地由超级计算机和多处理器并行执行,从而获得很大的性能提升。在单处理器计算机上将不会获得性能提升。