Передача команд MMX/SSE в AltiVec

Позвольте мне предисловие к этому с.. У меня очень ограниченный опыт работы с ASM, а тем более с SIMD.

Но бывает, что у меня есть следующий оптимизированный код MMX/SSE, который я бы хотел передать в инструкции AltiVec для использования на процессорах PPC/Cell.

Это, вероятно, большой вопрос. Хотя это всего лишь несколько строк кода, у меня не было никаких проблем, пытаясь понять, что происходит здесь.

Исходная функция:

static inline int convolve(const short *a, const short *b, int n)
{ int out = 0; union { __m64 m64; int i32[2]; } tmp; tmp.i32[0] = 0; tmp.i32[1] = 0; while (n >= 4) { tmp.m64 = _mm_add_pi32(tmp.m64, _mm_madd_pi16(*((__m64 *)a), *((__m64 *)b))); a += 4; b += 4; n -= 4; } out = tmp.i32[0] + tmp.i32[1]; _mm_empty(); while (n --) out += (*(a++)) * (*(b++)); return out;
}

Любые советы о том, как я могу переписать это для использования инструкций AltiVec?

Моя первая попытка (очень неправильная попытка) выглядит примерно так. Но это не совсем (или даже отдаленно) правильно.

static inline int convolve_altivec(const short *a, const short *b, int n)
{ int out = 0; union { vector unsigned int m128; int i64[2]; } tmp; vector unsigned int zero = {0, 0, 0, 0}; tmp.i64[0] = 0; tmp.i64[1] = 0; while (n >= 8) { tmp.m128 = vec_add(tmp.m128, vec_msum(*((vector unsigned short *)a), *((vector unsigned short *)b), zero)); a += 8; b += 8; n -= 8; } out = tmp.i64[0] + tmp.i64[1];
#endif while (n --) out += (*(a++)) * (*(b++)); return out;
}
2 ответа

Вы не за горами - я исправил несколько мелких проблем, немного очистил код, добавил тестовый жгут и, похоже, сейчас работает нормально:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <altivec.h>
static int convolve_ref(const short *a, const short *b, int n)
{ int out = 0; int i; for (i = 0; i < n; ++i) { out += a[i] * b[i]; } return out;
}
static inline int convolve_altivec(const short *a, const short *b, int n)
{ int out = 0; union { vector signed int m128; int i32[4]; } tmp; const vector signed int zero = {0, 0, 0, 0}; assert(((unsigned long)a & 15) == 0); assert(((unsigned long)b & 15) == 0); tmp.m128 = zero; while (n >= 8) { tmp.m128 = vec_msum(*((vector signed short *)a), *((vector signed short *)b), tmp.m128); a += 8; b += 8; n -= 8; } out = tmp.i32[0] + tmp.i32[1] + tmp.i32[2] + tmp.i32[3]; while (n --) out += (*(a++)) * (*(b++)); return out;
}
int main(void)
{ const int n = 100; vector signed short _a[n / 8 + 1]; vector signed short _b[n / 8 + 1]; short *a = (short *)_a; short *b = (short *)_b; int sum_ref, sum_test; int i; for (i = 0; i < n; ++i) { a[i] = rand(); b[i] = rand(); } sum_ref = convolve_ref(a, b, n); sum_test = convolve_altivec(a, b, n); printf("sum_ref = %d\n", sum_ref); printf("sum_test = %d\n", sum_test); printf("%s\n", sum_ref == sum_test ? "PASS" : "FAIL"); return 0;
}
</altivec.h></stdlib.h></stdio.h></assert.h>


(Предупреждение: весь мой опыт Altivec исходит от работы с Xbox360/PS3 - я не уверен, насколько они отличаются от других платформ Altivec).

Прежде всего, вы должны проверить выравнивание указателя. Ожидается, что большинство операций с векторными нагрузками (и хранилищами) будут состоять из согласованных адресов по 16 байт. Если это не так, все будет происходить без предупреждения, но вы не получите ожидаемых данных.

Возможно (но медленнее) выполнять неравномерные нагрузки, но вам в основном нужно прочитать бит до и после ваших данных и объединить их. См. страница Apple Altivec. Я также сделал это перед использованием инструкций загрузки lvlx и lvrx, а затем ORing их вместе.

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

И последнее, но не менее важное: вы вычисляете 4 интервала за раз, а не 2. Таким образом, ваш союз должен содержать 4 ints, и вы должны суммировать все 4 из них в конце.

licensed under cc by-sa 3.0 with attribution.