Pregunta

Déjame incondicional con .. Tengo experiencia muy limitada con ASM, y menos aún con SIMD.

Pero sucede que tengo el siguiente código optimizado MMX / SSE, que me gustaría al puerto a través de las instrucciones AltiVec para su uso en procesadores PPC / célula.

Esta es probablemente una gran pregunta .. A pesar de que es sólo unas pocas líneas de código, he tenido un sinfín de problemas tratando de averiguar lo que está pasando aquí.

La función original:

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;
}

¿Algún consejo sobre cómo podría volver a escribir esto para usar las instrucciones AltiVec?

Mi primer intento (un intento muy mal) se ve algo como esto .. Pero no es del todo (o incluso de forma remota) correcta.

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;
}
¿Fue útil?

Solución

Usted no está lejos - He arreglado algunos problemas menores, limpiado el código un poco, agregó un instrumento de prueba, y parece que funciona bien ahora:

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

Otros consejos

. (Advertencia: toda mi experiencia Altivec viene de trabajar en Xbox360 / PS3 - No estoy seguro de lo diferentes que son de otras plataformas Altivec)

En primer lugar, usted debe comprobar su alineación de punta. La mayoría de las cargas vectoriales (y tiendas) se espera que las operaciones a ser de direcciones alineadas de 16 bytes. Si no lo son, por lo general las cosas van a seguir sin previo aviso, pero no se obtendrán los datos que estaba esperando.

Es posible (pero más lento) para hacer cargas no alineados, pero que, básicamente, tiene que leer un poco antes y después de sus datos y combinarlos. Ver de Apple página Altivec . También he hecho antes de utilizar un lvlx y lvrx instrucciones de carga, y luego la operación lógica OR entre sí.


El siguiente, no estoy seguro de que sus multiplica y añade son los mismos. Nunca he usado ya sea _mm_madd_pi16 o vec_msum, así que no estoy positivo Son equivalentes. Usted debe caminar a través de un depurador y asegurarse de que te dan el mismo resultado para los mismos datos de entrada. Otra posible diferencia es que pueden tratar el desbordamiento de manera diferente (por ejemplo, modular vs. saturado).


Por último, pero no menos importante, está el cálculo de 4 enteros a la vez en lugar de 2. Por lo que su unión debe contener 4 enteros, y usted debe resumir todos los 4 de ellos al final.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top