Frage

Lassen Sie mich Vorwort dies mit .. Ich habe extrem begrenzte Erfahrung mit ASM, und noch weniger mit SIMD.

Aber es kommt vor, dass ich die folgende MMX / SSE optimierten Code haben, dass ich auf zu AltiVec Anweisungen für die Verwendung auf PPC / Cell-Prozessoren zu portieren möchten.

Dies ist wahrscheinlich eine große fragen .. Auch wenn es nur ein paar Zeilen Code ist, ich habe kein Ende der Schwierigkeiten hatte, versuchte herauszufinden, was hier vor sich geht.

Die ursprüngliche Funktion:

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

Irgendwelche Tipps, wie ich vielleicht umschreiben dieser AltiVec Anweisungen zu benutzen?

Mein erster Versuch (ein sehr falscher Versuch) sieht wie folgt aus etwas .. Aber es ist nicht ganz (oder auch nur entfernt) korrekt ist.

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;
}
War es hilfreich?

Lösung

Sie sind nicht weit weg - Ich reparierte ein paar kleineren Probleme, bereinigt den Code ein wenig, einen Testbaum hinzugefügt, und es scheint zu funktionieren OK jetzt:

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

Andere Tipps

. (Achtung: alle meine Altivec Erfahrung kommt auf Xbox360 / PS3 von der Arbeit - ich bin mir nicht sicher, wie unterschiedlich sie von anderen AltiVec-Plattformen sind)

Zunächst einmal sollten Sie Ihre Zeiger Ausrichtung überprüfen. Die meisten Vektor Lasten (und Speicher) Operationen von 16-Byte-ausgerichteten Adressen erwartet werden. Sind sie es nicht, wird es in der Regel ohne Vorwarnung weitermachen, aber Sie werden nicht die Daten erhalten Sie erwartet hatten.

Es ist möglich (aber langsamer) unaligned viel zu tun, aber Sie haben im Grunde ein wenig vor und nach Ihren Daten zu lesen und sie kombinieren. Siehe Apples Altivec . Ich habe es auch getan, bevor ein lvlx und lvrx Ladebefehle verwenden und ORing sie dann zusammen.


Als nächstes, ich bin nicht sicher, dass Ihre Multiplikationen und Additionen gleich sind. Ich habe noch nie so oder _mm_madd_pi16 oder vec_msum verwendet, so dass ich nicht sicher bin, sie sind gleichwertig. Sie sollten in einem Debugger Schritt durch und stellen Sie sicher, sie geben Ihnen die gleiche Leistung für die gleichen Eingangsdaten. Ein weiterer möglicher Unterschied besteht darin, dass sie behandeln Überlauf können anders (z modular gegen sättigen).


Last but not least, Sie computing 4 ints zu einer Zeit statt 2. So Ihre Vereinigung 4 Ints halten sollte, und Sie sollten alle 4 von ihnen am Ende summieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top