3

はじめに..私はASMの経験が非常に限られており、SIMDの経験はさらに少ないです。

しかし、次のMMX / SSE最適化コードがあり、PPC/Cellプロセッサで使用するためにAltiVec命令に移植したいと思います。

これはおそらく大きな質問です。数行のコードですが、ここで何が起こっているのかを理解するのに問題はありませんでした。

元の機能:

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

2 に答える 2

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;
}
于 2010-12-04T10:46:06.947 に答える
1

(警告:私のAltivecの経験はすべてXbox360 / PS3での作業から来ています-他のAltivecプラットフォームとどれほど違うかはわかりません)。

まず、ポインタの配置を確認する必要があります。ほとんどのベクトルロード(およびストア)操作は、16バイトに整列されたアドレスからのものであると予想されます。そうでない場合、通常は警告なしに処理が続行されますが、期待したデータは取得されません。

アラインされていないロードを実行することは可能ですが(ただし低速です)、基本的にはデータの前後を少し読んでそれらを組み合わせる必要があります。AppleのAltivecページを参照してください。lvlxまた、 and load命令を使用する前にそれをlvrx実行し、次にそれらをOR処理しました。


次は、あなたの掛け算と足し算が同じかどうかわかりません。私は_mm_madd_pi16またはvec_msumのいずれかを使用したことがないので、それらが同等であるとは確信していません。デバッガーをステップスルーして、同じ入力データに対して同じ出力が得られることを確認する必要があります。もう1つの考えられる違いは、オーバーフローの処理が異なる場合があることです(たとえば、モジュラーとサチュレート)。


大事なことを言い忘れましたが、一度に2ではなく4 intを計算しているので、ユニオンは4 intを保持し、最後に4つすべてを合計する必要があります。

于 2010-12-04T10:01:49.360 に答える