2012-09-19 19 views
23

मैंने कई एसएसई कंपाइलर इंट्रिनिक्स का उपयोग करके एक 3 डी वेक्टर क्लास लिखा है। सब कुछ ठीक काम करता है जब तक कि मैंने 3 डी वेक्टर वाले वर्गों को नए के साथ सदस्य के रूप में स्थापित करना शुरू नहीं किया। मैंने रिलीज मोड में अजीब दुर्घटनाओं का अनुभव किया लेकिन डीबग मोड में नहीं और दूसरी तरफ।एसएसई, इंट्रिनिक्स, और संरेखण

तो मैंने कुछ लेख पढ़े और अनुमान लगाया कि मुझे 3 डी वेक्टर वर्ग के उदाहरण के 16 वर्गों के लिए कक्षाओं को संरेखित करने की आवश्यकता है। तो मैं सिर्फ इतना की तरह वर्गों के सामने _MM_ALIGN16 (__declspec(align(16)) कहा:

_MM_ALIGN16 struct Sphere 
{ 
    // .... 

    Vector3 point; 
    float radius 
}; 

कि पहली बार में इस मुद्दे को हल करने के लिए लग रहा था। लेकिन कुछ कोड बदलने के बाद मेरा प्रोग्राम फिर से अजीब तरीके से दुर्घटनाग्रस्त हो गया। मैंने वेब पर कुछ और खोज की और blog आलेख मिला। मैंने कोशिश की कि लेखक, अर्न्स्ट हॉट ने समस्या को हल करने के लिए क्या किया और यह मेरे लिए भी काम करता है। मैं नया जोड़ा गया है और इस तरह मेरी वर्गों के लिए ऑपरेटरों को हटा दें:

_MM_ALIGN16 struct Sphere 
{ 
    // .... 

    void *operator new (unsigned int size) 
    { return _mm_malloc(size, 16); } 

    void operator delete (void *p) 
    { _mm_free(p); } 

    Vector3 point; 
    float radius 
}; 

अर्नस्ट कहा गया है कि इस aproach के साथ-साथ कई समस्याएं खड़ी कर सकता है, लेकिन वह सिर्फ एक मंच है जो समझा क्यों यह समस्याग्रस्त किया जा सकता बिना अब मौजूद नहीं है से जोड़ता है।

तो मेरी प्रश्न हैं:

  1. ऑपरेटरों परिभाषित करने के साथ समस्या क्या है?

  2. कक्षा परिभाषा में _MM_ALIGN16 क्यों नहीं जोड़ रहा है?

  3. एसएसई इंट्रिनिक्स के साथ आने वाले संरेखण के मुद्दों को संभालने का सबसे अच्छा तरीका क्या है?

+0

पहले मामले में, क्या आप अपने ढांचे को ढेर या ढेर पर आवंटित कर रहे हैं? मुझे यकीन नहीं है कि मॉलोक डिफ़ॉल्ट रूप से गठबंधन स्मृति को लौटाता है, जबकि _mm_malloc निश्चित रूप से होगा - "कुछ समय बाद मेरा प्रोग्राम फिर से क्रैश करना शुरू हुआ" का क्या मतलब है? क्या इसका मतलब यह है कि इसे थोड़ा सा चलने के बाद (और यह क्या कर रहा था)? – Thomas

+0

समस्या तब शुरू हुई जब मैंने ढेर पर structs आवंटित करना शुरू किया। "थोड़ी देर के बाद" वाक्य के साथ मेरा मतलब है कि कोड बदलने के बाद यह दुर्घटनाग्रस्त हो गया। मुझे लगता है कि संरेखण दुर्घटना से सही था और फिर मैंने इसे नष्ट कर दिया। मुझे लगता है कि मॉलोक स्मृति 16 बाइट गठबंधन वापस नहीं करता है जो मुझे लगता है कि समस्या है। मेरा सवाल वास्तव में ऑपरेटर दृष्टिकोण के साथ समस्या क्या है और एसएसई इंट्रिनिक्स का उपयोग कर कोड प्रबंधित करने का सबसे अच्छा तरीका क्या है। –

+2

वास्तव में आपको 'क्षेत्र 'के संरेखण को निर्दिष्ट करने की आवश्यकता नहीं है (इस' _MM_ALIGN16' चीज़ का उपयोग करके), क्योंकि संकलक पर्याप्त रूप से स्मार्ट है कि 'क्षेत्र' में 16-गठबंधन सदस्य हैं और स्वचालित रूप से 'क्षेत्र' को समायोजित करते हैं। एस संरेखण आवश्यकताओं (दिया गया है कि 'वेक्टर 3' सही ढंग से गठबंधन है)। यही कारण है कि आपको 'वेक्टर 3' को स्पष्ट रूप से संरेखित करने की आवश्यकता नहीं है, अगर उसके पास पहले से ही '__m128' सदस्य है। यह केवल गतिशील आवंटन है जो एक समस्या है और इसे 'ऑपरेटर न्यू/डिलीट' ओवरलोडिंग से दूर किया जा सकता है, जैसे कि ब्लॉग में लिखे गए (और आमतौर पर अतिरिक्त चीजें, जैसे 'std :: allocator')। –

उत्तर

18

पहले:

  • स्टेटिक आवंटन। स्वचालित चर के लिए सही ढंग से गठबंधन करने के लिए, आपके प्रकार को उचित संरेखण विनिर्देश की आवश्यकता होती है (उदा। __declspec(align(16)), __attribute__((aligned(16))), या आपके _MM_ALIGN16)। लेकिन सौभाग्य से आपको केवल इसकी आवश्यकता है अगर प्रकार के सदस्यों (यदि कोई हो) द्वारा संरेखण आवश्यकताओं पर्याप्त नहीं हैं। इसलिए आपको Sphere के लिए इसकी आवश्यकता नहीं है, यह देखते हुए कि आपका Vector3 पहले से ही ठीक से संरेखित है। और यदि आपके Vector3 में __m128 सदस्य है (जो कि काफी संभावना है, अन्यथा मैं ऐसा करने का सुझाव दूंगा), तो आपको Vector3 के लिए इसकी आवश्यकता भी नहीं है। इसलिए आपको आमतौर पर कंपाइलर विशिष्ट संरेखण विशेषताओं के साथ गड़बड़ नहीं करना पड़ता है।

  • गतिशील आवंटन। आसान भाग के लिए बहुत कुछ। समस्या यह है कि सी ++ निम्नतम स्तर पर, किसी भी गतिशील स्मृति आवंटित करने के लिए एक प्रकार का अज्ञात स्मृति आवंटन फ़ंक्शन का उपयोग करता है। यह केवल सभी मानक प्रकारों के लिए उचित संरेखण की गारंटी देता है, जो 16 बाइट्स हो सकता है लेकिन इसकी गारंटी नहीं है।

    इसके लिए आपको अपनी खुद की मेमोरी आवंटन को लागू करने के लिए बिल्टिन operator new/delete को अधिभारित करना होगा और अच्छे पुराने malloc के बजाय हुड के नीचे एक गठबंधन आवंटन फ़ंक्शन का उपयोग करना होगा। ओवरलोडिंग operator new/delete अपने आप पर एक विषय है, लेकिन यह मुश्किल नहीं है क्योंकि यह पहले लग सकता है (हालांकि आपका उदाहरण पर्याप्त नहीं है) और आप इसके बारे में this excellent FAQ question में पढ़ सकते हैं।

    दुर्भाग्यवश आपको प्रत्येक प्रकार के लिए ऐसा करना है जिसमें किसी भी सदस्य को गैर-मानक संरेखण की आवश्यकता है, आपके मामले में Sphere और Vector3 दोनों। लेकिन आप इसे थोड़ा आसान बनाने के लिए क्या कर सकते हैं बस उन ऑपरेटरों के लिए उचित अधिभार के साथ एक खाली बेस क्लास बनाएं और फिर इस बेस क्लास से सभी आवश्यक कक्षाएं प्राप्त करें।

    क्या ज्यादातर लोगों को कभी-कभी भूल जाते हैं कि मानक संभाजक std::alocator सभी स्मृति आवंटन के लिए वैश्विक operator new का उपयोग करता है, इसलिए अपने प्रकार के मानक कंटेनर के साथ काम नहीं करेगा (और एक std::vector<Vector3> कि दुर्लभ एक उपयोग के मामले नहीं है)। आपको अपने मानक अनुरूप अनुरूप आवंटक बनाने और इसका उपयोग करने की आवश्यकता है। लेकिन सुविधा और सुरक्षा के लिए यह वास्तव में बेहतर है कि आप अपने प्रकार के लिए std::allocator विशेषज्ञ (शायद इसे अपने कस्टम आवंटक के रूप में प्राप्त कर लें) को विशेषज्ञता दें ताकि इसका हमेशा उपयोग किया जा सके और जब भी आप उपयोग करते हैं तो उचित आवंटक का उपयोग करने की आपको आवश्यकता नहीं है std::vector। दुर्भाग्य से इस मामले में आपको फिर से प्रत्येक गठबंधन प्रकार के लिए विशेषज्ञ बनाना होगा, लेकिन एक छोटी बुराई मैक्रो उसमें मदद करता है।

    इसके अतिरिक्त आपको और std::return_temporary_buffer जैसे वैश्विक operator new/delete का उपयोग करके अन्य चीजों को देखना होगा, और यदि आवश्यक हो तो उनकी देखभाल करें।

दुर्भाग्य से अभी तक उन समस्याओं के लिए एक बहुत अच्छा तरीका नहीं है, मुझे लगता है, जब तक आप एक मंच है कि मूल रूप से 16 को संरेखित करता है पर हैं और इस के बारे में पता। या आप प्रत्येक मेमोरी ब्लॉक को 16 बाइट्स पर हमेशा संरेखित करने के लिए वैश्विक operator new/delete को अधिभारित कर सकते हैं और एसएसई सदस्य वाले प्रत्येक वर्ग के संरेखण की देखभाल करने से मुक्त हो सकते हैं, लेकिन मुझे इस दृष्टिकोण के प्रभावों के बारे में पता नहीं है। सबसे बुरे मामले में इसे केवल स्मृति को बर्बाद करने के परिणामस्वरूप होना चाहिए, लेकिन फिर आप आमतौर पर सी ++ में गतिशील रूप से छोटी वस्तुओं को आवंटित नहीं करते हैं (हालांकि std::list और std::map इस बारे में अलग-अलग सोच सकते हैं)। स्थिर स्मृति __declspec(align(16)) जैसी चीजों का उपयोग करने का सही संयोजन के लिए

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

    तो योग करने के लिए।

  • ओवरलोड operator new/delete गैर-मानक संरेखण आवश्यकताओं वाले प्रत्येक सदस्य के लिए ओवरलोड operator new/delete

  • गठबंधन प्रकार के मानक कंटेनर में उपयोग करने के लिए एक कंसस्टॉम मानक-अनुरूप आलोकेटर बनाएं, या बेहतर अभी तक, प्रत्येक गठबंधन प्रकार के लिए std::allocator का विशेषज्ञ बनाएं।


अंत में

कुछ सामान्य सलाह। अक्सर आप कई वेक्टर संचालन करते समय गणना-भारी ब्लॉक में एसएसई लाभ कमाते हैं। इन सभी संरेखण समस्याओं को सरल बनाने के लिए, विशेष रूप से Vector3 युक्त प्रत्येक प्रकार के संरेखण की देखभाल करने की समस्याएं, यह एक विशेष एसएसई वेक्टर प्रकार बनाने के लिए एक अच्छा अपमान हो सकता है और सामान्य गैर-प्रयोग करके सामान्य गणना के अंदर ही इसका उपयोग कर सकता है भंडारण और सदस्य चर के लिए एसएसई वेक्टर।

+0

क्या सी ++ 11 से 'std :: aligned_storage' विशेषज्ञ कॉल सम्मेलनों की आवश्यकता के बिना यह सब सक्षम करेगा? –

+1

@ graham.reeds 'alignas' कीवर्ड की बजाय। 'std :: aligned_storage' की वास्तव में आवश्यकता नहीं है, क्योंकि '__m128' पहले से ही ठीक से गठबंधन है और आप' std :: aligned_storage' सदस्य के बजाय '__m128' सदस्य चाहते हैं। लेकिन निश्चित रूप से, 'alignas'' __declspec (align()) '(या जो भी gcc पसंद करता है) कहने का नया प्लेटफॉर्म स्वतंत्र तरीका है, भले ही उनमें से कोई भी आमतौर पर आवश्यक न हो। लेकिन यह सब केवल स्थिर स्मृति संरेखण के लिए मदद करता है। –

1
  1. समस्या ऑपरेटरों के साथ कि खुद को से वे पर्याप्त नहीं कर रहे हैं है। वे स्टैक आवंटन को प्रभावित नहीं करते हैं, जिसके लिए आपको अभी भी __declspec(align(16)) की आवश्यकता है।

  2. __declspec(align(16)) इस बात को प्रभावित करता है कि संकलक स्मृति में वस्तुओं को कैसे स्थानांतरित करता है, अगर और केवल तभी विकल्प हो। नई वस्तुओं के लिए, संकलक के पास operator new द्वारा लौटाई गई स्मृति का उपयोग करने के अलावा कोई विकल्प नहीं है।

  3. आदर्श रूप से, एक कंपाइलर का उपयोग करें जो उन्हें मूल रूप से संभालता है। कोई सैद्धांतिक कारण नहीं है कि उन्हें double से अलग तरीके से इलाज करने की आवश्यकता क्यों है। अन्यथा, वर्कअराउंड के लिए कंपाइलर दस्तावेज़ पढ़ें। प्रत्येक विकलांग संकलक के पास अपने स्वयं के मुद्दों का सेट होगा और इसलिए अपने स्वयं के कामकाज का सेट होगा।

+0

धन्यवाद! क्रिश्चियन राउ की टिप्पणी से मैं लेता हूं कि '__declspec (संरेखण (16)) अप्रचलित है। मुझे लगता है कि हिस्सा संकलक पर निर्भर करता है? मुझे यकीन नहीं है कि मैं आपके उत्तर के 3. भाग को समझता हूं। 'उन्हें मूल रूप से संभालने' से आपका क्या मतलब है। मैं संकलक का उपयोग करता हूं जो विजुअल स्टूडियो 2012 एक्सप्रेस के साथ आता है। –

+1

@ फेयरडिंकम थिंकम: मेरा मतलब है "एक कंपाइलर जो उन्हें मूल रूप से संभालता है" एक संकलक है जो एसएसई प्रकारों को संरेखित कर सकता है जैसे कि एफपी प्रकारों को संरेखित करता है, यानी प्रोग्रामर से सहायता के बिना। आपको उन लोगों के लिए '__declspec (संरेखण (8)) की आवश्यकता नहीं है। मेरे पास वीएस2012 नहीं है इसलिए मैं निश्चित रूप से यह नहीं कह सकता कि यह पहले से ही स्मार्ट है या नहीं। – MSalters

+1

आपको सबसे अधिक कस्टम आवंटन को भी लागू करना होगा। –

2

मूल रूप से, आप क्योंकि SIMD वेक्टर प्रकार सामान्य रूप से प्रकार में निर्मित में से किसी से भी बड़ा संरेखण आवश्यकताओं है सुनिश्चित करें कि आपके वैक्टर ठीक से संरेखित कर रहे हैं बनाने की जरूरत है।

निम्नलिखित बातें कर रही है की आवश्यकता है कि:

  1. सुनिश्चित करें कि Vector3 सही तरीके से संरेखित जब यह ढेर या एक संरचना के एक सदस्य पर है। यह __attribute__((aligned(32))) से Vector3 कक्षा (या जो भी विशेषता आपके कंपाइलर द्वारा समर्थित है) लागू करके किया जाता है। ध्यान दें, आपको Vector3 युक्त संरचनाओं में विशेषता लागू करने की आवश्यकता नहीं है, जो आवश्यक नहीं है और पर्याप्त नहीं है (यानी।इसे Sphere पर लागू करने की आवश्यकता नहीं है)।

  2. सुनिश्चित करें कि Vector3 या इसकी संलग्न संरचना ढेर आवंटन का उपयोग करते समय ठीक से गठबंधन की गई है। यह सादा malloc() या operator new() का उपयोग करने के बजाय posix_memalign() (या अपने प्लेटफार्म के लिए एक समान कार्य) का उपयोग करके किया जाता है क्योंकि बाद वाले दो अंतर्निर्मित प्रकारों (आमतौर पर 8 या 16 बाइट्स) के लिए स्मृति को संरेखित करते हैं जिन्हें सिम प्रकार के लिए पर्याप्त होने की गारंटी नहीं है ।

    तुम सब स्मृति आवंटन के दो प्रकार के लिए देखभाल करने के लिए है की
संबंधित मुद्दे