質問

Iは、(64チャネル波ファイルをデインターリーブ)ファイルから8ビットデータの散乱読み出しを行っております。私は、バイトの単一のストリームであることを、それらを組み合わせています。私がいる問題は、書き出すデータの私の再構築である。

基本的に私は、16バイトの読み込み、その後、単一__m128i変数にそれらを構築してから戻ってメモリにタイムアウト値を書き込むこと_mm_stream_psを使用しています。しかし、私はいくつかの奇妙なパフォーマンスの結果を持っています。

私は次のように私の__m128iを設定する固有_mm_set_epi8を使用し、私の最初の方式でます:

    const __m128i packedSamples = _mm_set_epi8( sample15,   sample14,   sample13,   sample12,   sample11,   sample10,   sample9,    sample8,
                                                sample7,    sample6,    sample5,    sample4,    sample3,    sample2,    sample1,    sample0 );

は、基本的に私はそれが最高の性能を発揮するように最適化する方法を決定するために、コンパイラにすべてを任せます。これは、最悪のパフォーマンスを提供します。私のテストは〜0.195秒で実行されます。

第二に、私は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(シフトとORSとの)昔ながらの方法を作った後、単一_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にoccuringしているという考えを検討してきましたが、その後、私は私が何でもまったく同じ結果を出すためにでしょう。それは最初の2つの方法は、16の負荷は、レイテンシを隠蔽するためにループを介して流通得ることができないことを意味していることは可能ですか?もしそうなら、なぜこれは何ですか?確かに固有のコンパイラのように最適化を行うことができますし、どこそれが喜ば..私はそれが全体のポイントだと思った...また確実に16を実行すると、読み込み、16の書き込みは16よりもはるかに遅くなります読み込み、SSEジャグリングの束と1個の書き込み指示...そのすべてのインクルードの読み取りと遅いビットであることを書き込みした後!

が起こっていただきました!任意のアイデアをお持ちの方がはるかに理解されるであろう! :D

編集:私はこれに定数やchangeditとしてバイトをプリロード停止以下のコメントへのさらなるます:

    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実装としてSitll良くない...

アゲイン編集:私は、これまでに取得しています最高のパフォーマンスがある。

    // 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に近づくことができるように表示されません... HOハムます。

役に立ちましたか?

解決

おそらく、コンパイラは一度レジスタに固有のすべての引数を入れしようとしています。あなたはそれらを整理することなく、一度に多くの変数にアクセスする必要はありません。

各サンプルについて個別の識別子を宣言するのではなく、char[16]にそれらを入れてみてください。コンパイラは、限り、あなたは配列内の何のアドレスを取ることはありませんように、それが適当と考えるようレジスタに16個の値を推進していきます。あなたは__aligned__タグを追加する(または任意のVC ++の使用)と、おそらく完全に固有のを回避することができます。それ以外の場合は、( sample[15], sample[14], sample[13] … sample[0] )に固有の呼び出しが容易コンパイラの仕事をしなければならないか、少なくとも害を行いません。

<時間>

編集の私は、レジスタスピルを戦っているかなり確信しているが、その提案はおそらくあなたが望むものではありませんこれは、個別のバイトを格納します。私は私のアドバイスは、それが正しくスタックにノー往復でスケジュールだことを確認するために、読み取り操作であなたの最後の試み(MakeFourCCを使用して)をインターリーブすることだと思います。もちろん、オブジェクトコードの検査はそれを確保するための最良の方法である。

基本的に、あなたはレジスタファイルにデータをストリーミングして、それをバックストリーミングしています。それはデータをフラッシュする時が来た前に、あなたはそれをオーバーロードする必要はありません。

他のヒント

VSは、組み込み関数の最適化で悪名高い悪いです。特にから、およびSSEレジスタにデータを移動させます。組み込み関数自体が...しかし、かなりよく使用されています。

あなたは何を参照してください、この怪物とSSEレジスタを埋めるためにしようとしていることです。

00AA100C  movzx       ecx,byte ptr [esp+0Fh]  
00AA1011  movzx       edx,byte ptr [esp+0Fh]  
00AA1016  movzx       eax,byte ptr [esp+0Fh]  
00AA101B  movd        xmm0,eax  
00AA101F  movzx       eax,byte ptr [esp+0Fh]  
00AA1024  movd        xmm2,edx  
00AA1028  movzx       edx,byte ptr [esp+0Fh]  
00AA102D  movd        xmm1,ecx  
00AA1031  movzx       ecx,byte ptr [esp+0Fh]  
00AA1036  movd        xmm4,ecx  
00AA103A  movzx       ecx,byte ptr [esp+0Fh]  
00AA103F  movd        xmm5,edx  
00AA1043  movzx       edx,byte ptr [esp+0Fh]  
00AA1048  movd        xmm3,eax  
00AA104C  movzx       eax,byte ptr [esp+0Fh]  
00AA1051  movdqa      xmmword ptr [esp+60h],xmm0  
00AA1057  movd        xmm0,edx  
00AA105B  movzx       edx,byte ptr [esp+0Fh]  
00AA1060  movd        xmm6,eax  
00AA1064  movzx       eax,byte ptr [esp+0Fh]  
00AA1069  movd        xmm7,ecx  
00AA106D  movzx       ecx,byte ptr [esp+0Fh]  
00AA1072  movdqa      xmmword ptr [esp+20h],xmm4  
00AA1078  movdqa      xmmword ptr [esp+80h],xmm0  
00AA1081  movd        xmm4,ecx  
00AA1085  movzx       ecx,byte ptr [esp+0Fh]  
00AA108A  movdqa      xmmword ptr [esp+70h],xmm2  
00AA1090  movd        xmm0,eax  
00AA1094  movzx       eax,byte ptr [esp+0Fh]  
00AA1099  movdqa      xmmword ptr [esp+10h],xmm4  
00AA109F  movdqa      xmmword ptr [esp+50h],xmm6  
00AA10A5  movd        xmm2,edx  
00AA10A9  movzx       edx,byte ptr [esp+0Fh]  
00AA10AE  movd        xmm4,eax  
00AA10B2  movzx       eax,byte ptr [esp+0Fh]  
00AA10B7  movd        xmm6,edx  
00AA10BB  punpcklbw   xmm0,xmm1  
00AA10BF  punpcklbw   xmm2,xmm3  
00AA10C3  movdqa      xmm3,xmmword ptr [esp+80h]  
00AA10CC  movdqa      xmmword ptr [esp+40h],xmm4  
00AA10D2  movd        xmm4,ecx  
00AA10D6  movdqa      xmmword ptr [esp+30h],xmm6  
00AA10DC  movdqa      xmm1,xmmword ptr [esp+30h]  
00AA10E2  movd        xmm6,eax  
00AA10E6  punpcklbw   xmm4,xmm5  
00AA10EA  punpcklbw   xmm4,xmm0  
00AA10EE  movdqa      xmm0,xmmword ptr [esp+50h]  
00AA10F4  punpcklbw   xmm1,xmm0  
00AA10F8  movdqa      xmm0,xmmword ptr [esp+70h]  
00AA10FE  punpcklbw   xmm6,xmm7  
00AA1102  punpcklbw   xmm6,xmm2  
00AA1106  movdqa      xmm2,xmmword ptr [esp+10h]  
00AA110C  punpcklbw   xmm2,xmm0  
00AA1110  movdqa      xmm0,xmmword ptr [esp+20h]  
00AA1116  punpcklbw   xmm1,xmm2  
00AA111A  movdqa      xmm2,xmmword ptr [esp+40h]  
00AA1120  punpcklbw   xmm2,xmm0  
00AA1124  movdqa      xmm0,xmmword ptr [esp+60h]  
00AA112A  punpcklbw   xmm3,xmm0  
00AA112E  punpcklbw   xmm2,xmm3  
00AA1132  punpcklbw   xmm6,xmm4  
00AA1136  punpcklbw   xmm1,xmm2  
00AA113A  punpcklbw   xmm6,xmm1  

この非常にうまく機能し、簡単に速くなる(はずです):

__declspec(align(16)) BYTE arr[16] = { sample15, sample14, sample13, sample12, sample11, sample10, sample9, sample8, sample7, sample6, sample5, sample4, sample3, sample2, sample1, sample0 };

__m128i packedSamples = _mm_load_si128( (__m128i*)arr );
<時間>

私自身のテストベッドを構築します:

void    f()
{
    const int steps = 1000000;
    BYTE* pDest = new BYTE[steps*16+16];
    pDest += 16 - ((ULONG_PTR)pDest % 16);
    BYTE* pSrc = new BYTE[steps*16*16];

    const int channelStep0 = 0;
    const int channelStep1 = 1;
    const int channelStep2 = 2;
    const int channelStep3 = 3;
    const int channelStep4 = 16;

    __int64 freq;
    QueryPerformanceFrequency( (LARGE_INTEGER*)&freq );
    __int64 start = 0, end;
    QueryPerformanceCounter( (LARGE_INTEGER*)&start );

    for( int step = 0; step < steps; ++step )
    {
        __declspec(align(16)) BYTE arr[16];
        for( int j = 0; j < 4; ++j )
        {
            //for( int i = 0; i < 4; ++i )
            {
                arr[0+j*4] = *(pSrc + channelStep0);
                arr[1+j*4] = *(pSrc + channelStep1);
                arr[2+j*4] = *(pSrc + channelStep2);
                arr[3+j*4] = *(pSrc + channelStep3);
            }
            pSrc += channelStep4;
        }

#if test1
// test 1 with C
        for( int i = 0; i < 16; ++i )
        {
            *(pDest + step * 16 + i) = arr[i];
        }
#else
// test 2 with SSE load/store    
        __m128i packedSamples = _mm_load_si128( (__m128i*)arr );
        _mm_stream_si128( ((__m128i*)pDest) + step, packedSamples );
#endif
    }

    QueryPerformanceCounter( (LARGE_INTEGER*)&end );

    printf( "%I64d", (end - start) * 1000 / freq );

}

私にとってはテスト2テスト1その後、高速です。

私が何か間違ったことをしますか?これは、使用しているコードではありませんか?私は何を欠場か?これはちょうど私のためですか?

組み込み関数ブレークコンパイラの最適化を使用!

組み込み関数の全体のポイントは、コンパイラが生成した程度と知っているんオペコードの流れの中に、コンパイラが認識していないオペコードを挿入することです。コンパイラは、オペコードについてのいくつかのメタデータを与えられ、それは、レジスタやメモリにどのように影響するかをされていない限り、コンパイラは、任意のデータが固有の実行後に保存されていることを仮定することはできません。これは本当に、コンパイラの最適化一部を痛い - 。それは本来の周りの指示の順序を変更することはできません、それはレジスタがように影響を受けず、仮定することはできません。

私はこれを最適化するための最善の方法は、より大きな絵を見ていると思う - あなたが最終的な出力を書くことに、ソースデータの読み込みからプロセス全体を考慮する必要があります。あなたが開始するために、本当にひどく何かをやっている場合を除き、マイクロ最適化はほとんど、大きな成果が得られていない。

おそらく、ここで詳細であれば、必要な入力と出力の誰かがそれを処理するための最適な方法を提案することができます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top