2016-02-09 8 views
8

मैं छवि प्रसंस्करण के साथ सौदा करता हूं। मैं, द्वारा 255एसएसई का उपयोग कर 255 तक 16-बिट पूर्णांक को कैसे विभाजित करें?

मैं _mm_srli_epi16() की तरह पारी ऑपरेटर का उपयोग नहीं कर सकते हैं 16-बिट पूर्णांक SSE वेक्टर विभाजित करने की आवश्यकता है क्योंकि 255 2.

की शक्ति की एक बहु मैं निश्चित रूप से पता नहीं है कि यह पूर्णांक को पूर्ण करने के लिए पूर्णांक परिवर्तित करना, विभाजन करना और फिर पूर्णांक में रूपांतरण करना संभव है।

लेकिन हो सकता है किसी को एक और समाधान जानता है ...

+1

क्या [यह] (http://stackoverflow.com/q/16822757/3959454) मदद करता है? –

+1

आमतौर पर आप 256 (विभाजन के बजाए गोलाकार के साथ) विभाजित करेंगे - क्या इसका कोई कारण है कि इसे 255 होना चाहिए और 256 क्यों नहीं? –

+1

शायद [यह] (http://stackoverflow.com/questions/31575833/fastest-method-of-vectorized-integer-division-by-non-constant-divisor) प्रश्न भी आपके लिए दिलचस्प है। जब आपको भविष्य में गैर-निरंतर पूर्णांक विभाजन से निपटना होगा, तो फ्लोट में रूपांतरण भी एक तेज़ विकल्प है। – Youka

उत्तर

10

एक पूर्णांक 255 से विभाजन के सन्निकटन है:

inline __m128i DivideI16By255(__m128i value) 
{ 
    return _mm_srli_epi16(_mm_add_epi16(
     _mm_add_epi16(value, _mm_set1_epi16(1)), _mm_srli_epi16(value, 8)), 8); 
} 
: यह कैसा दिखाई देगा

inline int DivideBy255(int value) 
{ 
    return (value + 1 + (value >> 8)) >> 8; 
} 

तो SSE2 का उपयोग करने के साथ

AVX2 के लिए:

inline __m256i DivideI16By255(__m256i value) 
{ 
    return _mm256_srli_epi16(_mm256_add_epi16(
     _mm256_add_epi16(value, _mm256_set1_epi16(1)), _mm256_srli_epi16(value, 8)), 8); 
} 

Altivec के लिए (पावर):

typedef __vector int16_t v128_s16; 
const v128_s16 K16_0001 = {1, 1, 1, 1, 1, 1, 1, 1}; 
const v128_s16 K16_0008 = {8, 8, 8, 8, 8, 8, 8, 8}; 

inline v128_s16 DivideBy255(v128_s16 value) 
{ 
    return vec_sr(vec_add(vec_add(value, K16_0001), vec_sr(value, K16_0008)), K16_0008); 
} 

नियोन के लिए (एआरएम):

inline int16x8_t DivideI16By255(int16x8_t value) 
{ 
    return vshrq_n_s16(vaddq_s16(
     vaddq_s16(value, vdupq_n_s16(1)), vshrq_n_s16(value, 8)), 8); 
} 
+0

यह 'value == 65535' और सभी नकारात्मक संख्याओं के लिए गलत है (इसलिए हस्ताक्षर किए और न ही हस्ताक्षरित 16-बिट पूर्णांक के लिए काम करता है) –

+1

मुझे पता है कि यह अल्फा मिश्रण के लिए पूरी तरह से काम करता है। लेकिन मैं किसी अन्य मामलों में त्रुटियों को बाहर नहीं करता हूं। – ErmIg

+0

@ एंटनसेविन: मैंने आपके द्वारा प्राप्त अन्य प्रश्न के लिंक के आधार पर एक उत्तर पोस्ट किया। जीसीसी इस तरह से केवल कुछ और संचालन के साथ अच्छी तरह से एक सटीक सटीकता संस्करण vectorizes। –

3

जीसीसी का अनुकूलन DWORD(x * 0x8081) >> 0x17 जो आगे HWORD(x * 0x8081) >> 7 को सरल किया जा सकता है और अंत में HWORD((x << 15) + (x << 7) + x) >> 7 को unsigned short है x/255साथ एक्स

SIMD मैक्रो इस तरह दिख सकता: SSE integer division?

उपयोग GNU सी देशी:

#define MMX_DIV255_U16(x) _mm_srli_pi16(_mm_mulhi_pu16(x, _mm_set1_pi16((short)0x8081)), 7) 
#define SSE2_DIV255_U16(x) _mm_srli_epi16(_mm_mulhi_epu16(x, _mm_set1_epi16((short)0x8081)), 7) 
#define AVX2_DIV255_U16(x) _mm256_srli_epi16(_mm256_mulhi_epu16(x, _mm256_set1_epi16((short)0x8081)), 7) 
6

आप सभी मामलों के लिए एक बिल्कुल सही परिणाम चाहते हैं, सवाल एंटोन जुड़ा हुआ पर Marc Glisse's टिप्पणी से सलाह का पालन करें वेक्टर वाक्य रचना अपने दिए गए अदिश द्वारा एक सदिश के विभाजन व्यक्त करने के लिए, and see what it does:

typedef short vec_s16 __attribute__((vector_size(16))); 

vec_s16 div255(vec_s16 x){ return x/255; } // signed division 

    ; function arg x starts in xmm0 
    vpmulhw xmm1, xmm0, XMMWORD PTR .LC3[rip] ; a vector of set1(0x8081) 
    vpaddw xmm1, xmm1, xmm0 
    vpsraw xmm0, xmm0, 15  ; shift the original 
    vpsraw xmm1, xmm1, 7  ; shift the mulhi-and-add result 
    vpsubw xmm0, xmm1, xmm0 

.LC3: 
     .value -32639 
     .value -32639 
     ; repeated 

टी पर वह इस सवाल का जवाब सूजन होने का खतरा है, यहाँ यह intrinsics के साथ फिर से है:

__m128i div255_si128(__m128i x) { 
    __m128i tmp = _mm_mulhi_epi16(x, _mm_set1_epi16(0x8081)); 
    tmp = _mm_add_epi16(tmp, x); // There's no integer FMA that's usable here 
    x = _mm_srai_epi16(x, 15); // broadcast the sign bit 
    tmp = _mm_srai_epi16(tmp, 7); 
    return _mm_sub_epi16(tmp, x); 
} 

godbolt उत्पादन, नोट करें कि जीसीसी बहुत चालाक set1 के लिए स्मृति में एक ही 16B निरंतर उपयोग करने के लिए है और एक के लिए यह अपने आप में उत्पन्न div255 के लिए। AFAIK, यह स्ट्रिंग-निरंतर विलय की तरह काम करता है।

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