【问题标题】:Why won't GCC auto-vectorize this loop?为什么 GCC 不会自动矢量化这个循环?
【发布时间】:2011-06-28 17:32:46
【问题描述】:

我有以下 C 程序(我的实际用例的简化,它表现出相同的行为)

#include <stdlib.h>
#include <math.h>
int main(int argc, char ** argv) {
    const float * __restrict__ const input = malloc(20000*sizeof(float));
    float * __restrict__ const output = malloc(20000*sizeof(float));

    unsigned int pos=0;
    while(1) {
            unsigned int rest=100;
            for(unsigned int i=pos;i<pos+rest; i++) {
                    output[i] = input[i] * 0.1;
            }

            pos+=rest;            
            if(pos>10000) {
                    break;
            }
    }
}

当我用

编译时
 -O3 -g -Wall -ftree-vectorizer-verbose=5 -msse -msse2 -msse3 -march=native -mtune=native --std=c99 -fPIC -ffast-math

我得到了输出

main.c:10: note: not vectorized: unhandled data-ref 

其中 10 是内部 for 循环的行。当我查找它为什么会这样说时,它似乎是在说指针可以别名,但它们不能在我的代码中,因为我有 __restrict 关键字。他们还建议包括 -msse 标志,但他们似乎也没有做任何事情。有什么帮助吗?

【问题讨论】:

  • 什么版本的gcc?一个工作示例也可能很有用,当我用 4.4.5 尝试它时,它是一个矢量化的破解版本
  • 你能发布编译的示例代码吗?当我填写一些虚拟值时,循环被矢量化了......
  • 我已将示例更新为可编译。它仍然不会矢量化。我正在使用“gcc(Debian 4.4.5-10)4.4.5”

标签: c gcc vectorization


【解决方案1】:

这确实看起来像一个错误。在下面的等效函数中,foo() 是矢量化的,但 bar() 不是,在为 x86-64 目标编译时:

void foo(const float * restrict input, float * restrict output)
{
    unsigned int pos;
    for (pos = 0; pos < 10100; pos++)
        output[pos] = input[pos] * 0.1;
}

void bar(const float * restrict input, float * restrict output)
{
    unsigned int pos;
    unsigned int i;
    for (pos = 0; pos <= 10000; pos += 100)
        for (i = 0; i < 100; i++)
            output[pos + i] = input[pos + i] * 0.1;
}

添加 -m32 标志,改为针对 x86 目标进行编译,会导致两个函数都被矢量化。

【讨论】:

  • 谢谢!我在 64 位平台上!做 -m32 使它完美地工作。我现在正在提交错误报告。其他答案很好,但实际上只是解决方法,因为这应该无需修改。
  • 请注意,32 位可执行文件可能比非矢量化的 64 位要慢得多,因此除非您的目标纯粹是“使用 SSE”,否则您应该分析您的整个应用程序。
  • 谢谢 Ben,我实际上并没有使用它来编译我的代码,而只是为了提交错误报告。我只需稍微重新排列一下,就可以让它在 64 位上正确矢量化。
【解决方案2】:

它不喜欢阻止它理解内循环的外循环格式。如果我把它折叠成一个循环,我可以让它矢量化:

#include <stdlib.h>
#include <math.h>
int main(int argc, char ** argv) {
    const float * __restrict__ input = malloc(20000*sizeof(float));
    float * __restrict__ output = malloc(20000*sizeof(float));

    for(unsigned int i=0; i<=10100; i++) {
            output[i] = input[i] * 0.1f;
    }
}

(请注意,我并没有想太多关于如何正确地将pos+rest限制转换为单个for循环条件,可能是错误的)

您可以通过将简化的内部循环放入您使用指针和计数调用的函数中来利用这一点。即使它再次内联,它也可以正常工作。这是假设您删除了我刚刚简化但您需要保留的 while() 循环的部分内容。

【讨论】:

    【解决方案3】:

    尝试:

    const float * __restrict__ input = ...;
    float * __restrict__ output = ...;
    

    通过改变周围的事物进行一些实验:

    #include <stdlib.h>
    #include <math.h>
    
    int main(int argc, char ** argv) {
    
        const float * __restrict__ input = new float[20000];
        float * __restrict__  output = new float[20000];
    
        unsigned int pos=0;
        while(1) {
            unsigned int rest=100;
            output += pos;
            input += pos;
            for(unsigned int i=0;i<rest; ++i) {
                output[i] = input[i] * 0.1;
            }
    
            pos+=rest;
            if(pos>10000) {
                break;
            }
        }
    }
    
    g++ -O3 -g -Wall -ftree-vectorizer-verbose=7 -msse -msse2 -msse3 -c test.cpp
    
    test.cpp:14: note: versioning for alias required: can't determine dependence between *D.4096_24 and *D.4095_21
    test.cpp:14: note: mark for run-time aliasing test between *D.4096_24 and *D.4095_21
    test.cpp:14: note: Alignment of access forced using versioning.
    test.cpp:14: note: Vectorizing an unaligned access.
    test.cpp:14: note: vect_model_load_cost: unaligned supported by hardware.
    test.cpp:14: note: vect_model_load_cost: inside_cost = 2, outside_cost = 0 .
    test.cpp:14: note: vect_model_simple_cost: inside_cost = 2, outside_cost = 0 .
    test.cpp:14: note: vect_model_simple_cost: inside_cost = 2, outside_cost = 1 .
    test.cpp:14: note: vect_model_simple_cost: inside_cost = 1, outside_cost = 0 .
    test.cpp:14: note: vect_model_store_cost: inside_cost = 1, outside_cost = 0 .
    test.cpp:14: note: cost model: Adding cost of checks for loop versioning to treat misalignment.
    
    test.cpp:14: note: cost model: Adding cost of checks for loop versioning aliasing.
    
    test.cpp:14: note: Cost model analysis:
      Vector inside of loop cost: 8
      Vector outside of loop cost: 6
      Scalar iteration cost: 5
      Scalar outside cost: 1
      prologue iterations: 0
      epilogue iterations: 0
      Calculated minimum iters for profitability: 2
    
    test.cpp:14: note:   Profitability threshold = 3
    
    test.cpp:14: note: Vectorization may not be profitable.
    test.cpp:14: note: create runtime check for data references *D.4096_24 and *D.4095_21
    test.cpp:14: note: created 1 versioning for alias checks.
    
    test.cpp:14: note: LOOP VECTORIZED.
    test.cpp:4: note: vectorized 1 loops in function.
    
    Compilation finished at Wed Feb 16 19:17:59
    

    【讨论】:

    • 这样做的理由是什么?
    • @Oli 只是一个猜测,可能是他的编译器不喜欢额外的 const 或 __restrict 形式
    • @Jeremy 看到我的更新,它不喜欢运行时开始绑定。我猜它认为 pos 可能以某种方式别名。
    猜你喜欢
    • 2011-12-29
    • 2014-01-10
    • 2016-06-03
    • 1970-01-01
    • 2019-06-06
    • 2020-12-11
    • 2019-04-12
    • 2019-04-05
    • 2018-12-16
    相关资源
    最近更新 更多