0

現在、SSE3を使用してコードを最適化しています。コードには、ベクトル内のすべての要素を1つの要素だけシフトするように強制する1つのポイントがあります

v[0] = 0   //v is some char* and N = v.size()
for(int i = 1;i<N;i++){
    v[i] = v[i-1];
}

私の知る限り、SSEはベクトルシフトをサポートしていないので、これを最初からコーディングする必要があります。

しかし、それから私は、ポインタをデクリメントするだけだとしたらどうなるかという考えを思いつきました。

v = (v-1); 
v[0] = 0;

このように、操作は一定になり、操作はまったく必要ありません。

私はすでにこれをテストしました、そしてそれは私のテストプログラムのために働きます。
ただし、この操作が安全かどうかはわかりません。

これは本当にばかげた考えですか?

4

3 に答える 3

4

SSEベクトル内の要素のビットごとのシフトと、バイト境界に沿ったレジスタ全体のシフトのいずれかのシフトをサポートします。

ベクトルのタイプが 16 timesuint8_tであると仮定すると、探している操作は次のとおりです。

psrldq xmm, 1      ;packed shift right logical double quad word

本質的な

vec = _mm_srli_si128(vec, 1);   // shift by 1 byte

最初の質問: が char へのポインターである限りv、デクリメントまたはインクリメントは完全に安全です。逆参照はそうではないかもしれません。それはあなたのプログラムに依存します。

あなたの 2 番目の質問に対して: はい、それはばかげた考えのように見えます。最適化しようとしてSSE、バイトへのポインターを使用していくつかのタスクを実行する場合、おそらく何か間違ったことをしている可能性が高くvSSEレジスターに 16 個をロードしようとすると問題が発生します-ミスアライメントによるセグメンテーション違反またはパフォーマンスの低下のいずれかコンパイラに使用を強制するためmovdquです。

于 2012-11-16T08:23:20.187 に答える
2

最も簡単な答え: 投稿したループの代わりに、memmove(v+1, v, N-1) を使用します。これは、movdqu/movdqa/movntdqa とループ展開の適切な組み合わせを使用してハンドコーディングされたアセンブリであるため、まともなシステムでハンドコーディングされたアセンブリと同じくらい速く実行される可能性があります。

より複雑な答え: 全体像を見ると、実際にデータをシフトする必要がある可能性は非常に低いと思います。おそらく、隣接する要素と現在の要素にアクセスする必要がある場合があります。たとえば、v[i] と v[i-1] の両方で何らかの計算を行う場合です。

これを行うために SIMD コードを使用している場合、標準的な手法は、(たとえば) バイト 0..15 を xmm0 にロードし、16..31 を xmm1 にロードしてから、両方のレジスタをシャッフルして要素 1..16 で終了することです。 xmm2. 次に、xmm0 (ここではベクトル化された v[i-1] に対応) と xmm2 (ベクトル化された v[i]) を使用して計算を行うことができます。これは、論理/算術シフトという意味での「シフト」ではなく、SIMD レーン シフトです。

例: アセンブリでのバイトの操作

movdqa mem, xmm0 // load bytes 0..15
loop:
// increment mem by 16
movdqa mem, xmm1 // load bytes 16..31
movdqa xmm0, xmm2 // make a copy
movdqa xmm1, xmm3 // make a copy
psrldq xmm2, 1 // ends up with bytes 1..15 and a zero
pslldq xmm3, 15 // ends up with zeros and byte 16
por xmm2, xmm3 // ends up with bytes 1..16
// do something with xmm3 and xmm0 here, they contain bytes 1..16 and 0..15 respectively
// in other words xmm3 is a lane-shifted
movdqa xmm1, xmm0 // use our copy of bytes 16..31 to continue the loop
// goto loop

なぜこれをしないのか: 「ポインタをデクリメントするとどうなるか ... v = (v-1);」

これはクラッシュします:

char* v = (char*)malloc(...);
v=(v-1);
v[0] = 0; // or any read or write of v[0]

v が割り当てられたメモリのブロックの途中 (先頭ではない) のどこかを指している場合、デクリメントは正常に機能しますが、常にそうであることを確認する方法が必要です (たとえば、メモリが割り当てられているこのトリックを使用する同じ関数で)。

于 2012-11-16T09:41:50.940 に答える
0

ポインターをデクリメントすると、最初に 0 番目の要素で境界外アクセスが発生し、ベクターの位置合わせがずれます。データを除くベクトル操作は、適切に配置されてパフォーマンスが向上します。データが整列されていない場合、命令スケジューラはメモリからの読み取りを 2 つのフェッチに分割する必要があり、パフォーマンスが低下します。

SSE は、ベクトル全体に対してビット シフト操作を提供します。@hirschhornsalz の回答を参照してください。

于 2012-11-16T10:18:00.240 に答える