定数シフトのみを使用して可変ビットシフトをエミュレートしますか?

どうぞ...

Mike Acton が、CellPerformance サイトで CELL/PS3 マイクロコード化されたシフトを使用するよりも高速であると主張したため、これらも試してみることにしました。しかし、私のすべてのテストでは、マイクロコード化されたバージョンを使用することは、間接シフトの完全な汎用分岐フリー置換よりも高速であるだけでなく、コード (1 命令) に必要なメモリがはるかに少なくなります。

これらをテンプレートとして使用した唯一の理由は、符号付き (通常は算術) シフトと符号なし (論理) シフトの両方で正しい出力を得るためです。

template <typename T> FORCEINLINE T VariableShiftLeft(T nVal, int nShift)
{   // 31-bit shift capability (Rolls over at 32-bits)
    const int bMask1=-(1&nShift);
    const int bMask2=-(1&(nShift>>1));
    const int bMask3=-(1&(nShift>>2));
    const int bMask4=-(1&(nShift>>3));
    const int bMask5=-(1&(nShift>>4));
    nVal=(nVal&bMask1) + nVal;   //nVal=((nVal<<1)&bMask1) | (nVal&(~bMask1));
    nVal=((nVal<<(1<<1))&bMask2) | (nVal&(~bMask2));
    nVal=((nVal<<(1<<2))&bMask3) | (nVal&(~bMask3));
    nVal=((nVal<<(1<<3))&bMask4) | (nVal&(~bMask4));
    nVal=((nVal<<(1<<4))&bMask5) | (nVal&(~bMask5));
    return(nVal);
}
template <typename T> FORCEINLINE T VariableShiftRight(T nVal, int nShift)
{   // 31-bit shift capability (Rolls over at 32-bits)
    const int bMask1=-(1&nShift);
    const int bMask2=-(1&(nShift>>1));
    const int bMask3=-(1&(nShift>>2));
    const int bMask4=-(1&(nShift>>3));
    const int bMask5=-(1&(nShift>>4));
    nVal=((nVal>>1)&bMask1) | (nVal&(~bMask1));
    nVal=((nVal>>(1<<1))&bMask2) | (nVal&(~bMask2));
    nVal=((nVal>>(1<<2))&bMask3) | (nVal&(~bMask3));
    nVal=((nVal>>(1<<3))&bMask4) | (nVal&(~bMask4));
    nVal=((nVal>>(1<<4))&bMask5) | (nVal&(~bMask5));
    return(nVal);
}

編集: isel() に関する注意 あなたのウェブサイトで isel() コードを見ました。

// if a >= 0, return x, else y
int isel( int a, int x, int y )
{
    int mask = a >> 31; // arithmetic shift right, splat out the sign bit
    // mask is 0xFFFFFFFF if (a < 0) and 0x00 otherwise.
    return x + ((y - x) & mask);
};

FWIW、isel() を書き直してマスクとマスクの補数を行うと、PowerPC ターゲットでより高速になります。これは、コンパイラが「andc」オペコードを生成するほどスマートであるためです。オペコードの数は同じですが、オペコードの結果から入力レジスタへの依存関係が 1 つ少なくなっています。 2 つのマスク操作は、スーパースカラー プロセッサで並列に発行することもできます。すべてが正しく並んでいれば、2 ~ 3 サイクル速くなる可能性があります。 PowerPC バージョンでは、戻り値を次のように変更する必要があります:

return (x & (~mask)) + (y & mask);

これはどうですか:

if (y & 16) x <<= 16;
if (y & 8) x <<= 8;
if (y & 4) x <<= 4;
if (y & 2) x <<= 2;
if (y & 1) x <<= 1;

実行にはおそらくまだ時間がかかりますが、他のコードが間にある場合は簡単にインターリーブできます。


最大シフトが 31 であると仮定しましょう。したがって、シフト量は 5 ビットの数値です。シフトは累積的であるため、これを 5 つの一定のシフトに分けることができます。明らかなバージョンでは分岐が使用されていますが、あなたはそれを除外しました.

Nにしましょう 1 から 5 までの数値を指定します。x をシフトします。 2 N 値が 2 N のビットの場合 y で設定されます 、それ以外の場合は x をそのままにします。これを行う 1 つの方法:

#define SHIFT(N) x = isel(((y >> N) & 1) - 1, x << (1 << N), x);

マクロは x に x << 2ᴺ のいずれかを割り当てます または x 、N th かどうかに応じて ビットが y に設定されているかどうか。

そしてドライバー:

SHIFT(1); SHIFT(2); SHIFT(3); SHIFT(4); SHIFT(5)

N はマクロ変数であり、定数になることに注意してください。

これが実際に可変シフトよりも高速になるかどうかはわかりません。もしそうなら、なぜマイクロコードが代わりにこれを実行しないのか不思議に思う...