2010-12-04 2 views
3

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

Но бывает, что у меня есть следующий оптимизированный код 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; 
} 

ответ

3

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

#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; 
} 
+1

Brilliant. Спасибо, Пол. Мне пришлось модифицировать векторный тип массива «нуль», чтобы иметь тип подписанного типа (в соответствии с переменной m128), но в остальном он работал с абсолютным удовольствием (и прорывается с точки зрения производительности). Это заставляет меня больше узнать о расширениях SIMD. – 2010-12-05 11:09:59

1

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

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

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


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


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

Смежные вопросы