2012-04-01 12 views
6

इंटेल कंपाइलर इंट्रिनिक्स के साथ, 128-बिट रजिस्टर दिए गए, 8 16-बिट तत्वों को पैक करते हुए, मैं पंजीकरण के भीतर से (सस्ते) मनमानी तत्वों को कैसे एक्सेस करूं? _mm_cvtepi8_epi64 के बाद के उपयोग (हस्ताक्षर दो 8-बिट तत्वों का विस्तार करते हैं, रजिस्टर के निचले 16 बिट्स पर पैक किए गए हैं, दो 64-बिट तत्वों में)?128-बिट रजिस्टर में पैक किए गए मनमानी 16-बिट तत्वों तक पहुंच


कारण है कि मैं पूछना मैं समझाएंगे:

  1. इनपुट: कश्मीर बाइट्स, प्रत्येक या तो 0x0 या 0xff के साथ एक इन-स्मृति बफर।
  2. वांछित आउटपुट: इनपुट के प्रत्येक दो बाइट्स के लिए, क्रमशः 0x0 और 0xffff ffff ffff ffff के साथ दो क्वाड शब्द (64-बिट) पैक करने वाला एक रजिस्टर।
  3. अंतिम लक्ष्य: इनपुट बफर की प्रविष्टियों के अनुसार मास्क किए गए के डबल्स का बफर मिलाएं।

नोट: मान 0x0 और इनपुट बफर के 0xff, जो कुछ भी सबसे उपयोगी है करने के लिए बदला जा सकता है बशर्ते कि योग से पहले मास्किंग का असर बना हुआ है।

के रूप में मेरे सवाल से स्पष्ट हो सकता है, अपने वर्तमान प्लान आदानों बफ़र्स भर स्ट्रीमिंग इस प्रकार है,:

  1. 64-बिट के लिए 8-बिट से इनपुट मुखौटा बफर बढ़ाएँ।
  2. विस्तारित मुखौटा के साथ युगल बफर मास्क करें।
  3. मास्क किए गए युगल को समेटें।

धन्यवाद, आसफ

+1

'pmovsxbq' वास्तव में एक मेमोरी ऑपरेंड ले सकता है और स्मृति से उन दो बाइटों को सीधे लोड कर सकता है।लेकिन निश्चित रूप से एमएसवीसी टीम की परवाह नहीं है। – harold

+0

@harold हां, इंटेल द्वारा दिए गए इंट्रिनिक्स के लिए वास्तव में एक पता मोड गुम है। तो वास्तव में इंटेल को दोष देना है, एमएस नहीं (जैसा कि मुझे यह कहना नफरत है ;-))। आसान समाधान इनलाइन असेंबली में 'pmovsxbq' का उपयोग कर रहा है। अन्यथा बाइट्स को सही जगहों पर प्राप्त करने के लिए एक बार में 16 बाइट पढ़ना और कुछ 'pshufb' पढ़ना होगा। – hirschhornsalz

+0

@drhirsch अच्छी तरह से अप्रत्याशित है .. मुझे – harold

उत्तर

2

IDK क्यों कोई कभी उचित जवाब यह है कि टिप्पणी में कई बार आ गया है पोस्ट, लेकिन यहाँ यह है:

प्रत्येक बाइट, एक पूरे डबल के लिए मुखौटा है तो PMOVSXBQ है कि वास्तव में क्या हम की जरूरत है: लोड दो m16 पॉइंटर से बाइट्स, और उन्हें xmm रजिस्टर के दो 64 बिट (qword) हिस्सों में साइन-इन करें।

# UNTESTED CODE 
(loop setup stuff) 
# RSI: double pointer 
# RDI: mask pointer 
# RCX: loop conter = mask byte-count 
    LEA RDI, [RDI + RCX*1] 
    LEA RSI, [RSI + RCX*8] ; sizeof(double) = 8 
    NEG RCX ; point to the end and count up 

    XORPD XMM0, XMM0 ; clear accumulator 
ALIGN 16 
.loop: 
    PMOVSXBQ XMM1, [RDI + RCX] 
    ANDPD XMM1, [RSI + RCX * 8] 
    ADDPD XMM0, XMM1 
    ADD  RCX, 2  ; 2 bytes/doubles per iter 
    JL  .loop 
    HADDPD XMM0, XMM0 ; combine the two parallel sums 
    ret 

इंट्रिनिक्स के साथ इसे लिखना आसान होना चाहिए। जैसा कि अन्य ने इंगित किया है, केवल अंतर्निहित पॉइंटर्स का उपयोग इंट्रिनिक्स के तर्क के रूप में करें।

Sandybridge पर और बाद में, राम से PMOVSXBQ का उपयोग कर शायद अच्छा है:

अपने प्रश्न, चारों ओर डेटा शिफ्ट करने के लिए कि यह कैसे को पंक्तिबद्ध करने PMOVSX के लिए के बारे में के अन्य भाग का उत्तर दें। पिछले सीपीयू पर जो प्रति चक्र दो लोड को संभाल नहीं सकता है, एक समय में 16 बी मास्क डेटा लोड कर रहा है, और PSRLDQ xmm1, 2 के साथ 2 बाइट्स द्वारा इसे स्थानांतरित करने से रजिस्टर के कम 2 बाइट्स में मास्क डेटा के 2 बाइट डाले जाएंगे। या शायद PUNPCKHQDQ, या PSHUFD उच्च निर्भरता श्रृंखला को 64 से दूसरे 64 के निम्न 64 तक ले जाकर दो निर्भरता श्रृंखलाएं प्राप्त करने के लिए। आपको यह जांचना होगा कि किस पोर्ट का उपयोग किस निर्देश (शिफ्ट बनाम शफल/निकालने) द्वारा किया जाता है, और देखें कि PMOVSX और ADDPD के साथ कौन से संघर्ष कम हैं।

punpck और pshufd दोनों एसएनबी पर पी 1/पी 5 का उपयोग करते हैं, तो pmovsx करता है। addpd केवल पी 1 पर चल सकता है। andpd केवल पी 5 पर चल सकता है। हम्म, शायद PAND बेहतर होगा, क्योंकि यह पी 0 (और पी 1/पी 5) पर चल सकता है। अन्यथा लूप में कुछ भी निष्पादन बंदरगाह 0 का उपयोग नहीं करेगा। अगर पूर्णांक से एफपी डोमेन तक डेटा ले जाने के लिए विलंबता जुर्माना है, तो हम PMOVSX का उपयोग करते हुए अपरिहार्य है, क्योंकि इसे int डोमेन में मास्क डेटा प्राप्त होगा। सबसे लंबी निर्भरता श्रृंखला से लूप को लंबा बनाने के लिए अधिक accumulators का उपयोग करने के लिए बेहतर है। लेकिन यह सुनिश्चित करने के लिए कि 4 यूपीएस प्रति चक्र जारी कर सकते हैं, लूप बफर में फिट करने के लिए 28uops या इससे कम रखें।

और पूरी चीज़ को अनुकूलित करने के बारे में अधिक जानकारी: लूप को संरेखित करना वास्तव में आवश्यक नहीं है, क्योंकि नेहलेम पर और बाद में यह लूप बफर में फिट होगा।

आपको लूप को 2 या 4 से अनलोल करना चाहिए, क्योंकि प्री-हैसवेल इंटेल CPU में एक ही चक्र में सभी 4 (फ़्यूज्ड) यूओप्स को संभालने के लिए पर्याप्त निष्पादन इकाइयां नहीं हैं। (3 वेक्टर और एक फ़्यूज्ड add/jl। वेक्टर यूप्स के साथ दो लोड फ़्यूज़ का हिस्सा हैं।) सैंड्रिब्रिज और बाद में दोनों चक्रों को लोड कर सकते हैं, इसलिए लूप ओवरहेड को छोड़कर प्रति चक्र एक पुनरावृत्ति करने योग्य है।

ओह, ADDPD में 3 चक्रों की विलम्ब है। इसलिए आपको लूप-ले जाने वाली निर्भरता श्रृंखला को बाधा से बचने के लिए एकाधिक accumulators को अनलॉक और उपयोग करने की आवश्यकता है। शायद 4 से अनलॉक करें, और फिर अंत में 4 जमाकर्ताओं को जोड़ दें। आपको स्रोत कोड में भी इंट्रिनिक्स के साथ ऐसा करना होगा, क्योंकि इससे एफपी गणित के लिए संचालन का क्रम बदल जाएगा, इसलिए संकलक अनलोल करते समय ऐसा करने के इच्छुक नहीं हो सकता है।

तो प्रत्येक अनियंत्रित-बाय -4 लूप में 4 घड़ी चक्र लगेगा, साथ ही लूप ओवरहेड के लिए 1 यूओपी भी होगा। नेहलेम पर, जहां आपके पास एक छोटा पाश-कैश है लेकिन कोई यूओपी कैश नहीं है, तो अनलॉकिंग का मतलब हो सकता है कि आपको डिकोडर थ्रूपुट के बारे में देखभाल करना शुरू करना है। पूर्व-रेतीलेब्रिज पर, हालांकि प्रति घड़ी एक लोड शायद बाधा होगी।

डिकोडर थ्रूपुट के लिए, आप ANDPD के बजाय ANDPS का उपयोग कर सकते हैं, जो एन्कोड करने के लिए एक कम बाइट लेता है। आईडीके अगर इससे मदद मिलेगी।

इसे 256b ymm रजिस्टरों को विस्तारित करने के लिए सबसे सरल कार्यान्वयन के लिए AVX2 की आवश्यकता होगी (VPMOVSXBQ ymm के लिए)। आपको AVX-केवल दो VPMOVSXBQ xmm करके एक गति प्राप्त हो सकती है और उन्हें VINSERTF128 या कुछ के साथ संयोजित किया जा सकता है।

2

आप _mm_extract_epi16 (PEXTRW) और _mm_insert_epi16 (PINSRW) को देखा है?

+0

मैंने किया, और मुझे लगता है कि यह संभावित रूप से एक रजिस्टर के बजाय स्मृति में आउटपुट होगा, सबकुछ धीमा कर देगा। क्या मै गलत हु? क्या संकलक (एमएसवीसी) इसे ठीक करेगा? – Whaa

+0

नहीं - ये निर्देश सीधे एसएसई (एक्सएमएम) रजिस्ट्रार और सामान्य रजिस्टरों के बीच काम करते हैं। यदि आप उदाहरण के लिए जेनरेट किए गए कोड को देखते हैं '_mm_set_epi16' आप देखेंगे कि यह सिर्फ 'पिनएसआरडब्ल्यू' की एक स्ट्रिंग उत्पन्न करता है। –

+0

समझ में आता है। धन्यवाद, पॉल। – Whaa

3

बल्कि सवाल ही की स्पर्श रेखा, टिप्पणियों पर कुछ जानकारी में और अधिक भरने क्योंकि टिप्पणी अनुभाग खुद भी इस धारण करने के लिए छोटा है (इस प्रकार से!):

कम से कम जीसीसी निम्नलिखित के साथ सौदा कर सकते हैं कोड:

#include <smmintrin.h> 

extern int fumble(__m128i x); 

int main(int argc, char **argv) 
{ 
    __m128i foo; 
    __m128i* bar = (__m128i*)argv; 

    foo = _mm_cvtepi8_epi64(*bar); 

    return fumble(foo); 
}

यह निम्न विधानसभा में इस बदल जाता है:

Disassembly of section .text.startup: 

0000000000000000 : 
    0: 66 0f 38 22 06   pmovsxbq (%rsi),%xmm0 
    5: e9 XX XX XX XX   jmpq .....

इसका मतलब है कि वें ई intrinsics मेमोरी-तर्क फॉर्म में आने की आवश्यकता नहीं है - संकलक पारदर्शी रूप से एक mem तर्क को संदर्भित करता है और यदि संभव हो तो संबंधित मेम-ऑपरेंड निर्देश का उपयोग करता है। आईसीसी वही करता है। मेरे पास यह जांचने के लिए विंडोज मशीन/विजुअल सी ++ नहीं है कि एमएसवीसी भी ऐसा करता है, लेकिन मैं इसकी अपेक्षा करता हूं।

+0

इसके बारे में बिल्कुल यकीन नहीं है। असेंबली फॉर्म को किसी भी संरेखण की आवश्यकता नहीं होती है, और यह किसी शब्द ('movw' या' mov WORD PTR') में पॉइंटर लेता है। क्या संकलक 'pmovsxbq' उत्सर्जित करेगा भले ही सूचक को असाइन किया गया हो? वैसे भी पॉल आर की तुलना में एक बेहतर जवाब, जो इस परिदृश्य के लिए बेकार है। – hirschhornsalz

+0

ठीक है, अब मैं देखता हूं कि सूचक वास्तव में असाइन नहीं किया गया है। शोर के लिए खेद है :-) – hirschhornsalz

+0

@drhirsch: उपर्युक्त पाठ्यक्रम से उत्पन्न होता है - बस यह स्पष्ट करने के लिए कि संकलक 'pmovsxbq (...),% xmm..' उत्सर्जित करेगा यदि आंतरिक को अस्वीकृत सूचक को तर्क के रूप में दिया जाता है। मैंने केवल एक मनमाना उपलब्ध गैर-'एनयूएलएल' सूचक चुना ;-) –

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