2011-01-28 15 views
6

मैंने अभी एक आरजीबी को YUV420 कनवर्टर को अनुकूलित करने का प्रयास किया है। एक लुकअप टेबल का उपयोग करके गति गति उत्पन्न हुई, जैसा निश्चित बिंदु अंकगणित का उपयोग किया गया था। हालांकि मैं एसएसई निर्देशों का उपयोग कर वास्तविक लाभ की उम्मीद कर रहा था। मेरे पहले जाने के परिणामस्वरूप धीमे कोड और सभी परिचालनों को चेन करने के बाद, यह लगभग मूल कोड के समान गति है। क्या मेरे कार्यान्वयन में कुछ गड़बड़ है या एसएसई निर्देश सिर्फ हाथ के लिए उपयुक्त नहीं हैं?सिम: सीएसई आरजीबी सीयू + कार्यान्वयन के समान गति के बारे में वाईयूवी रंग रूपांतरण क्यों है?

मूल कोड का एक वर्ग इस प्रकार है:

#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 में मेरा पहला प्रयास तो शायद मैं कुछ याद कर रहा हूँ में से एक है का उपयोग कर संस्करण? FYI करें मैं विजुअल स्टूडियो 2008

उत्तर

8

का उपयोग कर विंडोज प्लेटफॉर्म पर काम कर रहा हूँ समस्याओं के एक जोड़े:

  • आप गलत संरेखित भार का उपयोग कर रहे हैं - इन काफी महंगे हैं (अलग Nehalem उर्फ ​​Core i5/कोर पर से i7) - कम से कम 2x एक गठबंधन भार की लागत - यदि आपके पास भार के बाद बहुत अधिक गणना है तो लागत को अमूर्त किया जा सकता है लेकिन इस मामले में आपके पास अपेक्षाकृत कम है। आप इन 16 बाइट गठबंधन और गठबंधन भार का उपयोग करके bgr1, bgr2, आदि से लोड के लिए इसे ठीक कर सकते हैं। [बेहतर अभी तक, इन इंटरमीडिएट एरे का उपयोग सीधे स्मृति से एसएसई रजिस्टरों तक नहीं करें और सिम के साथ अपने सभी शफलिंग आदि करें - नीचे देखें]

  • आप स्केलर और सिम के बीच आगे और आगे जा रहे हैं कोड - स्केलर कोड शायद प्रमुख भूमिका निभाएगा, जहां तक ​​प्रदर्शन का संबंध है, इसलिए किसी भी सिम लाभ को इसके द्वारा प्रचारित किया जाएगा - आपको वास्तव में सब कुछ सिम निर्देशों का उपयोग करके अपने लूप के अंदर करने की आवश्यकता है (यानी इससे छुटकारा पाएं स्केलर कोड)

+0

हाय पॉल, आपके उत्तर के लिए धन्यवाद। मैंने सभी बार्स को 16 बाइट गठबंधन करने के लिए संशोधित किया है और मैं _mm_loadu_ps के बजाय _mm_load_ps का उपयोग कर रहा हूं। अब तक मुझे कोई उल्लेखनीय अंतर नहीं दिख रहा है। अपने दूसरे सुझाव के संबंध में, कृपया मेरी अज्ञानता से क्षमा करें: मैं स्केलर और सिम कोड के बीच स्विचिंग से कैसे बच सकता हूं? मुझे समझ में नहीं आता कि मैं स्केलर कोड से कैसे छुटकारा पा सकता हूं। – Ralf

+0

@ राल्फ: यह मुश्किल हिस्सा है, यानी आप स्केलर कोड के साथ अन्यथा क्या कर सकते हैं इसके सिम तरीके के बारे में सोच रहे हैं। आदर्श रूप में आप अपने डेटा सीधे लोड करना चाहिए SSE के लिए स्मृति से पंजीकृत करता है, तो आवश्यक व्यवस्था में तत्वों को फिर से संगठित, गणना करने, आवश्यक उत्पादन व्यवस्था में वापस फिर से संगठित करने, तो SSE रजिस्टर से स्मृति पर स्टोर (रों) । आप तो तत्व अगर बहुत आसान (PSHUFB) की उथल SSSE3 (उर्फ SSE3.5) या बेहतर है - SSE3 साथ और पहले यह अभी भी संभव है, लेकिन एक छोटे से जटिल काम के बाद से वहाँ सीमित हैं फेरबदल निर्देश उपलब्ध। –

+0

ठीक है, धन्यवाद पॉल, मुझे SIMD :) – Ralf

1

आप इंसिनट्रिक्स के बजाय इनलाइन असेंबली निर्देशों का उपयोग कर सकते हैं। यह आपके कोड की गति को थोड़ा बढ़ा सकता है। लेकिन इनलाइन असेंबली संकलक विशिष्ट है। वैसे भी, जैसा कि पॉल आर द्वारा उत्तर में बताया गया है, आपको पूर्ण गति प्राप्त करने के लिए गठबंधन डेटा का उपयोग करना होगा। लेकिन डेटा संरेखण और अधिक कंपाइलर विशिष्ट चीज़ है :)

यदि आप कंपाइलर बदल सकते हैं, तो आप विंडोज के लिए इंटेल कंपाइलर आज़मा सकते हैं। मुझे संदेह है कि यह विशेष रूप से इनलाइन असेंबली कोड के लिए बेहतर होगा, लेकिन यह निश्चित रूप से देखने लायक है।

+0

हाय जॉन के बारे में कुछ और अधिक शोध करते हैं, मैं डेटा संरेखित करने की कोशिश की, दुर्भाग्य से कोई लाभ नहीं हुआ। आपके उत्तर के लिए धन्यवाद। – Ralf

+0

एचएम .... क्या आपने केवल संरेखण करने का प्रयास किया था? चूंकि आप _mm_loadu_ps (float *) द्वारा xmm रजिस्टर में अपना डेटा लोड करने का प्रयास करते हैं (यह MOVUPS निर्देशों के लिए मानचित्र करता है), आप प्रोसेसर को असाइन किए गए डेटा को लोड करने के लिए कहते हैं। डेटा को संरेखित करने के लिए पर्याप्त नहीं है, आपको उचित निर्देश का उपयोग करना होगा। आपके मामले के लिए यह _mm_load_ps (फ्लोट *) है (यह MOVAPS निर्देश के लिए मानचित्र है)। यदि यह फ़ंक्शन विफल रहता है, तो इसका मतलब है कि आपके संरेखण में कुछ गड़बड़ है। – JohnGray

+0

आपके उत्तर जॉन के लिए धन्यवाद, केवल इसे देखा ... हाँ, मैंने _mm_load_ps का उपयोग करने के लिए सभी निर्देशों को बदल दिया है, लेकिन ऐसा कोई फर्क नहीं पड़ता। – Ralf

0

मैं अपने दृष्टिकोण के साथ कुछ समस्याएं देखें:

  1. सी ++ सूचक टी से संस्करण लोड करने के लिए "डबल आर, जी, बी", और सभी संभावना में, संकलक इन लदान में करने के लिए अनुकूलित किया गया है एफपी रजिस्ट्रार सीधे, यानी, "डबल आर, जी, बी" रन टाइम पर रजिस्टरों में रहता है। लेकिन आपके संस्करण में, आप "फ्लोट bgr0/1/2/3" में लोड होते हैं और फिर _mm_loadu_ps कहते हैं। मुझे आश्चर्य नहीं होगा अगर "फ्लोट बीजीआर 0/1/2/3" स्मृति में हैं, तो इसका मतलब है कि आपके पास स्मृति को अतिरिक्त पढ़ और लिखना है।

  2. आप इनलाइन असेंबली के बजाय इंट्रिनिक्स का उपयोग कर रहे हैं। कुछ, यदि सभी नहीं, तो उन __m128 चर के स्मृति अभी भी स्मृति में हो सकते हैं। फिर, स्मृति को अतिरिक्त पढ़ता है और लिखता है।

  3. अधिकतर काम शायद RRGB24YUVCI2 _ *() में किया जाता है और आप इन्हें अनुकूलित करने की कोशिश नहीं कर रहे हैं।

आप अपने किसी भी चर को संरेखित नहीं कर रहे हैं, लेकिन यह आपकी अतिरिक्त मेमोरी एक्सेस के लिए केवल अतिरिक्त जुर्माना है, इन्हें पहले खत्म करने का प्रयास करें।

आपका सबसे अच्छा शर्त एक मौजूदा, अनुकूलित आरजीबी/YUV रूपांतरण पुस्तकालय खोजने के लिए और इसका इस्तेमाल होता है।

+0

आपकी प्रतिक्रिया के लिए धन्यवाद। एक प्रश्न: मैं आरआरजीबी 24YUVC12_ * को कैसे अनुकूलित करूं? तुम्हारा मतलब है कि मुझे किसी भी तरह की रेंज जांच अनुकूलित करना चाहिए? मौजूदा ऑप्टिमाइज्ड लाइब्रेरी प्रकार को ढूंढने के उद्देश्य को ढूंढना: रंग रूपांतरण सिर्फ यह देखने के लिए एक परीक्षण था कि वीडियो प्रोसेसिंग एल्गोरिदम पर सिमड कैसे लागू किया जा सकता है। – Ralf

संबंधित मुद्दे