【问题标题】:Explicit vectorization显式矢量化
【发布时间】:2015-09-15 17:33:03
【问题描述】:

据我了解,如果我设置了相应的编译器标志,大多数现代编译器会在适当的情况下自动使用 SIMD 指令进行循环。由于编译器只有在可以确定这样做不会改变程序语义的情况下才能使用向量化,所以在我确实知道它是安全的,但编译器出于各种原因认为它不安全的情况下,它不会使用向量化。

是否有可以在没有库的普通 C++ 中使用的显式矢量化指令,让我自己处理矢量化数据而不是依赖编译器?我想它看起来像这样:

double* dest;
const double* src1, src2;
// ...
for (uint32 i = 0; i < n; i += vectorization_size / sizeof(double))
{
    vectorized_add(&dest[i], &src1[i], &src2[i]);
}

【问题讨论】:

  • 如何编写代码让编译器知道它是安全的?
  • AFAIK,“标准 C++”中没有关于这些选项的内容。但我想微软 C++ 和 GCC/Clang 中都有“专有”选项/指令可以做到这一点。
  • 目前标准 C++ 中没有这方面的内容,尽管标准委员会已经进行了一些讨论。目前,您需要使用平台提供的内在函数,这些内在函数不能跨 CPU 架构移植,只能在编译器之间移植。

标签: c++ vectorization


【解决方案1】:

纯 C++?不会。std::valarray 可以将您的编译器引导到 SIMD 水,但不能让它喝水。

OpenMP 是目前“库”最少的库:它更像是一种语言扩展而不是库,并且所有主要的 C++ 编译器都支持它。虽然主要和历史上用于多核并行,但 OpenMP 4.0 引入了特定于 SIMD 的结构,它至少可以敦促您的编译器矢量化某些可清晰矢量化的过程,即使是带有明显标量子例程的过程。它还可以帮助您识别阻止编译器矢量化的代码方面。 (除此之外……你不也想要多核并行吗?)

double* dest;
const double* src1, src2;

#pragma omp simd
for (int i = 0; i < n; i++)
{
    dest[i] = src1[i] + src2[i];
}

要通过降低精度的操作、多通道聚合、无分支屏蔽等实现最后一英里,确实需要与底层指令集的显式连接,而使用任何接近“普通 C++”的东西是不可能的。不过,OpenMP 可以让您走得更远。

【讨论】:

    【解决方案2】:

    TL;DR 不保证,但 KISS 和您可能会获得高度优化的代码。在修改之前测量和检查生成的代码。

    您可以在在线编译器上使用它,例如gcc.godbolt will vectorize 以下直接调用 std::transform 用于带有 -O3 的 gcc 5.2

    #include <algorithm>
    
    const int sz = 1024;
    
    void f(double* src1, double* src2, double* dest)
    {
        std::transform(src1 + 0, src1 + sz, src2, dest, 
            [](double lhs, double rhs){
            return lhs + rhs;
        });    
    }
    

    本周早些时候有一个similar Q&A。一般主题似乎是,在现代处理器和编译器上,您的代码(普通算法调用)越简单,您就越有可能获得高度优化(矢量化、展开)的代码。

    【讨论】:

    • 这不是矢量化的。不过,它在 -O3 处矢量化。
    • 使用的寄存器不重要。使用的指令是。 movsdaddsd 一次处理一个双精度数。 (请注意,这段代码恰好循环了 8192/8 = 1024 次;矢量化循环的迭代次数更少。)
    猜你喜欢
    • 1970-01-01
    • 2017-06-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 2020-04-22
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多