わかりました。このコードが何をしているのかわかりませんが、ternery演算子を最適化して、コードのこの部分をSSEでのみ動作させる方法を尋ねていることは知っています。最初のステップとして、条件演算子を回避するために整数フラグと乗算を使用するアプローチを試すことをお勧めします。例えば:
このセクション
for(int m=0; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m++)
{
bool bIsEvenFloor = vn1.m128i_u16[m]==0;
vnPxChroma.m128i_u16[m] = m%2==0 ?
(bIsEvenFloor ? vnPxCeilChroma.m128i_u16[m] : vnPxFloorChroma.m128i_u16[m]) :
(bIsEvenFloor ? vnPxFloorChroma.m128i_u16[m] : vnPxCeilChroma.m128i_u16[m]);
}
構文的にはこれと同等です
// DISCLAIMER: Untested both in compilation and execution
// Process all m%2=0 in steps of 2
for(int m=0; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
{
// This line could surely pack muliple u16s into one SSE2 register
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor ^ 0x1 // Flip 1 to 0, 0 to 1
// This line could surely perform an SSE2 multiply across multiple registers
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxCeilChroma.m128i_u16[m] +
iIsOddFloor * vnPxFloorChroma.m128i_u16[m]
}
// Process all m%2!=0 in steps of 2
for(int m=1; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
{
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor ^ 0x1 // Flip 1 to 0, 0 to 1
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxFloorChroma.m128i_u16[m] +
iIsOddFloor * vnPxCeilChroma.m128i_u16[m]
}
基本的に、2つのループに分割すると、シリアルメモリアクセスのパフォーマンスの向上は失われますが、モジュロ演算と2つの条件演算子は削除されます。
ここで、ループごとに2つのブール演算子があり、追加する可能性のある乗算はSSE固有の実装ではないことに気づきました。vn1.m123i_u16 []配列には何が格納されていますか?ゼロと1だけですか?もしそうなら、あなたはこの部分を必要とせず、それをなくすことができます。そうでない場合は、この配列のデータを0と1のみを持つように正規化できますか?vn1.m123i_u16配列に1と0のみが含まれている場合、このコードは次のようになります。
uint16 iIsOddFloor = vn1.m128i_u16[m]
uint16 iIsEvenFloor = iIsOddFloor ^ 0x1 // Flip 1 to 0, 0 to 1
また、SSE乗算を使用して、およびレジスタを実行しisEvenFloor * vnPx... part
たり、格納したりしていないことに気付くでしょう。申し訳ありませんが、u16のSSE組み込み関数を上から乗算/登録することを思い出せませんが、それでもこのアプローチが役立つことを願っています。あなたが調べるべきいくつかの最適化:iIsEvenFloor
iIsOddFloor
// This line could surely pack muliple u16s into one SSE2 register
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor ^ 0x1 // Flip 1 to 0, 0 to 1
// This line could surely perform an SSE2 multiply across multiple registers
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxCeilChroma.m128i_u16[m] +
iIsOddFloor * vnPxFloorChroma.m128i_u16[m]
あなたが投稿したコードのこのセクションと私の修正では、SSE1 / 2/3組み込み関数をまだ十分に活用していませんが、それをどのように行うことができるか(コードをベクトル化する方法)についていくつかのポイントを提供する可能性があります。
最後に、私はすべてをテストすると言います。上記のコードを変更せずに実行し、プロファイルを作成してから、変更を加えて再度プロファイリングします。実際のパフォーマンスの数値はあなたを驚かせるかもしれません!
アップデート1:
私はIntelSIMD組み込み関数のドキュメントを調べて、これに役立つ可能性のある関連する組み込み関数を選び出しました。具体的には、ビット単位のXOR、AND、およびMULT/ADDを見てください。
__m128データ型
__m128iデータ型は、16個の8ビット、8個の16ビット、4個の32ビット、または2個の64ビット整数値を保持できます。
__m128i _mm_add_epi16(__ m128i a、__m128i b)a
の8つの符号付きまたは符号なし16ビット整数をbの8つの符号付きまたは符号なし16ビット整数に追加します
__m128i _mm_mulhi_epu16(__ m128i a、__m128i b)a
からの8つの符号なし16ビット整数にbからの8つの符号なし16ビット整数を乗算します。8つの符号なし32ビット結果の上位16ビットをパックします
R0 = hiword(a0 * b0)
R1 = hiword(a1 * b1)
R2 = hiword(a2 * b2)
R3 = hiword(a3 * b3)
..
R7 = hiword(a7 * b7)
__m128i _mm_mullo_epi16(__ m128i a、__m128i b)a
の8つの符号付きまたは符号なし16ビット整数にbの8つの符号付きまたは符号なし16ビット整数を乗算します。8符号付きまたは符号なし32ビットの結果の上位16ビットをパックします
R0 = loword(a0 * b0)
R1 = loword(a1 * b1)
R2 = loword(a2 * b2)
R3 = loword(a3 * b3)
..
R7 = loword(a7 * b7)
__m128i _mm_and_si128(__ m128i a、__m128i b)
m1の128ビット値とm2の128ビット値のビットごとのANDを実行します。
__m128i _mm_andnot_si128(__ m128i a、__m128i b)b
の128ビット値のビット単位のANDとaの128ビット値のビット単位のNOTを計算します。
__m128i _mm_xor_si128(__ m128i a、__m128i b)
m1の128ビット値とm2の128ビット値のビット単位のXORを実行します。
また、参照用のコード例から
uint16 u1 = u2 = u3 ... = u15 = 0x1
__m128i vnMask = _mm_set1_epi16(0x0001); //8つの符号付き16ビット整数値を設定します。
uint16 vn1 [i] = vnFloors [i]&0x1
__m128i vn1 = _mm_and_si128(vnFloors、vnMask); //aの128ビット値とbの128ビット値のビットごとのANDを計算します。