Почему циклы во встроенной функции не могут быть автоматически векторизованы?

Я пытаюсь векторизовать некоторые простые вычисления для ускорения от архитектуры SIMD. Тем не менее, я также хочу поместить их как встроенные функции, потому что вызовы функций и не векторизованные коды также занимают время вычислений. Тем не менее, я не всегда могу достичь их одновременно. Фактически, большинство моих встроенных функций не могут быть автоматически векторизованы. Вот простой тестовый код, который работает:

inline void add1(double *v, int Length) {
for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1(double v[], int L) {
add1(v, L);
}

int main(){return 0;}

В Mac OS X 10.12.3 скомпилируйте его:

clang++ -O3 -Rpass=loop-vectorize -Rpass-analysis=loop-vectorize -std=c++11 -ffast-math test.cpp

test.cpp:2:5: remark: vectorized loop (vectorization width: 2, interleaved count: 2) [-Rpass=loop-vectorize]
for(int i=0; i < Length; i++) v[i] += 1;
^

Однако что-то очень похожее (только перемещение аргументов в call_add1) не работает:

inline void add1(double *v, int Length) {
for(int i=0; i < Length; i++) v[i] += 1;
}

void call_add1() {
double v[20]={0,1,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9};
int L=20;
add1(v, L);
}

int main(){ return 0;}

Компиляция с помощью той же команды не приводит к выводу. Почему это происходит? Как я могу сделать так, чтобы циклы во встроенных функциях всегда автоматически векторизовались? Я хочу векторизовать множество функциональных циклов, поэтому надеюсь, что исправление не будет сложным.

4

Решение

Компиляция вашего кода с -fsave-optimization-record показывает, что цикл был развернут, а затем устранен.

--- !Passed
Pass:            loop-unroll
Name:            FullyUnrolled
DebugLoc:        { File: main.cpp, Line: 2, Column: 5 }
Function:        _Z9call_add1v
Args:
- String:          'completely unrolled loop with '
- UnrollCount:     '20'
- String:          ' iterations'
...
--- !Passed
Pass:            gvn
Name:            LoadElim
DebugLoc:        { File: main.cpp, Line: 2, Column: 40 }
Function:        _Z9call_add1v
Args:
- String:          'load of type '
- Type:            double
- String:          ' eliminated'
- String:          ' in favor of '
- InfavorOfValue:  '0.000000e+00'

Если вы поместите 4000 элементов в массив, он превысит порог оптимизатора, и clang включит векторизацию.

8

Другие решения

Это потому, что для второго случая компилятор знает, что никаких побочных эффектов нет, и оптимизирует все https://godbolt.org/g/CnojEi лязг 4.0.0 с -O3 оставляет только:

call_add1():
rep ret
main:
xor eax, eax
ret

И ты не понимаешь про магию петель.

В 1-м случае компилятор создает некоторое тело для функции, потому что функция изменяет аргумент. Если вы скомпилировали это как объектный файл. Вы можете ссылаться на эту функцию, и она будет работать. Я предполагаю, что если параметры будут постоянными, то, возможно, функция также останется с пустым телом.

Когда вы распечатываете содержимое, программы не идентичны, но обе они используют векторизованные инструкции: https://godbolt.org/g/KF1kNt

4

Похоже, что компилятор просто развернет и оптимизирует цикл, когда v указано явно. Который является хорошая вещь: код, который не должен быть выполнен, является самым быстрым.

Чтобы убедиться, что это оптимизация, вы можете попытаться сделать некоторые переменные изменчивыми (живой пример).

3
По вопросам рекламы [email protected]