2010-12-04 13 views
3

私はこれを序文にしています。私はASMでの経験は非常に限られており、SIMDでの経験は非常に限られています。AltiVecへのMMX/SSE命令の移植

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

これはおそらく大きな質問です。ほんの数行のコードであっても、ここで何が起こっているのか分かりません。

本来の機能:

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

あなたは遠くじゃない - 私は、コードを少しクリーンアップ、いくつかのマイナーな問題を修正し、テストハーネスを追加し、OKになりまし動作するようです:

#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

ブリリアント。ありがとうポール。私は 'ゼロ'配列のベクトル型をsigned int型(m128変数のものと一致する)に変更する必要がありましたが、それ以外の場合は絶対的な扱いをしていました。これにより、SIMD拡張機能についてもっと学びたいと思うようになります。 –

+0

@Tim Kane:素晴らしい - それはあなたのためにうれしい。ゼロベクトル上にはっきりと現れました - 今修正されました。 AltiVecは本当にクールですが、残念ながら今のところ出ています。 IntelのAVXやAMDのSSE5など、地平線には刺激的なSIMDがあります。 –

1

(警告:私のAltivecに経験のすべては、Xbox360用/ PS3に取り組んでから来ている - 私は、彼らが他のAltivecにプラットフォームからどのように異なるかわかりません)。

まず、ポインタの位置を確認してください。ほとんどのベクトルロード(およびストア)操作は、16バイトのアライメントされたアドレスからのものであると予想されます。そうでない場合、通常は何も警告なしに実行されますが、期待していたデータは得られません。

アライメントされていない読み込みは可能ですが、基本的にデータの前後に少しずつ読み込んで結合する必要があります。 Apple's Altivec pageを参照してください。私はまた、lvlxlvrxのロード命令を使用し、それらを一緒にOR接続する前にそれを実行しました。


アップ次に、私はあなたの乗算と追加が同じであるかわかりません。 _mm_madd_pi16とvec_msumのどちらも使用していないので、私は同等ではないと私は思います。デバッガをステップバイステップで実行し、同じ入力データに対して同じ出力が得られるようにする必要があります。別の可能な相違点は、オーバーフローを異なる方法で処理できることです(モジュラ対飽和など)。


最後に少なくとも、あなたはだからあなたの労働組合は4つのint型を保持する必要があり、あなたが最後にそれらのすべて4を合計する必要があり代わりに2の時点で4つのint型を計算していません。

関連する問題