उलझन में, 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 चक्र विलंबता में चलता है।
संभावित डुप्लिकेट [_mm_extract_ps एसएसई जीसीसी instrinc फ़ंक्शन का उपयोग कर एक हेक्स फ्लोट को सी/सी ++ में एक फ्लोट में कैसे परिवर्तित करें] (http://stackoverflow.com/questions/3130169/how-to-convert-a-hex- फ्लोट-टू-ए-फ्लोट-इन-सीसी-उपयोग-मिमी-निकालने-ps-sse-gcc-instr) –
यहां मेरा उत्तर देखें: http://stackoverflow.com/questions/3130169/how-to -convert-a-hex-float-to-a-float-in-cc-use-mm-extract-ps-sse-gcc-inst –