【问题标题】:Auto vectorization not working自动矢量化不起作用
【发布时间】:2014-06-16 05:32:22
【问题描述】:

我正在尝试让我的代码自动矢量化,但它不起作用。

int _tmain(int argc, _TCHAR* argv[])
{
    const int N = 4096;
    float x[N];
    float y[N];
    float sum = 0;

    //create random values for x and y 
    for (int i = 0; i < N; i++)
    {
        x[i] = rand() >> 1;
        y[i] = rand() >> 1;
    }

    for (int i = 0; i < N; i++){
        sum += x[i] * y[i];
    }
}

这里没有循环矢量化,但我真的只对第二个循环感兴趣。

我正在使用 Visual Studio Express 2013 并使用 /O2/Qvec-report:2(报告循环是否已矢量化)选项进行编译。当我编译时,我收到以下消息:

--- Analyzing function: main
c:\users\...\documents\visual studio 2013\projects\intrin3\intrin3\intrin3.cpp(28) : info C5002: loop not vectorized due to reason '1200'
c:\users\...\documents\visual studio 2013\projects\intrin3\intrin3\intrin3.cpp(41) : info C5002: loop not vectorized due to reason '1305'

原因 '1305',如 HERE 所示,表示“编译器无法识别此循环的正确矢量化类型信息”。我不太确定这意味着什么。有什么想法吗?

将第二个循环分成两个循环后:

for (int i = 0; i < N; i++){
    sumarray[i] = x[i] * y[i];
}

for (int i = 0; i < N; i++){
    sum += sumarray[i];
}

现在上述循环中的第一个循环矢量化,但第二个循环没有,再次出现错误代码 1305。

【问题讨论】:

  • 您要为哪个 SIMD 主机编译?可能是主机没有提供所需的指令(我怀疑您需要水平添加。您是否尝试将循环分成两部分:一个生成 sum[] 向量,然后第二个循环添加sum[] 中的元素?这至少可以缩小范围。
  • 我不确定您所说的 SIMD 主机是什么意思。我的 CPU 是英特尔酷睿 i7。另外,真的很抱歉,但我误传第一个循环确实被矢量化了,这是不正确的。我已经更新了帖子和输出消息。感谢您的建议,现在将尝试打破循环。是否有必要将其设为向量还是可以使用 sum 数组?
  • 打破循环并将相关信息添加到问题中。
  • 尝试将“/arch:AVX”添加到命令行。它应该能够生成额外的 SIMD 指令。
  • @JonB.Jones:我的意思是,对于 SIMD 主机,您正在为什么 CPU 生成代码,有哪些 SIMD 指令可用?有时循环可以被向量化,但随后代码无法生成,因为 CPU 不提供向量化循环所需的那些 SIMD 指令。

标签: c++ optimization vectorization sse simd


【解决方案1】:

发生错误 1305 是因为优化器没有对循环进行矢量化,因为没有使用值 sum。只需添加 printf("%d\n", sum) 即可解决此问题。但随后您会收到一个新的错误代码 1105“循环包含无法识别的归约操作”。要解决此问题,您需要设置 /fp:fast

原因是浮点运算不是关联的,使用 SIMD 或 MIMD(即使用多个线程)的归约需要关联。通过使用更宽松的浮点模型,您可以进行归约。

我刚刚使用以下代码对其进行了测试,默认的fp:precise 没有矢量化,而当我使用fp:fast 时它会。

#include <stdio.h>
int main() {
    const int N = 4096;
    float x[N];
    float y[N];
    float sum = 0;
    for (int i = 0; i < N; i++){
        sum += x[i] * y[i];
    }
    printf("sum %f\n", sum);
}

关于您关于rand() 函数循环的问题,rand() 函数不是 SIMD 函数。它不能被矢量化。您需要找到一个 SIMD rand() 函数。我不知道一个。另一种方法是预先计算一个随机数数组并改用该数组。无论如何,rand() 是一个可怕的随机数生成器,仅对某些玩具箱有用。考虑使用 Mersenne twister PRNG。

【讨论】:

  • 所以,只是为了确保.. 像这样编译: cl filename.cpp /O2 /fp:fast /Qvec-report:2 ?因为那仍然给了我相同的错误代码。
  • @JonB.Jones,你不能用rand() 函数向量化你的函数,但是如果你使用fp:fast,你可以向量化减少。我自己测试过。
  • @JonB.Jones,您收到错误代码 1305“类型信息不足”。我收到错误代码 1105“循环包含无法识别的缩减操作”。我不确定为什么我们会得到不同的错误代码。 blogs.msdn.com/b/nativeconcurrency/archive/2012/05/22/…
  • @JonB.Jones,没有printf 我得到了你的错误代码。当我打印该值时,我没有收到错误代码并且它被矢量化了。显然,如果您不使用该值,优化器不会对循环进行矢量化。尝试添加printf("%f\n", sum) and fp:fast`。
  • 没关系,我并不担心 rand() 函数的循环。至于另一个,添加 printf() 函数确实成功了!非常感谢!
【解决方案2】:

一个问题可能是您的堆栈分配不一定与您的编译器对齐。如果您的编译器支持 c++11,您可以使用:

float x[N] alignas(16);
float y[N] alignas(16);

显式获取 16 字节对齐的内存,这是大多数 SSE 操作所必需的。


编辑:

即使对齐不是问题并且您的编译器正在向量化未对齐的代码,您也应该进行此优化,因为与对齐的对应物相比,未对齐的 SSE 操作非常慢。

【讨论】:

    猜你喜欢
    • 2018-12-16
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    相关资源
    最近更新 更多