Ben*_*een 46 language-agnostic performance vectorization low-level
为什么,在硬件执行操作的最低级别和所涉及的一般底层操作(即:运行代码时所有编程语言的实际实现的一般性),矢量化通常比循环更快?
当使用矢量化时,计算机在循环时做了什么(我说的是计算机执行的实际计算,而不是程序员编写的计算),或者它有什么不同的做法?
我一直无法说服自己为什么差异应该如此重要.我可能会说服矢量化代码在某处削减一些循环开销,但计算机仍然必须执行相同数量的操作,不是吗?例如,如果我们将大小为N的向量乘以标量,我们将使用N次乘法执行任一方式,不是吗?
Jer*_*fin 46
矢量化(通常使用的术语)是指SIMD(单指令,多数据)操作.
这实际上意味着,一条指令并行地对多个操作数执行相同的操作.例如,要将大小为N的向量乘以标量,让我们调用M可以同时操作的操作数大小.如果是这样,那么它需要执行的指令数大约是N/M,其中(纯粹的标量操作)它必须执行N次操作.
例如,Intel当前的AVX 2指令集使用256位寄存器.这些可以用于保持(和操作)一组4个64位操作数,或者每个32位操作数.
因此,假设您正在处理32位单精度实数,这意味着单个指令可以同时执行8次运算(在您的情况下为乘法),因此(至少在理论上)您可以使用完成N次乘法只有N/8个乘法指令.至少,理论上,这应该允许操作完成的速度大约是一次执行一条指令所允许的速度的8倍.
当然,确切的好处取决于每条指令支持的操作数.英特尔的首次尝试仅支持64位寄存器,因此要同时操作8个项目,这些项目每个只能是8位.他们目前支持256位寄存器,他们已经宣布支持512位(他们甚至可能在一些高端处理器中出货,但至少在普通消费者处理器中没有).说得好一点,充分利用这种能力也是非常重要的.调度指令使您实际上有N个操作数可用并且在正确的时间在正确的位置并不是一件容易的事(根本不是).
为了正确看待事物,(现在很古老的)Cray 1以这种方式获得了很多速度.它的向量单元在64个64位寄存器上运行,因此每个时钟周期可以进行64次双精度运算.在最佳矢量化代码上,它更接近于当前CPU的速度,而不仅仅是基于其(低得多)时钟速度.充分利用这一点并不总是那么容易(但仍然不是).
但请记住,矢量化并不是 CPU并行执行操作的唯一方法.还有指令级并行的可能性,它允许单个CPU(或CPU的单个核心)一次执行多个指令.大多数现代CPU包括硬件(理论上),如果指令是负载,存储和ALU的混合,则每个时钟周期最多执行大约4条指令.当内存不是瓶颈时,它们通常可以平均每个时钟执行接近2条指令,或者在经过良好调整的循环中执行更多指令.
然后,当然,还有多线程 - 在(至少逻辑上)单独的处理器/内核上运行多个指令流.
因此,现代CPU可能有4个内核,每个内核每个时钟可以执行2个向量乘法,每个指令可以在8个操作数上运行.因此,至少在理论上,它可以每个时钟执行4*2*8 = 64次操作.
某些指令的吞吐量更好或更差.例如,FP增加吞吐量低于FMA或在Skylake之前乘以英特尔(每个时钟1个向量而不是2个).但是像AND或XOR这样的布尔逻辑每个时钟吞吐量有3个向量; 它不需要很多晶体管来构建AND/XOR/OR执行单元,因此CPU会复制它们.当使用高吞吐量指令而不是特定执行单元的瓶颈时,总管道宽度(解码并发布到核心的无序部分的前端)的瓶颈是常见的.
Rae*_*ald 12
矢量化是一种并行处理。它使更多的计算机硬件能够专门用于执行计算,因此计算速度更快。
许多数值问题,特别是偏微分方程的求解,需要对大量单元、单元或节点执行相同的计算。矢量化并行执行许多单元/元素/节点的计算。
矢量化使用特殊的硬件。与多核 CPU 不同,多核 CPU 的每个并行处理单元都是一个全功能的 CPU 核心,矢量处理单元只能执行简单的操作,并且所有单元同时执行相同的操作,对一系列数据值进行操作(一个向量)同时。
矢量化有两个主要好处。
主要优点是设计用于支持向量指令的硬件通常具有在使用向量指令时能够并行执行多个 ALU 操作的硬件。例如,如果您要求它使用 16 元素向量指令执行 16 次加法,则它可能有 16 个加法器可以同时并行执行所有加法。访问所有这些加法器1的唯一方法是通过矢量化。使用标量指令,您只会得到 1 个孤独的加法器。
使用向量指令通常可以节省一些开销。您以大块的形式加载和存储数据(在某些最近的 Intel CPU 上一次最多 512 位),并且每次循环迭代都会做更多的工作,因此相对而言循环开销通常较低2,并且您需要更少的指令来执行同样的工作,所以 CPU 前端开销更低,等等。
最后,循环和矢量化之间的二分法很奇怪。当您使用非矢量代码并将其矢量化时,如果之前有循环,您通常会以循环结束,如果没有,则不会结束。比较实际上是在标量(非向量)指令和向量指令之间进行的。
1或至少 16 个中的 15 个,也许其中一个也用于进行标量运算。
2在标量情况下,您可能会以大量循环展开为代价获得类似的循环开销优势。