ファイルから 8 ビット データの分散読み取りを実行しています (64 チャンネルの Wave ファイルのインターリーブ解除)。次に、それらを結合して単一のバイト ストリームにします。私が抱えている問題は、書き出すデータの再構築にあります。
基本的に、私は 16 バイトを読み取り、それらを単一の __m128i 変数に構築し、_mm_stream_ps を使用して値をメモリに書き戻しています。ただし、いくつかの奇妙なパフォーマンス結果があります。
最初のスキームでは、_mm_set_epi8 組み込み関数を使用して __m128i を次のように設定します。
const __m128i packedSamples = _mm_set_epi8( sample15, sample14, sample13, sample12, sample11, sample10, sample9, sample8,
sample7, sample6, sample5, sample4, sample3, sample2, sample1, sample0 );
基本的に、最高のパフォーマンスを得るためにどのように最適化するかはコンパイラに任せています。これにより、最悪のパフォーマンスが得られます。MY テストは ~0.195 秒で実行されます。
2 番目に、4 つの _mm_set_epi32 命令を使用してマージし、それらを圧縮しようとしました。
const __m128i samples0 = _mm_set_epi32( sample3, sample2, sample1, sample0 );
const __m128i samples1 = _mm_set_epi32( sample7, sample6, sample5, sample4 );
const __m128i samples2 = _mm_set_epi32( sample11, sample10, sample9, sample8 );
const __m128i samples3 = _mm_set_epi32( sample15, sample14, sample13, sample12 );
const __m128i packedSamples0 = _mm_packs_epi32( samples0, samples1 );
const __m128i packedSamples1 = _mm_packs_epi32( samples2, samples3 );
const __m128i packedSamples = _mm_packus_epi16( packedSamples0, packedSamples1 );
これにより、パフォーマンスが多少向上します。私のテストは現在、約 0.15 秒で実行されます。とにかくこれがまさに _mm_set_epi8 が行っていることだと思いますので、これを行うことでパフォーマンスが向上するという直感に反するようです...
私の最後の試みは、4 つの CC を昔ながらの方法で (シフトと or を使用して) 作成し、単一の _mm_set_epi32 を使用してそれらを __m128i に配置したコードを使用することでした。
const GCui32 samples0 = MakeFourCC( sample0, sample1, sample2, sample3 );
const GCui32 samples1 = MakeFourCC( sample4, sample5, sample6, sample7 );
const GCui32 samples2 = MakeFourCC( sample8, sample9, sample10, sample11 );
const GCui32 samples3 = MakeFourCC( sample12, sample13, sample14, sample15 );
const __m128i packedSamples = _mm_set_epi32( samples3, samples2, samples1, samples0 );
これにより、さらに優れたパフォーマンスが得られます。テストの実行に約 0.135 秒かかります。私は本当に混乱し始めています。
そこで、単純な読み取りバイト書き込みバイト システムを試してみましたが、最後の方法よりもわずかに高速です。
それで、何が起こっているのですか?これはすべて直感に反するように思えます。
データの供給が速すぎるため、_mm_stream_ps で遅延が発生しているという考えを検討しましたが、何をしてもまったく同じ結果が得られます。最初の 2 つの方法は、16 の負荷がループを介して分散されず、レイテンシが隠蔽されていないことを意味している可能性はありますか? もしそうなら、これはなぜですか?確かに、組み込み関数により、コンパイラーは好きな場所で最適化を行うことができます..それが全体のポイントだと思いました...また、16回の読み取りと16回の書き込みを実行すると、SSEジャグリングの束で16回の読み取りと1回の書き込みよりもはるかに遅くなります。命令...結局のところ、読み取りと書き込みが遅いビットです!
何が起こっているのかアイデアを持っている人なら誰でも大歓迎です!:D
編集:以下のコメントに加えて、バイトを定数としてプリロードするのをやめ、これに変更しました:
const __m128i samples0 = _mm_set_epi32( *(pSamples + channelStep3), *(pSamples + channelStep2), *(pSamples + channelStep1), *(pSamples + channelStep0) );
pSamples += channelStep4;
const __m128i samples1 = _mm_set_epi32( *(pSamples + channelStep3), *(pSamples + channelStep2), *(pSamples + channelStep1), *(pSamples + channelStep0) );
pSamples += channelStep4;
const __m128i samples2 = _mm_set_epi32( *(pSamples + channelStep3), *(pSamples + channelStep2), *(pSamples + channelStep1), *(pSamples + channelStep0) );
pSamples += channelStep4;
const __m128i samples3 = _mm_set_epi32( *(pSamples + channelStep3), *(pSamples + channelStep2), *(pSamples + channelStep1), *(pSamples + channelStep0) );
pSamples += channelStep4;
const __m128i packedSamples0 = _mm_packs_epi32( samples0, samples1 );
const __m128i packedSamples1 = _mm_packs_epi32( samples2, samples3 );
const __m128i packedSamples = _mm_packus_epi16( packedSamples0, packedSamples1 );
これにより、パフォーマンスが最大 0.143 秒に向上しました。そのままの C 実装ほど良くはありません ...
もう一度編集:これまでに得た最高のパフォーマンスは
// Load the samples.
const GCui8 sample0 = *(pSamples + channelStep0);
const GCui8 sample1 = *(pSamples + channelStep1);
const GCui8 sample2 = *(pSamples + channelStep2);
const GCui8 sample3 = *(pSamples + channelStep3);
const GCui32 samples0 = Build32( sample0, sample1, sample2, sample3 );
pSamples += channelStep4;
const GCui8 sample4 = *(pSamples + channelStep0);
const GCui8 sample5 = *(pSamples + channelStep1);
const GCui8 sample6 = *(pSamples + channelStep2);
const GCui8 sample7 = *(pSamples + channelStep3);
const GCui32 samples1 = Build32( sample4, sample5, sample6, sample7 );
pSamples += channelStep4;
// Load the samples.
const GCui8 sample8 = *(pSamples + channelStep0);
const GCui8 sample9 = *(pSamples + channelStep1);
const GCui8 sample10 = *(pSamples + channelStep2);
const GCui8 sample11 = *(pSamples + channelStep3);
const GCui32 samples2 = Build32( sample8, sample9, sample10, sample11 );
pSamples += channelStep4;
const GCui8 sample12 = *(pSamples + channelStep0);
const GCui8 sample13 = *(pSamples + channelStep1);
const GCui8 sample14 = *(pSamples + channelStep2);
const GCui8 sample15 = *(pSamples + channelStep3);
const GCui32 samples3 = Build32( sample12, sample13, sample14, sample15 );
pSamples += channelStep4;
const __m128i packedSamples = _mm_set_epi32( samples3, samples2, samples1, samples0 );
_mm_stream_ps( pWrite + 0, *(__m128*)&packedSamples );
これにより、約 0.095 秒で処理できます。これはかなり優れています。私はSSEに近づくことができないようですが...私はまだそれに混乱していますが..うーん。