2010-06-28 7 views
7

मैं सिमड/एसएसई के लिए बहुत नया हूं और मैं कुछ साधारण छवि फ़िल्टरिंग (धुंधला) करने की कोशिश कर रहा हूं। नीचे दिया गया कोड क्षैतिज दिशा में एक साधारण [1 2 1] वज़न के साथ 8-बिट ग्रे बिटमैप के प्रत्येक पिक्सेल को फ़िल्टर करता है। मैं एक समय में 16 पिक्सेल की रकम बना रहा हूं।सिमड/एसएसई नौसिखिया: सरल छवि फ़िल्टरिंग

कम से कम मेरे लिए इस कोड के बारे में बहुत बुरा लगता है, यह है कि इसमें बहुत सारे सम्मिलित/निकालें हैं, जो बहुत ही सुरुचिपूर्ण नहीं है और शायद सबकुछ भी धीमा कर देता है। स्थानांतरण करते समय एक reg से डेटा को दूसरे में लपेटने का कोई बेहतर तरीका है?

buf छवि डेटा है, 16-बाइट गठबंधन है। w/ज चौड़ाई और ऊंचाई कर रहे हैं, 16.

__m128i *p = (__m128i *) buf; 
__m128i cur1, cur2, sum1, sum2, zeros, tmp1, tmp2, saved; 
zeros = _mm_setzero_si128(); 
short shifted, last = 0, next; 

// preload first row 
cur1 = _mm_load_si128(p); 
for (x = 1; x < (w * h)/16; x++) { 
    // unpack 
    sum1 = sum2 = saved = cur1; 
    sum1 = _mm_unpacklo_epi8(sum1, zeros); 
    sum2 = _mm_unpackhi_epi8(sum2, zeros); 
    cur1 = tmp1 = sum1; 
    cur2 = tmp2 = sum2; 
    // "middle" pixel 
    sum1 = _mm_add_epi16(sum1, sum1); 
    sum2 = _mm_add_epi16(sum2, sum2); 
    // left pixel 
    cur2 = _mm_slli_si128(cur2, 2); 
    shifted = _mm_extract_epi16(cur1, 7); 
    cur2 = _mm_insert_epi16(cur2, shifted, 0); 
    cur1 = _mm_slli_si128(cur1, 2); 
    cur1 = _mm_insert_epi16(cur1, last, 0); 
    sum1 = _mm_add_epi16(sum1, cur1); 
    sum2 = _mm_add_epi16(sum2, cur2); 
    // right pixel 
    tmp1 = _mm_srli_si128(tmp1, 2); 
    shifted = _mm_extract_epi16(tmp2, 0); 
    tmp1 = _mm_insert_epi16(tmp1, shifted, 7); 
    tmp2 = _mm_srli_si128(tmp2, 2); 
    // preload next row 
    cur1 = _mm_load_si128(p + x); 
    // we need the first pixel of the next row for the "right" pixel 
    next = _mm_extract_epi16(cur1, 0) & 0xff; 
    tmp2 = _mm_insert_epi16(tmp2, next, 7); 
    // and the last pixel of last row for the next "left" pixel 
    last = ((uint16_t) _mm_extract_epi16(saved, 7)) >> 8; 
    sum1 = _mm_add_epi16(sum1, tmp1); 
    sum2 = _mm_add_epi16(sum2, tmp2); 
    // divide 
    sum1 = _mm_srli_epi16(sum1, 2); 
    sum2 = _mm_srli_epi16(sum2, 2); 
    sum1 = _mm_packus_epi16(sum1, sum2); 
    mm_store_si128(p + x - 1, sum1); 
} 
+0

मैं भी कोड के लिए संभव सुधार के बारे में सामान्य टिप्पणियों की सराहना होगी। धन्यवाद! – dietr

उत्तर

2

मैं एसएसई रजिस्टर पर पड़ोसी पिक्सल रखने का सुझाव देता हूं। यही है, एक एसएसई वैरिएबल में _mm_slli_si128/_mm_srli_si128 का परिणाम रखें, और सभी डालने और निकालने को खत्म करें। मेरा तर्क यह है कि पुराने सीपीयू में, सम्मिलित/निकालने के निर्देशों को एसएसई इकाइयों और सामान्य प्रयोजन इकाइयों के बीच संचार की आवश्यकता होती है, जो एसएसई के भीतर गणना को रखने से बहुत धीमी होती है, भले ही यह एल 1 कैश तक फैलती है।

जब यह किया जाता है, तो केवल चार 16-बिट बदलाव (_mm_slli_si128, _mm_srli_si128, विभाजित शिफ्ट) की गणना नहीं करनी चाहिए। मेरा सुझाव है कि आप अपने कोड के साथ बेंचमार्क करें, क्योंकि उस समय तक आपका कोड मेमोरी बैंडविड्थ सीमा को पहले ही मार सकता है .. जिसका मतलब है कि आप अब अनुकूलित नहीं कर सकते हैं।

यदि छवि बड़ी है (एल 2 आकार से बड़ा) और आउटपुट जल्द ही वापस नहीं पढ़ा जाएगा, तो वापस लिखने के लिए MOVNTDQ (_mm_stream_si128) का उपयोग करने का प्रयास करें। कई वेबसाइटों के अनुसार यह एसएसई 2 में है, हालांकि आप दोबारा जांचना चाहेंगे।

SIMD ट्यूटोरियल:

कुछ SIMD गुरु वेबसाइटों:

2

के गुणकों पड़ोस आपरेशन इस तरह की हमेशा SSE के साथ एक दर्द था, जब तक SSE3.5 (उर्फ SSSE3) साथ आया था, और PALIGNR (_mm_alignr_epi8) शुरू की गई थी ।

आप हालांकि SSE2/SSE3 साथ पश्चगामी संगतता की जरूरत है, तो आप एक बराबर मैक्रो या इनलाइन समारोह जो _mm_alignr_epi8 SSE2/SSE3 के लिए emulates और जो जब SSE3.5/SSE4 लक्ष्यीकरण _mm_alignr_epi8 के माध्यम से चला जाता है लिख सकते हैं।

एक और तरीका स्थानांतरित डेटा प्राप्त करने के लिए गलत तरीके से लोड का उपयोग करना है - यह पुराने CPUs (लगभग दो बार विलंबता और संरेखित भार के आधा थ्रुपुट) पर महंगा है, लेकिन यह बहुत अधिक गणना के आधार पर स्वीकार्य हो सकता है प्रति लोड कर रहे हैं। यह भी लाभ है कि वर्तमान इंटेल सीपीयू (कोर i7) पर गलत हस्ताक्षर किए गए भारों के साथ गठबंधन भार की तुलना में कोई दंड नहीं है, इसलिए आपका कोड कोर i7 एट अल पर काफी कुशल होगा।

+0

मैंने पहले से ही alignr देखा, लेकिन संदिग्ध के रूप में, मैं एसएसई 2 के साथ संगत होना चाहता हूँ। मुझे लगता है कि एसएसई 2 एक अच्छा "सबसे कम आम denominator" है जब यह x86 पर सिमड की बात आती है, और यदि अकेले एसएसई 2 मुझे एक संतोषजनक गति प्रदान करेगा, तो मैं कुछ भी उन्नत लागू करने से परेशान नहीं होगा। – dietr

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