5

elmaelmcは両方ともunsigned long配列です。res1とですres2

unsigned long simdstore[2];  
__m128i *p, simda, simdb, simdc;  
p = (__m128i *) simdstore;  

for (i = 0; i < _polylen; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k++)  
    {
        //res1[i + k] ^= _mulpre1[u1][k];  
        //res2[i + k] ^= _mulpre2[u2][k];               

        simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdc = _mm_xor_si128 (simda, simdb);  
        _mm_store_si128 (p, simdc);  
        res1[i + k] = simdstore[0];  
        res2[i + k] = simdstore[1];                     
    }     
}  

forループ内には、要素のXORの非simdバージョンとsimdバージョンの両方が含まれています。2番目のforループ内の最初の2行は、明示的なXORを実行しますが、残りは同じ操作のsimdバージョンを実装します。

このループは外部から何百回も呼び出されるため、このループを最適化すると、合計計算時間を短縮できます。

問題は、simdコードがスカラーコードよりも何倍も遅く実行されることです。

編集:部分的な展開を行いました

__m128i *p1, *p2, *p3, *p4;  
p1 = (__m128i *) simdstore1;  
p2 = (__m128i *) simdstore2;  
p3 = (__m128i *) simdstore3;  
p4 = (__m128i *) simdstore4;  

for (i = 0; i < 20; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k = k + 4)  
    {
        simda1  = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simda2  = _mm_set_epi64x (_mulpre2[u2][k + 1], _mulpre1[u1][k + 1]);  
        simda3  = _mm_set_epi64x (_mulpre2[u2][k + 2], _mulpre1[u1][k + 2]);  
        simda4  = _mm_set_epi64x (_mulpre2[u2][k + 3], _mulpre1[u1][k + 3]);  

        simdb1  = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdb2  = _mm_set_epi64x (res2[i + k + 1], res1[i + k + 1]);  
        simdb3  = _mm_set_epi64x (res2[i + k + 2], res1[i + k + 2]);  
        simdb4  = _mm_set_epi64x (res2[i + k + 3], res1[i + k + 3]);  

        simdc1  = _mm_xor_si128 (simda1, simdb1);  
        simdc2  = _mm_xor_si128 (simda2, simdb2);  
        simdc3  = _mm_xor_si128 (simda3, simdb3);  
        simdc4  = _mm_xor_si128 (simda4, simdb4);  

        _mm_store_si128 (p1, simdc1);  
        _mm_store_si128 (p2, simdc2);  
        _mm_store_si128 (p3, simdc3);  
        _mm_store_si128 (p4, simdc4);  

        res1[i + k]= simdstore1[0];  
        res2[i + k]= simdstore1[1]; 
        res1[i + k + 1]= simdstore2[0];  
        res2[i + k + 1]= simdstore2[1];   
        res1[i + k + 2]= simdstore3[0];  
        res2[i + k + 2]= simdstore3[1]; 
        res1[i + k + 3]= simdstore4[0];  
        res2[i + k + 3]= simdstore4[1];   
    }  
}  

しかし、結果はあまり変わりません。それでもスカラーコードの2倍の時間がかかります。

4

4 に答える 4

7

免責事項:私はPowerPCのバックグラウンドを持っているので、ここで言っているのは完全なホグウォッシュかもしれません。しかし、結果にすぐにアクセスしようとするため、ベクターパイプラインが停止しています。

すべてをベクターパイプラインに保持するのが最善です。vectorからintまたはfloatに変換したり、結果をメモリに保存したりすると、すぐに停止します。

SSEまたはVMXを処理する場合の最適な操作モードは、ロード、処理、保存です。データをベクトルレジスタにロードし、すべてのベクトル処理を実行してから、メモリに保存します。

推奨:いくつかの__m128iレジスタを予約し、ループを数回展開してから保存します。

編集:また、展開し、res1とres2を16バイト整列すると、このsimdstoreの間接参照(おそらくLHSと別のストール)を経由せずに、結果をメモリに直接保存できます。

編集:明白なことを忘れました。ポリゴンが通常大きい場合は、反復ごとにデータキャッシュのプリフェッチを実行することを忘れないでください。

于 2010-12-09T04:58:54.860 に答える
5

ここでは、実行されているロードとストアの数に比べて計算をほとんど行っていないため、SIMDのメリットはほとんどありません。この場合、特に64ビットモードで使用できるx86-64 CPUを使用している場合は、スカラーコードを使用する方が便利です。これにより、現在パフォーマンスの主要な要因であるロードとストアの数が減ります。

(注:特にCore 2以降を使用している場合は、ループを展開しないでください。

于 2010-12-09T10:08:57.137 に答える
4

コードの外観res1とres2は、完全に独立したベクトルのようです。それでも、同じレジスタでそれらを混合して、それらを排他的論理和します。

次のような別のレジスタを使用します(ベクトルはすべて整列している必要があります)。

__m128i x0, x1, x2, x3;  
for (i = 0; i < _polylen; i++)  
{  

    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k+=2)  
    {     
        //res1[i + k] ^= _mulpre1[u1][k];
        x0= _mm_load_si128(&_mulpre1[u1][k]);
        x1= _mm_load_si128(&res1[i + k]);
        x0= _mm_xor_si128 (x0, x1);
        _mm_store_si128 (&res1[i + k], x0);
        //res2[i + k] ^= _mulpre2[u2][k];               
        x2= _mm_load_si128(&_mulpre2[u2][k]);
        x3= _mm_load_si128(&res2[i + k]);
        x2= _mm_xor_si128 (x2, x3);
        _mm_store_si128 (&res2[i + k], x2);
   }     
}  

私は4つのレジスタのみを使用していることに注意してください。手動で展開して、x86の8つのレジスタすべてまたはx86_64以上のレジスタを使用できます。

于 2010-12-09T06:00:23.470 に答える
2

私もSIMDの専門家ではありませんが、前述のEboMikeのデロールと組み合わせて、データをプリフェッチすることでメリットが得られるようです。res1とres2を1つの整列された配列(他に使用するものに応じて構造体の)にマージした場合にも役立つ可能性があります。追加のコピーは必要なく、直接操作できます。

于 2010-12-09T05:13:40.920 に答える