6

RGBからYUV420へのコンバーターを最適化しようとしました。ルックアップテーブルを使用すると、固定小数点演算を使用した場合と同様に、速度が向上しました。しかし、私はSSE命令を使用して実際の利益を期待していました。私が最初に試した結果、コードが遅くなり、すべての操作を連鎖させた後は、元のコードとほぼ同じ速度になりました。私の実装に何か問題がありますか、それともSSE命令が目前のタスクに適していないのですか?

元のコードのセクションは次のとおりです。

#define RRGB24YUVCI2_00   0.299
#define RRGB24YUVCI2_01   0.587
#define RRGB24YUVCI2_02   0.114
#define RRGB24YUVCI2_10  -0.147
#define RRGB24YUVCI2_11  -0.289
#define RRGB24YUVCI2_12   0.436
#define RRGB24YUVCI2_20   0.615
#define RRGB24YUVCI2_21  -0.515
#define RRGB24YUVCI2_22  -0.100

void RealRGB24toYUV420Converter::Convert(void* pRgb, void* pY, void* pU, void* pV)
{
  yuvType* py = (yuvType *)pY;
  yuvType* pu = (yuvType *)pU;
  yuvType* pv = (yuvType *)pV;
  unsigned char* src = (unsigned char *)pRgb;

  /// Y have range 0..255, U & V have range -128..127.
  double u,v;
  double r,g,b;

  /// Step in 2x2 pel blocks. (4 pels per block).
  int xBlks = _width >> 1;
  int yBlks = _height >> 1;
  for(int yb = 0; yb < yBlks; yb++)
  for(int xb = 0; xb < xBlks; xb++)
  {
    int chrOff = yb*xBlks + xb;
    int lumOff = (yb*_width + xb) << 1;
    unsigned char* t    = src + lumOff*3;

    /// Top left pel.
    b = (double)(*t++);
    g = (double)(*t++);
    r = (double)(*t++);
    py[lumOff] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((int)(0.5 + RRGB24YUVCI2_00*r + RRGB24YUVCI2_01*g + RRGB24YUVCI2_02*b));

    u = RRGB24YUVCI2_10*r + RRGB24YUVCI2_11*g + RRGB24YUVCI2_12*b;
    v = RRGB24YUVCI2_20*r + RRGB24YUVCI2_21*g + RRGB24YUVCI2_22*b;

    /// Top right pel.
    b = (double)(*t++);
    g = (double)(*t++);
    r = (double)(*t++);
    py[lumOff+1] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((int)(0.5 + RRGB24YUVCI2_00*r + RRGB24YUVCI2_01*g + RRGB24YUVCI2_02*b));

    u += RRGB24YUVCI2_10*r + RRGB24YUVCI2_11*g + RRGB24YUVCI2_12*b;
    v += RRGB24YUVCI2_20*r + RRGB24YUVCI2_21*g + RRGB24YUVCI2_22*b;

    lumOff += _width;
    t = t + _width*3 - 6;
    /// Bottom left pel.
    b = (double)(*t++);
    g = (double)(*t++);
    r = (double)(*t++);
    py[lumOff] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((int)(0.5 + RRGB24YUVCI2_00*r + RRGB24YUVCI2_01*g + RRGB24YUVCI2_02*b));

    u += RRGB24YUVCI2_10*r + RRGB24YUVCI2_11*g + RRGB24YUVCI2_12*b;
    v += RRGB24YUVCI2_20*r + RRGB24YUVCI2_21*g + RRGB24YUVCI2_22*b;

    /// Bottom right pel.
    b = (double)(*t++);
    g = (double)(*t++);
    r = (double)(*t++);
    py[lumOff+1] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((int)(0.5 + RRGB24YUVCI2_00*r + RRGB24YUVCI2_01*g + RRGB24YUVCI2_02*b));

    u += RRGB24YUVCI2_10*r + RRGB24YUVCI2_11*g + RRGB24YUVCI2_12*b;
    v += RRGB24YUVCI2_20*r + RRGB24YUVCI2_21*g + RRGB24YUVCI2_22*b;

    /// Average the 4 chr values.
    int iu = (int)u;
    int iv = (int)v;
    if(iu < 0) ///< Rounding.
      iu -= 2;
    else
      iu += 2;
    if(iv < 0) ///< Rounding.
      iv -= 2;
    else
      iv += 2;

    pu[chrOff] = (yuvType)( _chrOff + RRGB24YUVCI2_RANGECHECK_N128TO127(iu/4) );
    pv[chrOff] = (yuvType)( _chrOff + RRGB24YUVCI2_RANGECHECK_N128TO127(iv/4) );
  }//end for xb & yb...
}//end Convert.

そしてこれがSSEを使ったバージョンです

const float fRRGB24YUVCI2_00 = 0.299;
const float fRRGB24YUVCI2_01 = 0.587;
const float fRRGB24YUVCI2_02 = 0.114;
const float fRRGB24YUVCI2_10 = -0.147;
const float fRRGB24YUVCI2_11 = -0.289;
const float fRRGB24YUVCI2_12 = 0.436;
const float fRRGB24YUVCI2_20 = 0.615;
const float fRRGB24YUVCI2_21 = -0.515;
const float fRRGB24YUVCI2_22 = -0.100;

void RealRGB24toYUV420Converter::Convert(void* pRgb, void* pY, void* pU, void* pV)
{
   __m128 xmm_y = _mm_loadu_ps(fCOEFF_0);
   __m128 xmm_u = _mm_loadu_ps(fCOEFF_1);
   __m128 xmm_v = _mm_loadu_ps(fCOEFF_2);

   yuvType* py = (yuvType *)pY;
   yuvType* pu = (yuvType *)pU;
   yuvType* pv = (yuvType *)pV;
   unsigned char* src = (unsigned char *)pRgb;

   /// Y have range 0..255, U & V have range -128..127.
   float bgr1[4];
   bgr1[3] = 0.0;
   float bgr2[4];
   bgr2[3] = 0.0;
   float bgr3[4];
   bgr3[3] = 0.0;
   float bgr4[4];
   bgr4[3] = 0.0;

   /// Step in 2x2 pel blocks. (4 pels per block).
   int xBlks = _width >> 1;
   int yBlks = _height >> 1;
   for(int yb = 0; yb < yBlks; yb++)
     for(int xb = 0; xb < xBlks; xb++)
     {
       int       chrOff = yb*xBlks + xb;
       int       lumOff = (yb*_width + xb) << 1;
       unsigned char* t    = src + lumOff*3;

       bgr1[2] = (float)*t++;
       bgr1[1] = (float)*t++;
       bgr1[0] = (float)*t++;
       bgr2[2] = (float)*t++;
       bgr2[1] = (float)*t++;
       bgr2[0] = (float)*t++;
       t = t + _width*3 - 6;
       bgr3[2] = (float)*t++;
       bgr3[1] = (float)*t++;
       bgr3[0] = (float)*t++;
       bgr4[2] = (float)*t++;
       bgr4[1] = (float)*t++;
       bgr4[0] = (float)*t++;
       __m128 xmm1 = _mm_loadu_ps(bgr1);
       __m128 xmm2 = _mm_loadu_ps(bgr2);
       __m128 xmm3 = _mm_loadu_ps(bgr3);
       __m128 xmm4 = _mm_loadu_ps(bgr4);

       // Y
       __m128 xmm_res_y = _mm_mul_ps(xmm1, xmm_y);
       py[lumOff] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((xmm_res_y.m128_f32[0] + xmm_res_y.m128_f32[1] + xmm_res_y.m128_f32[2] ));
       // Y
       xmm_res_y = _mm_mul_ps(xmm2, xmm_y);
       py[lumOff + 1] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((xmm_res_y.m128_f32[0]    + xmm_res_y.m128_f32[1] + xmm_res_y.m128_f32[2] ));
       lumOff += _width;
       // Y
       xmm_res_y = _mm_mul_ps(xmm3, xmm_y);
       py[lumOff] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((xmm_res_y.m128_f32[0] + xmm_res_y.m128_f32[1] + xmm_res_y.m128_f32[2] ));
       // Y
       xmm_res_y = _mm_mul_ps(xmm4, xmm_y);
       py[lumOff+1] = (yuvType)RRGB24YUVCI2_RANGECHECK_0TO255((xmm_res_y.m128_f32[0] + xmm_res_y.m128_f32[1] + xmm_res_y.m128_f32[2] ));

       // U
       __m128 xmm_res = _mm_add_ps(
                          _mm_add_ps(_mm_mul_ps(xmm1, xmm_u), _mm_mul_ps(xmm2, xmm_u)),
                          _mm_add_ps(_mm_mul_ps(xmm3, xmm_u), _mm_mul_ps(xmm4, xmm_u))
                       );

       float fU  = xmm_res.m128_f32[0] + xmm_res.m128_f32[1] + xmm_res.m128_f32[2];

       // V
       xmm_res = _mm_add_ps(
      _mm_add_ps(_mm_mul_ps(xmm1, xmm_v), _mm_mul_ps(xmm2, xmm_v)),
      _mm_add_ps(_mm_mul_ps(xmm3, xmm_v), _mm_mul_ps(xmm4, xmm_v))
      );
       float fV  = xmm_res.m128_f32[0] + xmm_res.m128_f32[1] + xmm_res.m128_f32[2];

       /// Average the 4 chr values.
       int iu = (int)fU;
       int iv = (int)fV;
       if(iu < 0) ///< Rounding.
         iu -= 2;
       else
         iu += 2;
       if(iv < 0) ///< Rounding.
         iv -= 2;
       else
         iv += 2;

       pu[chrOff] = (yuvType)( _chrOff + RRGB24YUVCI2_RANGECHECK_N128TO127(iu >> 2) );
       pv[chrOff] = (yuvType)( _chrOff + RRGB24YUVCI2_RANGECHECK_N128TO127(iv >> 2) );
     }//end for xb & yb...
}

これはSSE2での最初の試みの1つなので、何かが足りないのではないでしょうか。参考までに、私はVisualStudio2008を使用してWindowsプラットフォームで作業しています。

4

3 に答える 3

9

いくつかの問題:

  • ミスアラインされたロードを使用しています-これらは非常に高価です(Nehalem別名Core i5 / Core i7を除く)-アラインされたロードの少なくとも2倍のコスト-ロード後に十分な計算がある場合、コストは償却できますが、この場合、あなたは比較的少ないです。これらの16バイトを整列させ、整列されたロードを使用することにより、bgr1、bgr2などからのロードに対してこれを修正できます。[さらに良いことに、これらの中間配列はまったく使用しないでください。データをメモリからSSEレジスタに直接ロードし、SIMDを使用してすべてのシャッフルなどを実行してください。以下を参照してください]

  • あなたはスカラーとSIMDコードの間を行ったり来たりしています-パフォーマンスに関してはスカラーコードがおそらく支配的な部分になるでしょう、それでSIMDゲインはこれによって圧倒される傾向があります-あなたは本当にあなたのループ内ですべてをする必要がありますSIMD命令を使用する(つまり、スカラーコードを取り除く)

于 2011-01-28T14:19:03.037 に答える
1

insintricsの代わりにインラインアセンブリ命令を使用できます。コードの速度が少し上がる可能性があります。ただし、インラインアセンブリはコンパイラ固有です。とにかく、Paul Rの回答で述べられているように、フルスピードを達成するには、整列されたデータを使用する必要があります。しかし、データアライメントはさらにコンパイラ固有のものです:)

コンパイラを変更できる場合は、Windows用のIntelコンパイラを試してみてください。特にインラインアセンブリコードの場合は、はるかに優れているとは思えませんが、一見の価値があります。

于 2011-01-28T14:45:39.490 に答える
0

あなたのアプローチにはいくつか問題があります。

  1. C ++バージョンはポインタtから「doubler、g、b」にロードされ、おそらくコンパイラはこれらを最適化してFPレジスタに直接ロードします。つまり、「double r、g、b」は実行時にレジスタに存在します。時間。ただし、ご使用のバージョンでは、「float bgr0 / 1/2/3」にロードしてから、_mm_loadu_psを呼び出します。「floatbgr0/1/2/3」がメモリ内にある場合でも驚かないでしょう。これは、メモリへの追加の読み取りと書き込みがあることを意味します。

  2. インラインアセンブリの代わりに組み込み関数を使用しています。すべてではないにしても、これらの__m128変数の一部はまだメモリ内にある可能性があります。この場合も、メモリへの追加の読み取りと書き込みが行われます。

  3. ほとんどの作業はおそらくRRGB24YUVCI2_*()で行われ、これらを最適化しようとはしていません。

変数を調整していませんが、それは余分なメモリアクセスに対する追加のペナルティにすぎません。最初に、これらを削除してみてください。

最善の策は、既存の最適化されたRGB/YUV変換ライブラリを見つけて使用することです。

于 2011-04-22T20:34:12.437 に答える