Почему GCC не будет автоматически векторизовать этот цикл? - PullRequest
5 голосов
/ 17 февраля 2011

У меня есть следующая программа на 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, но, похоже, они тоже ничего не делают.Любая помощь?

Ответы [ 3 ]

3 голосов
/ 17 февраля 2011

Это, конечно, похоже на ошибку. В следующих эквивалентных функциях 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 приводит к векторизации обеих функций.

2 голосов
/ 17 февраля 2011

Ему не нравится формат внешнего цикла, который мешает ему понять внутренний цикл. Я могу заставить его векторизовать, если просто сложить его в один цикл:

#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 в одно условие цикла, это может быть неправильно)

Возможно, вы сможете воспользоваться этим, поместив упрощенный внутренний цикл в функцию, которую вы вызываете с помощью указателей и счетчика. Даже если он снова вставлен, он может работать нормально. Это предполагает, что вы удалили части вашего цикла while(), которые я только что упростили, но вам нужно сохранить.

1 голос
/ 17 февраля 2011

попробовать:

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
...