Domanda

Lasciatemi prefazione questo con .. Ho esperienza estremamente limitata con ASM, e ancor meno con SIMD.

Ma succede aver il seguente codice ottimizzato MMX / SSE, che desidero porta indietro per istruzioni AltiVec per l'uso su PPC / processori Cell.

Questo è probabilmente un grande chiedere .. Anche se è solo poche righe di codice, ho avuto problemi a non finire cercando di capire che cosa sta succedendo qui.

La funzione originaria:

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

Eventuali suggerimenti su come potrei riscrivere questa opzione per utilizzare le istruzioni AltiVec?

Il mio primo tentativo (un tentativo molto sbagliato) sembra qualcosa di simile .. Ma non è del tutto (o anche da remoto) corretto.

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;
}
È stato utile?

Soluzione

Non sei molto lontano - ho risolto un paio di problemi minori, ripulito il codice un po ', ha aggiunto un test harness, e sembra funzionare bene ora:

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

Altri suggerimenti

. (Attenzione: tutta la mia esperienza Altivec deriva dal lavorare su Xbox360 / PS3 - Non sono sicuro di quanto siano differenti da altre piattaforme Altivec)

Prima di tutto, si dovrebbe verificare il vostro allineamento puntatore. La maggior parte dei carichi di vettore (e negozi) le operazioni dovrebbero essere allineati da indirizzi a 16 byte. Se non lo sono, le cose di solito portano avanti senza preavviso, ma non sarà possibile ottenere i dati che ti aspettavi.

E 'possibile (ma più lento) per fare i carichi non allineate, ma si deve fondamentalmente a leggere un po' prima e dopo i dati e combinarli. Vedere di Apple pagina Altivec . Ho anche fatto prima utilizzando un lvlx e lvrx istruzioni di carico, e poi li ORing insieme.


Il prossimo, non sono sicuro che i tuoi si moltiplica e aggiunge sono gli stessi. Non ho mai usato né _mm_madd_pi16 o vec_msum, quindi non sono positivi che siano equivalenti. Si dovrebbe scorrere in un debugger e assicurarsi che ti danno la stessa uscita per gli stessi dati di input. Un'altra possibile differenza è che essi possono trattare troppopieno diverso (ad esempio modulare vs satura).


Ultimo ma non meno importante, si sta calcolando 4 int alla volta invece di 2. Così il vostro sindacato dovrebbe contenere 4 int, e si dovrebbe sommare tutti e 4 di loro alla fine.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top