2011-11-19 19 views
11

मैं एसएसई 4 के साथ __m128i ऑब्जेक्ट के साथ 16 हस्ताक्षरित 8 बिट पूर्णांक के साथ गुणा करना चाहता हूं, लेकिन मुझे केवल 16 बिट पूर्णांक गुणा करने के लिए एक आंतरिक मिल सकता है। क्या _mm_mult_epi8 जैसे कुछ भी नहीं है?एसएसई गुणा 16 x uint8_t

+1

क्या आप अपने प्रश्न को थोड़ा सा स्पष्ट कर सकते हैं? क्या आप 16 8 बिट पूर्णांक के साथ एक 128 बिट पूर्णांक गुणा करना चाहते हैं या 16 8 बिट पूर्णांक वाले 16 8 बिट पूर्णांक या एक दूसरे के साथ एक ही रजिस्टर में 16 8 बिट पूर्णांक को गुणा करना चाहते हैं। पूर्व मामला थोड़ा अजीब होगा। –

+0

बस एक विचार है लेकिन क्यों 8 बिट से 16 तक पैड नहीं? और यदि आप ओवरफ्लो का परीक्षण करना चाहते हैं तो आप केवल एएच और एएच कर सकते हैं और देख सकते हैं कि अतिप्रवाह की जांच करने के लिए कोई मिलान है या नहीं। थोड़ा गन्दा और अंधेरे में बस एक छिद्र। अगर मुझे सिम के लिए निर्देश सेट 8 बिट प्रोसेसर –

+0

@ पॉल: 8-बिट मानों का उपयोग अभी भी ग्राफिक्स में किया जाता है, तो यह मुझे भी आश्चर्यचकित करेगा कि 8 बिट माल के लिए समर्थन था। AltiVec में 8-बिट गुणा है, हालांकि 16-बिट परिणामों के साथ एक समय में केवल 8। – Potatoswatter

उत्तर

11

एमएमएक्स/एसएसई/एवीएक्स में कोई 8-बिट गुणा नहीं है।

inline __m128i _mm_mullo_epi8(__m128i a, __m128i b) 
{ 
    __m128i zero = _mm_setzero_si128(); 
    __m128i Alo = _mm_cvtepu8_epi16(a); 
    __m128i Ahi = _mm_unpackhi_epi8(a, zero); 
    __m128i Blo = _mm_cvtepu8_epi16(b); 
    __m128i Bhi = _mm_unpackhi_epi8(b, zero); 
    __m128i Clo = _mm_mullo_epi16(Alo, Blo); 
    __m128i Chi = _mm_mullo_epi16(Ahi, Bhi); 
    __m128i maskLo = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14, 12, 10, 8, 6, 4, 2, 0); 
    __m128i maskHi = _mm_set_epi8(14, 12, 10, 8, 6, 4, 2, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); 
    __m128i C = _mm_or_si128(_mm_shuffle_epi8(Clo, maskLo), _mm_shuffle_epi8(Chi, maskHi)); 

    return C; 
} 
8

केवल 8 बिट एसएसई गुणा निर्देश PMADDUBSW (एसएसएसई 3 और बाद में, सी/सी ++ आंतरिक: _mm_maddubs_epi16) है। यह 16 x 8 बिट अनुक्रमित मान 16 x 8 बिट द्वारा मानों पर हस्ताक्षर किए और फिर 8 x 16 बिट हस्ताक्षरित परिणाम देने के लिए आसन्न जोड़े को रकम करता है। यदि आप इस विशेष निर्देश का उपयोग नहीं कर सकते हैं तो आपको 16 बिट वैक्टर के जोड़े को अनपैक करना होगा और नियमित 16 बिट गुणा निर्देशों का उपयोग करना होगा। जाहिर है, यह कम से कम 2x थ्रूपुट हिट का तात्पर्य है, इसलिए यदि संभवतः आप कर सकते हैं तो 8 बिट गुणा करें।

12

एक मरात के समाधान की तुलना में (संभावित) तेजी से रास्ते पर Agner Fog's solution आधारित:

के बजाय बंटवारे हाय/कम, विभाजन हालांकि, अगर आप 8 बिट गुणा आंतरिक 16-बिट गुणन का उपयोग इस प्रकार का अनुकरण कर सकते हैं विषम सम। इसमें अतिरिक्त लाभ है कि यह एसएसई 4.1 की आवश्यकता के बजाय शुद्ध एसएसई 2 के साथ काम करता है (ओपी के लिए कोई उपयोग नहीं है, लेकिन कुछ के लिए एक अच्छा जोड़ा बोनस)। यदि आपके पास AVX2 है तो मैंने एक अनुकूलन भी जोड़ा। तकनीकी रूप से AVX2 ऑप्टिमाइज़ेशन केवल एसएसई 2 इंट्रिनिक्स के साथ काम करता है, लेकिन यह सही समाधान के बाद शिफ्ट की तुलना में धीमा है।

__m128i mullo_epi8(__m128i a, __m128i b) 
{ 
    // unpack and multiply 
    __m128i dst_even = _mm_mullo_epi16(a, b); 
    __m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(a, 8),_mm_srli_epi16(b, 8)); 
    // repack 
#ifdef __AVX2__ 
    // only faster if have access to VPBROADCASTW 
    return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_and_si128(dst_even, _mm_set1_epi16(0xFF))); 
#else 
    return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_srli_epi16(_mm_slli_epi16(dst_even,8), 8)); 
#endif 
} 

Agner blendv_epi8 SSE4.1 समर्थन के साथ आंतरिक उपयोग करता है।

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

दिलचस्प है, (के साथ अनुकूलित बनाता है), और अधिक disassembly के काम करने के बाद कम से कम मेरे दो कार्यान्वयन बिल्कुल वही बात करने के लिए संकलित मिलता है। "Ivy-bridge" (AVX) को लक्षित करने वाले उदाहरण disassembly।

vpmullw xmm2,xmm0,xmm1 
vpsrlw xmm0,xmm0,0x8 
vpsrlw xmm1,xmm1,0x8 
vpmullw xmm0,xmm0,xmm1 
vpsllw xmm0,xmm0,0x8 
vpand xmm1,xmm2,XMMWORD PTR [rip+0x281] 
vpor xmm0,xmm0,xmm1 

यह पूर्व-संकलित 128-बिट xmm स्थिरता के साथ "AVX2- अनुकूलित" संस्करण का उपयोग करता है। केवल एसएसई 2 समर्थन के साथ संकलन एक समान परिणाम उत्पन्न करता है (हालांकि एसएसई 2 निर्देशों का उपयोग)। मुझे संदेह है कि एग्नेर फोग का मूल समाधान एक ही चीज़ के लिए अनुकूलित हो सकता है (अगर यह नहीं होता तो पागल हो जाएगा)। कोई विचार नहीं कि मारत का मूल समाधान एक अनुकूलित निर्माण में कैसे तुलना करता है, हालांकि मेरे पास एसएसई 2 के मुकाबले नए और सभी x86 सिम एक्सटेंशन के लिए एक ही विधि है, यह काफी अच्छा है।

+2

यह वास्तव में अच्छा है। यह इस तथ्य का लाभ उठाता है कि हस्ताक्षरित बनाम हस्ताक्षरित केवल एन एक्स एन -> 2 एन बिट गुणा के उच्च आधा को प्रभावित करता है, और [उच्च बिट्स में वह कचरा कम बिट्स में इच्छित परिणाम को प्रभावित नहीं करता है] (http://stackoverflow.com/questions/34377711/which-2s-complement-integer-operations-can-be-used-without-zeroing-high-bits-in)।यदि मास्क लोड करते समय कैश-मिस एक समस्या है, तो आप इसे 2 इंन्स के साथ फ्लाई पर उत्पन्न कर सकते हैं: 'pcmpeqw xmm7, xmm7' /' psrlw xmm7, 8'। (अन्य कॉन्स्ट-पीढ़ी अनुक्रमों के लिए http://stackoverflow.com/q/35085059/224132 देखें)। –

+1

यह साफ है, मैं देखता हूं [क्लैंग शिफ्ट-बाएं/शिफ्ट-दाएं को स्थिर मास्क के साथ 'vpand' पर अनुकूलित करता है] (http://goo.gl/GmFc9H)! यह शायद बेहतर कोड है, जब तक कि मास्क कैश में याद न हो जाए। जीसीसी उस अनुकूलन नहीं करता है। शिफ्ट और मास्क के बीच की पसंद AVX2 पर निर्भर नहीं है। यह इस बात पर निर्भर करता है कि स्मृति से एक बड़ा निरंतर आप क्या चाहते हैं। (मुझे लगता है कि एवीएक्स के बिना, क्लैंग अंत में एक movdqa बर्बाद कर देता है: यह दूसरे pmul के लिए 'pmullw xmm0, xmm1' का उपयोग कर सकता था और अंतिम परिणाम 'xmm0' (वापसी-मूल्य रजिस्टर) में बनाया गया था। –

+1

आपकी टिप्पणी 'vpbroadcastw' के बारे में पूरी तरह से गलत है: अधिकांश कंपाइलर्स स्थिरांक के लिए रन-टाइम प्रसारण में' set1' संकलित नहीं करते हैं, क्योंकि यह महंगा है। 'mov eax, 0xff' /' movd xmm0, eax'/vpbroadcastw xmm0, xmm0' है हैसवेल पर 3 यूओएस। 'Vpbroadcastw xmm0, [mem16]' 3 यूप्स भी है। फ्लाई पर उत्पन्न करना या तो सस्ता है (लेकिन कंपाइलर्स उन्हें स्मृति में फेंक देते हैं)। हालांकि, स्मृति से 'vpbroadcastd' केवल 1 यूओपी है, यहां तक ​​कि अप्रयुक्त भी: इसे केवल लोड पोर्ट की आवश्यकता है, एएलयू नहीं। इसलिए आपको 32 बी मेमोरी को लगातार स्थिर करने की आवश्यकता नहीं है जो लूप के बाहर लोड होने जा रहा है। –