2011-04-02 18 views
7

_mm_extract_psfloat के बजाय int क्यों लौटाता है?इंटेल एसएसई: 'फ्लोट` के बजाय `_mm_extract_ps` वापसी` int` क्यों करता है?

सी में एक्सएमएम रजिस्टर से एकल float पढ़ने का सही तरीका क्या है?

या बल्कि, एक अलग तरह से यह पूछने के लिए है: _mm_set_ps अनुदेश के विपरीत क्या है?

+0

संभावित डुप्लिकेट [_mm_extract_ps एसएसई जीसीसी instrinc फ़ंक्शन का उपयोग कर एक हेक्स फ्लोट को सी/सी ++ में एक फ्लोट में कैसे परिवर्तित करें] (http://stackoverflow.com/questions/3130169/how-to-convert-a-hex- फ्लोट-टू-ए-फ्लोट-इन-सीसी-उपयोग-मिमी-निकालने-ps-sse-gcc-instr) –

+0

यहां मेरा उत्तर देखें: http://stackoverflow.com/questions/3130169/how-to -convert-a-hex-float-to-a-float-in-cc-use-mm-extract-ps-sse-gcc-inst –

उत्तर

1

MSDN docs से, मेरा मानना ​​है कि आप परिणाम को एक फ्लोट में डाल सकते हैं।

उनके उदाहरण से नोट, 0xc0a40000 मान -5.125 (a.m128_f32 [1]) के बराबर है।

अपडेट: मैं अपने डू के बदले @ doug65536 और @ पीटर कॉर्डस (नीचे) के उत्तरों की दृढ़ता से अनुशंसा करता हूं, जो स्पष्ट रूप से कई कंपाइलरों पर खराब प्रदर्शन कोड उत्पन्न करता है।

+0

हालांकि, यह किस तरह का कलाकार होगा? एक सी-शैली का कलाकार काम नहीं करेगा, है ना? – Mehrdad

+1

निश्चित - जब तक भंडारण आकार मेल खाता है। बीटीडब्ल्यू - ऐसा लगता है कि एक मैक्रो है जो मदद कर सकता है: _MM_EXTRACT_FLOAT (देखें http://stackoverflow.com/questions/3130169/how-to-convert-a-hex-float-to-a-float-in-cc-using- मिमी-निकालने-पीएस-एसएसई-जीसीसी-इंस्ट्र) – holtavolt

+0

अह्ह्ह्ह्ह्ह्ह्ह्ह्हें जिस समस्या का सामना कर रहा था उसका ठीक समाधान है !! खोजना मुश्किल है; सहायता के लिए धन्यवाद! :) – Mehrdad

1

_mm_storeu_ps, या SSE store operations की किसी भी विविधता का प्रयास करें।

+0

मैं एक रजिस्टर को पढ़ने की कोशिश कर रहा हूं, स्मृति के लिए नहीं ... – Mehrdad

17

जवाब में से कोई भी वास्तव में सवाल है, जवाब देने के लिए दिखाई क्यों यह int वापसी करता है।

कारण यह है कि extractps निर्देश वास्तव में वेक्टर के एक घटक को एक सामान्य रजिस्टर में प्रतिलिपि बनाता है। यह एक int वापस करने के लिए बहुत मूर्खतापूर्ण प्रतीत होता है लेकिन वास्तव में यह हो रहा है - कच्चे फ़्लोटिंग पॉइंट मान एक सामान्य रजिस्टर (जो पूर्णांक धारण करता है) में समाप्त होता है।

यदि आपका कंपाइलर सभी फ़्लोटिंग पॉइंट ऑपरेशंस के लिए एसएसई उत्पन्न करने के लिए कॉन्फ़िगर किया गया है, तो रजिस्टर के लिए मूल्य को "निकालने" की सबसे नज़दीकी चीज वैक्टर के निम्न घटक में मूल्य को घुमाएगी, फिर इसे एक स्केलर फ्लोट। इस कारण चाहिए वेक्टर के तत्व एक SSE रजिस्टर में रहने के लिए:

/* returns the second component of the vector */ 
float foo(__m128 b) 
{ 
    return _mm_cvtss_f32(_mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 2))); 
} 

_mm_cvtss_f32 आंतरिक मुक्त है, यह निर्देश उत्पन्न नहीं करता है, यह केवल बनाता संकलक पुनर्व्याख्या XMM रजिस्टर के रूप में एक float तो यह हो सकता है इस तरह से लौट आया।

_mm_shuffle_ps वांछित मूल्य को निम्नतम घटक में प्राप्त किया जाता है। _MM_SHUFFLE मैक्रो परिणामी shufps निर्देश के लिए तत्काल ऑपरेंड उत्पन्न करता है।

उदाहरण में 2 127: 0 रजिस्टर (शुरुआत से तीसरा 32 बिट घटक, स्मृति क्रम में) के 95:64 से फ्लोट प्राप्त करता है और इसे रजिस्टर के 31: 0 घटक में रखता है (शुरुआत, स्मृति क्रम में)।

परिणामी जेनरेट कोड संभावित रूप से किसी भी अन्य फ़्लोटिंग पॉइंट वैल्यू रिटर्न की तरह किसी भी रजिस्टर में स्वाभाविक रूप से मूल्य वापस कर देगा, जिसमें स्मृति में कोई अक्षमता लिखने और उसे वापस पढ़ने के साथ।

यदि आप कोड उत्पन्न कर रहे हैं जो फ़्लोटिंग पॉइंट के लिए x87 एफपीयू का उपयोग करता है (सामान्य सी कोड के लिए जो एसएसई अनुकूलित नहीं है), तो संभवतया अक्षम कोड उत्पन्न हो जाएगा - संकलक शायद घटक का संग्रह करेगा एसएसई वेक्टर तब x12 रजिस्टर स्टैक में इसे पढ़ने के लिए fld का उपयोग करें। सामान्य 64-बिट प्लेटफार्मों में x87 का उपयोग नहीं होता है (वे सभी फ़्लोटिंग पॉइंट के लिए एसएसई का उपयोग करते हैं, ज्यादातर संकलक निर्देश जब तक कि संकलक वेक्टरिंग नहीं कर रहे हैं)।

मुझे यह जोड़ना चाहिए कि मैं हमेशा सी ++ का उपयोग करता हूं, इसलिए मुझे यकीन नहीं है कि यह __m128 को मूल्य से या सी में + सूचक में पास करने के लिए अधिक कुशल है या नहीं। मैं const __m128 & का उपयोग करूंगा और इस तरह का कोड होगा एक हेडर, इसलिए संकलक इनलाइन कर सकते हैं।

+0

+1 भयानक उत्तर, धन्यवाद। – Mehrdad

+0

'movss xmm, xmm' ऊपरी घटकों को शून्य नहीं करता है। यह उन्हें अपरिवर्तित छोड़ देता है। शायद आप पूर्णांक 'movd xmm, r32' या' movd xmm, m32' के बारे में सोच रहे होंगे। साथ ही, _MM_SHUFFLE '{d c b a} 'के वेक्टर को' {a a c} 'में बदल देता है। यह तीसरा तत्व निम्न 32 में रखता है, दूसरा नहीं, जब तक कि आप उच्च अंत से गिनती नहीं कर रहे हैं ... –

+0

@ पीटरकॉर्डस फिक्स्ड और शफल को स्पष्ट, धन्यवाद। – doug65536

4

उलझन में, int _mm_extract_ps() एक वेक्टर से float तत्व स्केलर प्राप्त करने के लिए नहीं है। अंतर्निहित निर्देश के स्मृति-गंतव्य रूप का खुलासा नहीं करता है (जो उस उद्देश्य के लिए उपयोगी हो सकता है)। यह एकमात्र ऐसा मामला नहीं है जहां अंतर्दृष्टि सीधे निर्देशों को व्यक्त नहीं कर सकती है। :(

जीसीसी और बजना जानते हैं कि कैसे एएसएम अनुदेश काम करता है और जब अन्य शफ़ल संकलन आप के लिए इसे उस तरह से उपयोग करेगा;। प्रकार- punning float को _mm_extract_ps परिणाम आमतौर पर जीसीसी (extractps eax, xmm0, 2/mov [mem], eax) से भयानक एएसएम में जो परिणाम

नाम अगर आप एक IEEE 754 binary32 float bit pattern पूर्णांक डोमेन में सीपीयू की एफपी डोमेन से बाहर निकालने (एक सी अदिश int के रूप में), के बजाय पूर्णांक वेक्टर ऑप्स साथ एफपी बिट पैटर्न जोड़ तोड़ के रूप में _mm_extract_ps के बारे में सोच समझ में आता है। के अनुसार जीसीसी, क्लैंग, और आईसीसी (नीचे देखें) के साथ मेरा परीक्षण, यह एकमात्र "पोर्टेबल" उपयोग-मामला है जहां _mm_extract_ps सभी कंपाइलर्स में अच्छे एएसएम में संकलित करता है। आप चाहते हैं कि एएसएम पाने के लिए कुछ और सिर्फ एक कंपाइलर-विशिष्ट हैक है।


इसी एएसएम अनुदेश EXTRACTPS r/m32, xmm, imm8 है। ध्यान दें कि गंतव्य मेमोरी हो सकता है या पूर्णांक रजिस्टर है, लेकिन कोई अन्य XMM रजिस्टर नहीं है। यह PEXTRD r/m32, xmm, imm8 (एसएसई 4.1 में भी) के एफपी समकक्ष है, जहां पूर्णांक-रजिस्टर-गंतव्य फॉर्म अधिक स्पष्ट रूप से उपयोगी है। एक्सट्रैक्ट्स INSERTPS xmm1, xmm2/m32, imm8 के विपरीत नहीं है।

शायद पीएक्सटीआरडी के साथ यह समानता आंतरिक कार्यान्वयन को एक्स्ट्रा-टू-मेमोरी उपयोग-केस (एएसएम, इंट्रिनिक्स नहीं) के नुकसान के बिना सरल बनाता है, या शायद इंटेल पर एसएसई 4.1 डिजाइनरों ने सोचा कि यह वास्तव में इस तरह से अधिक उपयोगी था एक विनाशकारी एफपी-डोमेन प्रति-और-शफल के रूप में (जो x86 गंभीर रूप से AVX के बिना कमी करता है)। एफपी-वेक्टर निर्देश हैं जिनमें एक्सएमएम स्रोत और मेमोरी-या-एक्सएमएम गंतव्य है, जैसे MOVSS xmm2/m32, xmm, इसलिए इस प्रकार का निर्देश नया नहीं होगा। मजेदार तथ्य: PEXTRD और EXTRACTPS के लिए ऑपकोड केवल अंतिम बिट में भिन्न होते हैं।


विधानसभा में, एक अदिश float सिर्फ एक XMM रजिस्टर (स्मृति में या 4 बाइट्स) के निम्न तत्व है। एक्सएमएम के ऊपरी तत्वों को ADDSS जैसे किसी भी अतिरिक्त एफपी अपवादों को उठाए बिना काम करने के लिए भी शून्य नहीं होना चाहिए। एक्सएमएम रजिस्टरों (जैसे सभी सामान्य x86-64 एबीआई) में एफपी तर्कों को पास/वापस करने वाले सम्मेलनों को कॉल करने में, float foo(float a) को यह मानना ​​चाहिए कि XMM0 के ऊपरी तत्व प्रवेश पर कचरा पकड़ते हैं, लेकिन बदले में XMM0 के उच्च तत्वों में कचरा छोड़ सकते हैं।()।

As @doug points out, अन्य फेरबदल निर्देश एक XMM रजिस्टर के निचले हिस्से में एक वेक्टर के एक नाव तत्व प्राप्त करने के लिए इस्तेमाल किया जा सकता। यह एसएसई 1/एसएसई 2 में पहले से ही एक हल की गई समस्या थी, और ऐसा लगता है कि एक्सट्रैक्ट्स और इंसर्ट्स रजिस्टर ऑपरेंड के लिए इसे हल करने की कोशिश नहीं कर रहे थे।


एसएसई 4।1 INSERTPS xmm1, xmm2/m32, imm8_mm_set_ss(function_arg) को लागू करने के लिए कंपाइलर्स के लिए सबसे अच्छे तरीकों में से एक है जब स्केलर फ्लोट पहले से ही एक रजिस्टर में है और वे ऊपरी तत्वों को शून्य करने के अनुकूल नहीं कर सकते हैं। (Which is most of the time for compilers other than clang)। उस लिंक किए गए प्रश्न में इंटरट्रैक्ट्स, इंसर्ट्स, और पीएमओवीजेएक्स जैसे निर्देशों के लोड या स्टोर संस्करणों का पर्दाफाश करने के लिए इंट्रिनिक्स की विफलता पर भी चर्चा की गई है, जिनमें 128 बी से स्मृति मेमोरी और संकुचित है (इस प्रकार एवीएक्स के बिना भी संरेखण की आवश्यकता नहीं है)। सुरक्षित कोड लिखना असंभव हो सकता है जो कुशलतापूर्वक संकलित करता है जैसे आप एएसएम में क्या कर सकते हैं।

एवीएक्स 3-ऑपरेंड SHUFPS के बिना, x86 एक एफपी वेक्टर की प्रतिलिपि बनाने और पूर्ण करने के लिए पूरी तरह से कुशल और सामान्य उद्देश्य प्रदान नहीं करता है जिस तरह से पूर्णांक PSHUFD कर सकते हैं। SHUFPS एक अलग जानवर है जब तक कि src = dst के साथ जगह में उपयोग नहीं किया जाता है। मूल को संरक्षित करने के लिए एक MOVAPS की आवश्यकता होती है, जो IvyBridge से पहले CPU पर यूओपी और विलंबता खर्च करती है, और हमेशा कोड-आकार की लागत होती है। एफपी निर्देशों के बीच पीएसएचयूएफडी का उपयोग विलंबता (बाईपास देरी) लागत। (कुछ चाल के लिए this horizontal-sum answer देखें, जैसे एसएसई 3 MOVSHDUP का उपयोग करना)।

एसएसई 4.1 INSERTPS एक तत्व को एक अलग रजिस्टर में निकाल सकता है, लेकिन AFAIK के पास अभी भी गंतव्य के पिछले मान पर निर्भरता है, भले ही सभी मूल मान प्रतिस्थापित किए जाएं। इस तरह की झूठी निर्भरता आउट-ऑफ-ऑर्डर निष्पादन के लिए खराब हैं। xor-zeroing INSERTPS के लिए एक गंतव्य के रूप में एक रजिस्टर अभी भी 2 यूओपीएस होगा, और शून्य-विलंबता MOVAPS (केवल पेरीन, नेहलेम, सैंडब्रिज) के लिए mov-elimination के बिना एसएसई 4.1 सीपीयू पर MOVAPS + SHUFPS की तुलना में कम विलंबता है। सिल्वरमोंट यदि आप कम शामिल करते हैं सशक्त सीपीयू)। कोड आकार थोड़ा खराब है, हालांकि।


_mm_extract_ps और उसके बाद का उपयोग टाइप-punning परिणाम वापस फ्लोट करने के लिए (सुझाव के रूप में in the currently-accepted answer और अपनी टिप्पणी) एक बुरा विचार है। जीसी या आईसीसी पर आपके कोड को कुछ भयानक (जैसे मेमोरी के लिए एक्स्ट्राक्ट्स और फिर एक्सएमएम रजिस्टर में वापस लोड करना) के लिए संकलित करना आसान है। क्लैंग ब्राइंडेड व्यवहार से प्रतिरक्षा प्रतीत होता है और यह सामान्य रूप से शफल निर्देशों (एक्स्ट्राक्ट्स के उचित उपयोग सहित) की अपनी पसंद के साथ संकलित करता है।

मैंने इन उदाहरणों को gcc5.4 -O3 -msse4.1 -mtune=haswell, clang3.8.1, और icc17, on the Godbolt compiler explorer के साथ करने की कोशिश की। मैंने सी मोड का इस्तेमाल किया, सी ++ नहीं, लेकिन जीएनयू सी ++ में यूनियन-आधारित टाइप पनिंग की अनुमति आईएसओ सी ++ के विस्तार के रूप में है। टाइप-पनिंग के लिए पॉइंटर-कास्टिंग जीएनयू एक्सटेंशन के साथ भी सी 99 और सी ++ में सख्त एलियासिंग का उल्लंघन करता है।

#include <immintrin.h> 

// gcc:bad clang:good icc:good 
void extr_unsafe_ptrcast(__m128 v, float *p) { 
    // violates strict aliasing 
    *(int*)p = _mm_extract_ps(v, 2); 
} 

    gcc: # others extractps with a memory dest 
    extractps  eax, xmm0, 2 
    mov  DWORD PTR [rdi], eax 
    ret 


// gcc:good clang:good icc:bad 
void extr_pun(__m128 v, float *p) { 
    // union type punning is safe in C99 (and GNU C and GNU C++) 
    union floatpun { int i; float f; } fp; 
    fp.i = _mm_extract_ps(v, 2); 
    *p = fp.f;  // compiles to an extractps straight to memory 
} 

    icc: 
    vextractps eax, xmm0, 2 
    mov  DWORD PTR [rdi], eax 
    ret  


// gcc:good clang:good icc:horrible 
void extr_gnu(__m128 v, float *p) { 
    // gcc uses extractps with a memory dest, icc does extr_store 
    *p = v[2]; 
} 

gcc/clang: 
    extractps  DWORD PTR [rdi], xmm0, 2 
icc: 
    vmovups XMMWORD PTR [-24+rsp], xmm0 
    mov  eax, DWORD PTR [-16+rsp]  # reload from red-zone tmp buffer 
    mov  DWORD PTR [rdi], eax 

// gcc:good clang:good icc:poor 
void extr_shuf(__m128 v, float *p) { 
    __m128 e2 = _mm_shuffle_ps(v,v, 2); 
    *p = _mm_cvtss_f32(e2); // gcc uses extractps 
} 

icc: (others: extractps right to memory) 
    vshufps xmm1, xmm0, xmm0, 2 
    vmovss DWORD PTR [rdi], xmm1 

आप एक XMM रजिस्टर में अंतिम परिणाम चाहते हैं, तो यह आपके extractps दूर अनुकूलन और कुछ पूरी तरह से अलग करने के लिए संकलक पर निर्भर है। जीसीसी और क्लैंग दोनों सफल होते हैं, लेकिन आईसीसी नहीं करता है।

// gcc:good clang:good icc:bad 
float ret_pun(__m128 v) { 
    union floatpun { int i; float f; } fp; 
    fp.i = _mm_extract_ps(v, 2); 
    return fp.f; 
} 

    gcc: 
    unpckhps  xmm0, xmm0 
    clang: 
    shufpd xmm0, xmm0, 1 
    icc17: 
    vextractps DWORD PTR [-8+rsp], xmm0, 2 
    vmovss xmm0, DWORD PTR [-8+rsp] 

ध्यान दें कि आईसीसी, extr_pun के लिए खराब प्रदर्शन किया भी, तो यह इस बात के लिए संघ-आधारित प्रकार-punning पसंद नहीं है।

स्पष्ट विजेता यहां _mm_shuffle_ps(v,v, 2) के साथ "मैन्युअल" शफल कर रहा है, और _mm_cvtss_f32 का उपयोग कर रहा है। हमें आईसीसी को छोड़कर, मेमोरी-डेस्ट केस के लिए एक्स्ट्राक्ट्स का उपयोग करने में असफल रहा, दोनों रजिस्टर और मेमोरी गंतव्यों के लिए प्रत्येक कंपाइलर से इष्टतम कोड मिला। एवीएक्स के साथ, SHUFPS + अलग स्टोर अभी भी इंटेल सीपीयू पर केवल 2 यूओपीएस है, बस बड़ा कोड आकार और एक टीएमपी रजिस्टर की आवश्यकता है।AVX के बिना, हालांकि, यह एक MOVAPS लागत मूल सदिश को नष्ट नहीं करने के लिए होगा:/


Agner Fog's instruction tables के अनुसार, Nehalem को छोड़कर सभी इंटेल सीपीयू कई UOPs के साथ दोनों PEXTRD के रजिस्टर-गंतव्य संस्करणों और EXTRACTPS लागू: आमतौर पर वेक्टर डोमेन से जीपी-इंटीजर तक डेटा ले जाने के लिए बस एक शफल यूओपी + एक एमओवीडी यूओपी। नेहलेम रजिस्टर-गंतव्य एक्सट्रैक्ट्स 1 + 2 चक्र विलंबता (1 + बाईपास देरी) के साथ पोर्ट 5 के लिए 1 यूओपी है।

मुझे नहीं पता कि वे एक ही यूओपी के रूप में एक्स्ट्राक्ट्स को कार्यान्वित करने में कामयाब रहे, लेकिन PEXTRD नहीं (जो 2 यूओएस है, और 2 + 1 चक्र विलंबता में चलता है)। नेहलेम एमओवीडी 1 + 1 चक्र विलंबता के साथ 1 यूओपी (और किसी भी एएलयू बंदरगाह पर चलता है) है। (+1 वीसी-इंट और सामान्य उद्देश्य पूर्णांक regs के बीच बाईपास विलंब के लिए है, मुझे लगता है)।

नेहलेम वेक्टर एफपी बनाम पूर्णांक डोमेन के बारे में बहुत कुछ परवाह करता है; एसएनबी-परिवार सीपीयू डोमेन के बीच छोटे (कभी-कभी शून्य) बाईपास देरी विलंबता होती है।

पेक्सट्रैड और एक्सट्रैक्ट्स के मेमोरी-डेस्ट संस्करण नेहलेम पर 2 यूओप्स हैं।

ब्रॉडवेल पर और बाद में, मेमोरी-गंतव्य एक्स्ट्राक्ट्स और पेक्सट्रैड 2 यूप्स हैं, लेकिन हैडवेल के माध्यम से सैंड्रिब्रिज पर, स्मृति-गंतव्य एक्सट्रैक्ट्स 3 यूओपीएस है। मेमरी-गंतव्य PEXTRD सैंडीब्रिज को छोड़कर सबकुछ पर 2 यूप्स है, जहां यह 3 है। यह अजीब लगता है, और एग्नेर फोग की टेबल में कभी-कभी त्रुटियां होती हैं, लेकिन यह संभव है। सूक्ष्म-संलयन कुछ सूक्ष्मजीवों पर कुछ निर्देशों के साथ काम नहीं करता है।

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

मजेदार तथ्य: _mm_extract_epi32(vec, 0) संकलन (अधिकांश कंपाइलर पर) movd eax, xmm0 जो pextrd eax, xmm0, 0 से छोटा और तेज़ है।

दिलचस्प बात यह है कि they perform differently on Nehalem (जो वेक्टर एफपी बनाम पूर्णांक डोमेन के बारे में बहुत अधिक परवाह करता है, और पेनिन (45 एनएम कोर 2) में एसएसई 4.1 पेश करने के तुरंत बाद बाहर आया। एक रजिस्टर गंतव्य के साथ एक्सट्रैक्ट 1 यूओपी है, 1 + 2 चक्र विलंबता (एफ 2 और पूर्णांक डोमेन के बीच बायपास विलंब से +2)। PEXTRD 2 यूओएस है, और 2 + 1 चक्र विलंबता में चलता है।

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