SSE-векторизация функции math 'pow' gcc
Я пытался векторизовать цикл, содержащий использование функции "pow" в математической библиотеке. Я знаю, что компилятор Intel поддерживает использование "pow" для инструкций sse, но я не могу заставить его работать с gcc (я думаю). Это тот случай, с которым я работаю:
int main(){
int i=0;
float a[256],
b[256];
float x= 2.3;
for (i =0 ; i<256; i++){
a[i]=1.5;
}
for (i=0; i<256; i++){
b[i]=pow(a[i],x);
}
for (i=0; i<256; i++){
b[i]=a[i]*a[i];
}
return 0;
}
Я компилирую со следующим:
gcc -O3 -Wall -ftree-vectorize -msse2 -ftree-vectorizer-verbose=5 code.c -o runthis
Это относится к os X 10.5.8 с использованием gcc версии 4.2 (я также использовал 4.5 и не мог сказать, что он что-то векторизовал - поскольку он вообще ничего не выводил). Похоже, что ни одна из векторов в векторе не существует - есть ли проблема выделения или какая-либо другая проблема, которую мне нужно использовать? Если я напишу одну из циклов как функцию, я получу немного более подробный вывод (код):
void pow2(float *a, float * b, int n) {
int i;
for (i=0; i<n; i++){
b[i]=a[i]*a[i];
}
}
(используя подробный вывод уровня 7):
note: not vectorized: can't determine dependence between *D.2878_13 and *D.2877_8
bad data dependence.
Я просмотрел страницу gcc auto-vectorization, но это не помогло. Если невозможно использовать pow в версии gcc, где бы я мог найти ресурс для выполнения функции, эквивалентной pow (в основном я имею дело с целыми степенями).
Отредактируйте, так что я просто копался в другом источнике - как он векторизовал это?!:
void array_op(double * d,int len,double value,void (*f)(double*,double*) ) {
for ( int i = 0; i < len; i++ ){
f(&d[i],&value);
}
};
Соответствующий вывод gcc:
note: Profitability threshold is 3 loop iterations.
note: LOOP VECTORIZED.
Ну, теперь я в недоумении - 'd' и 'value' изменяются функцией, которую gcc не знает - странно? Возможно, мне нужно проверить эту часть немного более тщательно, чтобы убедиться, что результаты верны для векторизованной части. Все еще ищете векторную математическую библиотеку - почему нет открытых исходных текстов?
Ответы
Ответ 1
Использование __restrict
или потребляющих входов (назначение локальным варам) перед записью выходов должно помочь.
Как и сейчас, компилятор не может векторизовать, потому что a
может иметь псевдоним b
, поэтому выполнение 4 умножений параллельно и запись обратно 4 значения могут быть неверными.
(Обратите внимание, что __restrict
не гарантирует, что компилятор векторизован, но многое можно сказать, что прямо сейчас он не может).
Ответ 2
Это не ответ на ваш вопрос; а скорее предложение о том, как можно полностью избежать этой проблемы.
Вы упоминаете, что находитесь на OS X; на этой платформе уже есть API-интерфейсы, которые обеспечивают операции, на которые вы смотрите, без необходимости автоматической векторизации. Есть ли причина, по которой вы их не используете? Авто-векторизация действительно классная, но она требует некоторой работы, и в целом она не дает результатов, которые так же хороши, как использование API, которые уже вектонизированы для вас.
#include <string.h>
#include <Accelerate/Accelerate.h>
int main() {
int n = 256;
float a[256],
b[256];
// You can initialize the elements of a vector to a set value using memset_pattern:
float threehalves = 1.5f;
memset_pattern4(a, &threehalves, 4*n);
// Since you have a fixed exponent for all of the base values, we will use
// the vImage gamma functions. If you wanted to have different exponents
// for each input (i.e. from an array of exponents), you would use the vForce
// vvpowf( ) function instead (also part of Accelerate).
//
// If you don't need full accuracy, replace kvImageGamma_UseGammaValue with
// kvImageGamma_UseGammaValue_half_precision to get better performance.
GammaFunction func = vImageCreateGammaFunction(2.3f, kvImageGamma_UseGammaValue, 0);
vImage_Buffer src = { .data = a, .height = 1, .width = n, .rowBytes = 4*n };
vImage_Buffer dst = { .data = b, .height = 1, .width = n, .rowBytes = 4*n };
vImageGamma_PlanarF(&src, &dst, func, 0);
vImageDestroyGammaFunction(func);
// To simply square a instead, use the vDSP_vsq function.
vDSP_vsq(a, 1, b, 1, n);
return 0;
}
В более общем плане, если ваш алгоритм довольно прост, автоинъекция вряд ли принесет отличные результаты. По моему опыту, спектр методов векторизации обычно выглядит примерно так:
better performance worse performance
more effort less effort
+------+------+----------------------+----------------------------+-----------+
| | | | | |
| | use vectorized APIs | auto vectorization |
| skilled vector C | scalar code
hand written assembly unskilled vector C