2011-08-25 9 views
5

RGBA लिए आरजीबी परिवर्तित करने पर कुछ पिछले प्रश्नों के एक अनुवर्ती में, और ARGB लिए आरजीबी से रूपांतरण, मैं SSE साथ BGRA रूपांतरण के लिए एक आरजीबी तेजी लाने के लिए करना चाहते हैं। 32-बिट मशीन मानें, और इंट्रिनिक्स का उपयोग करना चाहेंगे। मुझे 128-बिट रजिस्टरों के साथ काम करने के लिए स्रोत और गंतव्य बफर दोनों को संरेखित करने में कठिनाई हो रही है, और अन्य समझदार वेक्टरेशन समाधानों की तलाश है।फास्ट vectorized बीजीआर को BGRA

दिनचर्या vectorized जा करने के लिए इस प्रकार है, यह प्रक्रिया के लिए फायदेमंद हो सकता है ...

void RGB8ToBGRX8(int w, const void *in, void *out) 
    { 
     int i; 
     int width = w; 
     const unsigned char *src= (const unsigned char*) in; 
     unsigned int *dst= (unsigned int*) out; 
     unsigned int invalue, outvalue; 

     for (i=0; i<width; i++, src+=3, dst++) 
     { 
       invalue = src[0]; 
       outvalue = (invalue<<16); 
       invalue = src[1]; 
       outvalue |= (invalue<<8); 
       invalue = src[2]; 
       outvalue |= (invalue); 
       *dst = outvalue | 0xff000000; 
     } 
     } 

यह दिनचर्या, मुख्य रूप से बड़े बनावट (512KB) के लिए इस्तेमाल किया जाता है, इसलिए यदि मैं आपरेशन के कुछ parallelize कर सकते हैं एक बार में अधिक पिक्सल। बेशक, मुझे प्रोफाइल करने की आवश्यकता होगी। :)

संपादित करें:

मेरे संकलन तर्क ...

gcc -O2 main.c 
+1

क्या आप अपने कंपाइलर के लिए अनुकूलन ध्वज का उपयोग कर रहे हैं (कौन सा?)? कंपाइलर अक्सर कोड को अनुकूलित करने का बेहतर काम करेगा, _without_ गलतता पेश करना। आपने कौन सा बेंचमार्क डेटा एकत्र किया है? –

+0

कोई एसएसई उत्तर नहीं है, लेकिन क्या आपने 4 बार अपने लूप को अनलॉक करने का प्रयास किया है जैसे कि इनपुट हमेशा एक गठबंधन पते पर शुरू होता है? फिर आप इनपुट पिक्सेल की प्रत्येक सापेक्ष स्थिति के लिए विशिष्ट स्थानांतरण और मास्किंग के साथ, एक समय में एक मशीन शब्द इनपुट को पढ़ सकते हैं। जैसा कि दाना का उल्लेख है, यह देखने लायक है कि संकलक उच्च अनुकूलन स्तर पर कितना अच्छा प्रदर्शन करता है (बेंचमार्किंग के अलावा जेनरेट किए गए असेंबलर कोड का निरीक्षण करता है), लेकिन मुझे संदेह है कि लूप को अनलॉक करने के लिए पर्याप्त आक्रामक होगा _and_ प्रविष्टि बिंदु को विभाजित करें सभी में 'इन' के संरेखण। –

+0

महान प्रश्न। यह जीसीसी 4.6 के साथ बस "ओ 2" (ओ ओ 3 नहीं) है। मेरा बेंचमार्क केस 1012 पुनरावृत्ति है जो 512 के साथ "चौड़ाई" अवधि के रूप में चलता है। महान उत्तरों के लिए धन्यवाद! – Rev316

उत्तर

8

यह अनुरोधित ऑपरेशन करने के लिए एसएसई 3 इंट्रिनिक्स का उपयोग करने का एक उदाहरण है। इनपुट और आउटपुट पॉइंटर्स 16-बाइट गठबंधन होना चाहिए, और यह एक समय में 16 पिक्सेल के ब्लॉक पर चल रहा है।

मुझे नहीं लगता कि आपको एक महत्वपूर्ण गति वृद्धि मिलेगी, हालांकि। पिक्सेल पर किए गए ऑपरेशन इतने सरल हैं कि मेमोरी बैंडविड्थ पर हावी है।

#include <tmmintrin.h> 

/* in and out must be 16-byte aligned */ 
void rgb_to_bgrx_sse(unsigned w, const void *in, void *out) 
{ 
    const __m128i *in_vec = in; 
    __m128i *out_vec = out; 

    w /= 16; 

    while (w-- > 0) { 
     /*    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
     * in_vec[0] Ra Ga Ba Rb Gb Bb Rc Gc Bc Rd Gd Bd Re Ge Be Rf 
     * in_vec[1] Gf Bf Rg Gg Bg Rh Gh Bh Ri Gi Bi Rj Gj Bj Rk Gk 
     * in_vec[2] Bk Rl Gl Bl Rm Gm Bm Rn Gn Bn Ro Go Bo Rp Gp Bp 
     */ 
     __m128i in1, in2, in3; 
     __m128i out; 

     in1 = in_vec[0]; 

     out = _mm_shuffle_epi8(in1, 
      _mm_set_epi8(0xff, 9, 10, 11, 0xff, 6, 7, 8, 0xff, 3, 4, 5, 0xff, 0, 1, 2)); 
     out = _mm_or_si128(out, 
      _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); 
     out_vec[0] = out; 

     in2 = in_vec[1]; 

     in1 = _mm_and_si128(in1, 
      _mm_set_epi8(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0)); 
     out = _mm_and_si128(in2, 
      _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); 
     out = _mm_or_si128(out, in1); 
     out = _mm_shuffle_epi8(out, 
      _mm_set_epi8(0xff, 5, 6, 7, 0xff, 2, 3, 4, 0xff, 15, 0, 1, 0xff, 12, 13, 14)); 
     out = _mm_or_si128(out, 
      _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); 
     out_vec[1] = out; 

     in3 = in_vec[2]; 
     in_vec += 3; 

     in2 = _mm_and_si128(in2, 
      _mm_set_epi8(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0)); 
     out = _mm_and_si128(in3, 
      _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); 
     out = _mm_or_si128(out, in2); 
     out = _mm_shuffle_epi8(out, 
      _mm_set_epi8(0xff, 1, 2, 3, 0xff, 14, 15, 0, 0xff, 11, 12, 13, 0xff, 8, 9, 10)); 
     out = _mm_or_si128(out, 
      _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); 
     out_vec[2] = out; 

     out = _mm_shuffle_epi8(in3, 
      _mm_set_epi8(0xff, 13, 14, 15, 0xff, 10, 11, 12, 0xff, 7, 8, 9, 0xff, 4, 5, 6)); 
     out = _mm_or_si128(out, 
      _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); 
     out_vec[3] = out; 

     out_vec += 4; 
    } 
} 
2

मैं तुम्हारे लिए क्या पूछ रहे हैं की एक पूरी समझ नहीं है, और मैं बेसब्री से एक उचित प्रतिक्रिया की प्रतीक्षा कर रहा हूँ आपके प्रश्न के लिए इस बीच, मैं कार्यान्वयन के साथ आया हूं जो औसतन 8 से 10% तेज है। मैं वीएस -2010 का उपयोग कर विन 7 64 बिट चला रहा हूं, तेजी से विकल्प के साथ रिलीज के लिए सी ++ के साथ संकलित।

#pragma pack(push, 1) 
    struct RGB { 
     unsigned char r, g, b; 
    }; 

    struct BGRA { 
     unsigned char b, g, r, a; 
    }; 
#pragma pack(pop) 

    void RGB8ToBGRX8(int width, const void* in, void* out) 
    { 
     const RGB* src = (const RGB*)in; 
     BGRA* dst = (BGRA*)out; 
     do {   
      dst->r = src->r; 
      dst->g = src->g; 
      dst->b = src->b; 
      dst->a = 0xFF; 
      src++; 
      dst++; 
     } while (--width); 
    } 

यह मदद कर सकता है या नहीं भी, लेकिन मुझे उम्मीद है कि यह करता है। यदि यह नहीं करता है तो कृपया मुझे मत छोड़ो, मैं बस इसे साथ ले जाने की कोशिश कर रहा हूं।

structs का उपयोग करने के लिए मेरी प्रेरणा संकलक को संभावित रूप से पॉइंटर्स src और dst के रूप में सक्षम करने की अनुमति देना है। एक अन्य प्रेरणा अंकगणितीय परिचालनों की संख्या को सीमित करना है।

+0

कोई चिंता जैक नहीं! यदि आप स्पष्ट कर सकते हैं कि आप किस टुकड़े को समझ नहीं सकते हैं, तो मैं कोशिश कर सकता हूं और विस्तृत कर सकता हूं। :) – Rev316

+0

एसएसई का उपयोग करने के बारे में आपका क्या मतलब है? मुझे लगता है कि इसका मतलब है कि विशिष्ट अनुकूलन तकनीक (ओं) का उपयोग करने के लिए कंपाइलर को निर्देश देना, और यदि ऐसा है तो शायद कोड को हाथ से ट्विक करने के लायक नहीं है। आप यह भी कहते हैं कि आप इंट्रिनिक्स का उपयोग करना चाहते हैं, आपका क्या मतलब है? हालांकि, मुझे समांतरता की अच्छी समझ है। – Jack

+0

ओह। मैं एसएसई 2/3, या एसएसएसईई का उपयोग करने के वेक्टरेशन इंट्रिस्टिक्स का जिक्र कर रहा था। अधिकतर पैडिंग/मास्किंग ऑप्स, जैसा कि मैंने अन्य छवि रूपांतरणों के साथ सुरुचिपूर्ण समाधान देखा है। अब, मुझे पता है कि जीसीसी 4.एक्स में कई संकलन झंडे हैं जो यहां मदद करते हैं, लेकिन मुझे अनिश्चितता है और/या यदि यह बेहतर है। शायद आपकी विशेषज्ञता यहां सहायक होगी। – Rev316

2

मैं व्यक्तिगत रूप से पाया गया कि निम्नलिखित को लागू करने के लिए मुझे बीजीआर -24 परिवर्तित ARGB-32 के लिए के लिए सबसे अच्छा परिणाम दे दी है।

यह कोड छवि पर लगभग 8.8ms पर चलता है जबकि ऊपर प्रस्तुत 128-बिट वेक्टरेशन कोड प्रति छवि 14.5ms पर आया था।

void PixelFix(u_int32_t *buff,unsigned char *diskmem) 
{ 
    int i,j; 
    int picptr, srcptr; 
    int w = 1920; 
    int h = 1080; 

    for (j=0; j<h; j++) { 
     for (i=0; i<w; i++) { 
      buff[picptr++]=(diskmem[srcptr]<<24) | (diskmem[srcptr+1]<<16) | diskmem[srcptr+2]<<8 | 0xff; 
      srcptr+=3; 
     } 
    } 
} 

पहले, मैं इस दिनचर्या (प्रति छवि लगभग 13.2ms) का उपयोग कर रहा था। यहां, बफ एक हस्ताक्षरित चार * है।

for (j=0; j<h; j++) { 
    int srcptr = (h-j-1)*w*3; // remove if you don't want vertical flipping 
    for (i=0; i<w; i++) { 
     buff[picptr+3]=diskmem[srcptr++]; // b 
     buff[picptr+2]=diskmem[srcptr++]; // g 
     buff[picptr+1]=diskmem[srcptr++]; // r 
     buff[picptr+0]=255;    // a 
     picptr+=4; 
    } 
} 

2012 मैकमिनी 2.6ghz/i7 चल रहा है।

+0

इसके अलावा, कोई ऐप्पल के हालिया vImage रूपांतरण API में देखना चाह सकता है ..., विशेष रूप से दिनचर्या जैसे "vImageConvert_RGB888toARGB8888" 24-बिट आरजीबी से 32-बिट एआरजीबी (या बीजीआरए) में कनवर्ट करने के लिए। https://developer.apple.com/library/mac/documentation/Performance/Reference/vImage_conversion/Reference/reference.html#//apple_ref/c/func/vImageConvert_RGB888toARGB8888 – zzyzy

2

उम्म ... vImageConvert_RGB888toARGB8888 का उपयोग करना बहुत तेज़ (15 एक्स स्पीडअप) है।

ऊपर PixelFix कोड (छवि प्रति ≈6ms नए हार्डवेयर पर अब,)


  1. 6,373520 एमएस
  2. 6,383363 एमएस
  3. 6,413560 एमएस
  4. 6,278606 एमएस
  5. 6,293607 एमएस
  6. 6.368118 एमएस
  7. ६.३,३८,९०४ एमएस
  8. 6,389385 एमएस
  9. 6,365495 एमएस

vImageConvert_RGB888toARGB888 का उपयोग करना, पिरोया (नए हार्डवेयर पर)


  1. 0,563649 एमएस
  2. 0,400387 एमएस
  3. 0,375198 एमएस
  4. ०.३६०८९८ एमएस
  5. 0,391278 एमएस
  6. 0,396797 एमएस
  7. 0,405534 एमएस
  8. 0,386495 एमएस
  9. 0,367621 एमएस

मैं और अधिक कहने के की आवश्यकता है?

+1

एक अनुवर्ती ... एकल-थ्रेडेड का उपयोग करके 128-बिट वेक्टर कोड "rgb_to_bgrx_sse" उपरोक्त आकार के I/O बफर के लिए 11ms रेंज में परिणाम देता है। vImage यहां स्पष्ट विजेता है। – zzyzy

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