【问题标题】:What is "vectorization"?什么是“矢量化”?
【发布时间】:2010-11-28 04:09:28
【问题描述】:

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

【问题讨论】:

标签: vectorization simd auto-vectorization


【解决方案1】:

许多 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];

展开这个循环会变成这样:

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”是编译器用来指定向量指令的任何内在函数的占位符。请注意,一些编译器能够自动矢量化像这样的非常简单的循环,这通常可以通过编译选项来启用。更复杂的算法仍然需要程序员的帮助才能生成好的矢量代码。

【讨论】:

  • 这和循环展开/展开有什么区别?
  • 编译器自动矢量化展开的循环不是更容易吗?
  • @StephenCanon 如何检查某些行是否已被矢量化?如果要使用 objdump,会在 objdump 的输出中寻找什么?
  • @Shuklaswag:向量化是编译器可以为你做的事情,但它也是程序员明确自己做的事情。不涉及操作系统。
  • @user1823664 SIMD 指令和寄存器应该存在于 objdump 中。 Example 的矢量化加法程序集。
【解决方案2】:

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

来自wikipedia

标量方法:

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

【讨论】:

  • 这在本质上不与标量方法相同吗?你的语法和循环推进是不同的,但在下面你仍然将它乘以 4 倍。但不知何故,它可能会更快,可能 CPU 有执行一些称为矢量化的技巧的指令。
  • 看来我会在这里回答我自己的问题。当编译器看到向量化方法中的语法时,它会将其转换为优化的 CPU 指令,以乘以向量。像 SIMD。
  • @mskw:这是伪代码,不是 C 矢量扩展的实际语法。在真正的手动矢量化代码中,它看起来像 __m128 va = _mm_loadu_ps( A+i ) 等等,以及 _mm_mul_ps( va, vb ); 和一个存储内在函数。有关使用 AVX2 执行提前编译器无法轻松自动矢量化的更复杂操作的更长示例,请参阅How to count character occurrences using SIMD
【解决方案3】:

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

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

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

虽然有大量的解释,但以下是 矢量化NUMPY 文档页面

中的定义

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

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

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

  3. 代码更接近标准数学符号 (通常更容易正确编码数学 构造)

  4. 矢量化会产生更多“Pythonic”代码。没有 矢量化,我们的代码将充满低效和 for 循环难以阅读。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      向量化,简单来说,就是优化算法,使其可以在处理器中使用 SIMD 指令。

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

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

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

      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,它可以让你使用 pragma 对代码进行矢量化。我认为这是一个很好的起点。 OpenACC 也可以这样说。

      【讨论】:

        【解决方案6】:

        我认为英特尔人很容易掌握。

        向量化是将算法从操作 一次处理一个值到一次处理一组值 时间。现代 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 中。请参阅official issue

        【讨论】:

          【解决方案7】:

          希望你一切都好!

          向量化是指将缩放器实现(其中单个操作一次处理单个实体)转换为向量实现(其中单个操作同时处理多个实体)的所有技术。

          向量化是指一种技术,借助它我们可以优化代码以有效地处理大量数据。在 NumPy、pandas 等科学应用程序中看到矢量化的应用,您也可以在使用 Matlab、图像处理、NLP 等时使用此技术。总体而言,它优化了程序的运行时和内存分配。

          希望你能得到答案!

          谢谢。 ?

          【讨论】:

          • 在对数组的单个元素执行操作时,我们称之为缩放器编码... - 如果您在高级别的元素上执行标量循环像 Python 这样的语言,您的代码没有矢量化。矢量化代码是替代,其中迭代元素仅发生在内部优化函数中,在您的源代码中不可见。我假设您知道这一点,但是在该句子中间添加“标量”编码的定义会使您听起来像是在谈论编译器将标量循环转换为向量代码。
          • (C/C++ 编译器会自动矢量化,但除了有时 memcpy 之外,不会发明对库函数的调用。)
          • 感谢您添加评论,但我的意思是尽可能简单地说矢量化是指将缩放器实现转换的所有技术,其中单个操作一次处理单个实体到矢量实现其中单个操作同时处理多个实体。
          • 对,没错。我建议您 edit 您的回答实际上是这样说的,而不是听起来像您在说“对数组的单个元素执行操作”神奇地变成使用 SIMD、线程和/或本机代码的优化操作(对于尚未编译为本机代码的语言)
          • 我做到了,谢谢你的建议。
          【解决方案8】:

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

          【讨论】:

          • “在单处理器计算机上不会有性能提升”:不正确。大多数现代处理器对矢量化(SSE、Altivec 等由 stephentyrone 命名)具有(有限的)硬件支持,使用时可以显着提高速度。
          • 谢谢,我忘了并行化也可以在那个级别完成。
          猜你喜欢
          • 2010-12-03
          • 2018-05-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-29
          • 2014-07-19
          相关资源
          最近更新 更多