2013-02-19 64 views
15

मैं एसएसई का उपयोग कर वेक्टर (यू) के साथ 4x4 मैट्रिक्स (एम) गुणा के सबसे कुशल कार्यान्वयन को खोजने का प्रयास कर रहा हूं। मेरा मतलब है म्यू = वीएसएसई के साथ कुशल 4x4 मैट्रिक्स वेक्टर गुणा: क्षैतिज जोड़ और डॉट उत्पाद - बिंदु क्या है?

जहां तक ​​मैं समझता हूँ कि वहाँ इस बारे में जाने के लिए दो प्राथमिक तरीके हैं:।

method 1) v1 = dot(row1, u), v2 = dot(row2, u), v3 = dot(row3, u), v4 = dot(row4, u) 
    method 2) v = u1 col1 + u2 col2 + u3 col3 + u4 col4. 

विधि 2 SSE2 में लागू करने के लिए आसान है। विधि 1 को एसएसई 3 में क्षैतिज एड निर्देश या एसएसई 4 में डॉट उत्पाद निर्देश के साथ कार्यान्वित किया जा सकता है। हालांकि, मेरे सभी परीक्षण विधि 2 में हमेशा विधि से बेहतर प्रदर्शन होता है 1.

एक जगह जहां मैं विधि 1 का लाभ प्राप्त करता हूं, 3x4 मैट्रिक्स में होता है, उदाहरण के लिए एफ़िन ट्रांसफॉर्म के लिए। इस मामले में अंतिम बिंदु उत्पाद अनावश्यक है। लेकिन यहां तक ​​कि 4x4 मैट्रिक्स पर विधि 2 विधि 3x4 मैट्रिक्स पर विधि 1 से तेज है। एकमात्र विधि जो मैंने पाया है कि 4x4 मैट्रिक्स पर विधि 2 से तेज है, 4x3 मैट्रिक्स पर विधि 2 है।

तो क्षैतिज जोड़ और डॉट उत्पाद निर्देश का बिंदु क्या है? वास्तव में डॉट उत्पादन निर्देश इस मामले में सबसे खराब प्रदर्शन देता है। शायद डेटा के प्रारूप के साथ कुछ करने के लिए है? यदि कोई परिभाषित नहीं कर सकता कि मैट्रिक्स का आदेश कैसे दिया जाता है तो एक हस्तांतरण आवश्यक है और उस स्थिति में शायद विधि 1 बेहतर होगा?

कुछ कोड के लिए नीचे देखें।

__m128 m4x4v_colSSE(const __m128 cols[4], const __m128 v) { 
    __m128 u1 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(0,0,0,0)); 
    __m128 u2 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(1,1,1,1)); 
    __m128 u3 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(2,2,2,2)); 
    __m128 u4 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(3,3,3,3)); 

    __m128 prod1 = _mm_mul_ps(u1, cols[0]); 
    __m128 prod2 = _mm_mul_ps(u2, cols[1]); 
    __m128 prod3 = _mm_mul_ps(u3, cols[2]); 
    __m128 prod4 = _mm_mul_ps(u4, cols[3]); 

    return _mm_add_ps(_mm_add_ps(prod1, prod2), _mm_add_ps(prod3, prod4)); 
} 

__m128 m4x4v_rowSSE3(const __m128 rows[4], const __m128 v) { 
    __m128 prod1 = _mm_mul_ps(rows[0], v); 
    __m128 prod2 = _mm_mul_ps(rows[1], v); 
    __m128 prod3 = _mm_mul_ps(rows[2], v); 
    __m128 prod4 = _mm_mul_ps(rows[3], v); 

    return _mm_hadd_ps(_mm_hadd_ps(prod1, prod2), _mm_hadd_ps(prod3, prod4)); 
} 

__m128 m4x4v_rowSSE4(const __m128 rows[4], const __m128 v) { 
    __m128 prod1 = _mm_dp_ps (rows[0], v, 0xFF); 
    __m128 prod2 = _mm_dp_ps (rows[1], v, 0xFF); 
    __m128 prod3 = _mm_dp_ps (rows[2], v, 0xFF); 
    __m128 prod4 = _mm_dp_ps (rows[3], v, 0xFF); 

    return _mm_shuffle_ps(_mm_movelh_ps(prod1, prod2), _mm_movelh_ps(prod3, prod4), _MM_SHUFFLE(2, 0, 2, 0)); 
} 

उत्तर

10

क्षैतिज जोड़ने और डॉट उत्पाद निर्देश जटिल कर रहे हैं: वे एक से अधिक सरल microoperations जो सिर्फ सरल निर्देशों की तरह प्रोसेसर द्वारा क्रियान्वित कर रहे हैं में विघटित कर रहे हैं। माइक्रोऑपरेशंस में क्षैतिज ऐड और डॉट उत्पाद निर्देशों का सटीक अपघटन प्रोसेसर-विशिष्ट है, लेकिन हाल ही में इंटेल प्रोसेसर क्षैतिज जोड़ के लिए 2 शूफेल + 1 एडीडी माइक्रोप्रेशंस में विघटित हो गया है, और डॉट उत्पाद को 1 एमयूएल + 1 शूफेल + 2 में माइक्रोप्रेशंस में विघटित किया गया है। बड़ी संख्या में माइक्रोप्रेशेशंस के अलावा, यह निर्देश प्रोसेसर पाइपलाइन में निर्देश डिकोडर पर भी जोर देते हैं: इंटेल प्रोसेसर प्रति चक्र केवल एक जटिल निर्देश (4 सरल निर्देशों की तुलना में) को डीकोड कर सकता है। एएमडी बुलडोजर पर इन जटिल निर्देशों की सापेक्ष लागत भी अधिक है।

+0

धन्यवाद, यह बताता है कि निर्देश धीमे क्यों हैं। हालांकि, यह स्पष्ट नहीं करता कि उन्हें क्यों लागू किया गया था। लेकिन मुझे लगता है कि अब मुझे पता है। विधि 2 के लिए डेटा को सरणी (एसओए) की संरचना की आवश्यकता होती है, यानी इष्टतम होने के लिए आदेश दिया गया है। यदि डेटा structs (एओएस) की एक सरणी है, यानी पंक्ति का आदेश दिया गया है, तो एक हस्तांतरण किया जाना चाहिए और इस मामले में विधि 1 बहुत तेज़ है। दूसरे शब्दों में यदि डेटा को परिभाषित किया जा सकता है तो इसे एओएस के बजाय एक SOA बनाएं और विधि 2 का उपयोग करें। अन्यथा क्षैतिज जोड़ के साथ विधि 1 का उपयोग करें। मैट्रिक्स गुणा के लिए डॉट उत्पादन निर्देश का उपयोग न करें। –

+1

सीपीयू विक्रेताओं के पास नए निर्देश जोड़ने का इतिहास है जो बहुत उपयोगी हो सकता है, लेकिन प्रारंभ में उन्हें लागू करने के लिए बहुत कम हार्डवेयर समर्पित कर रहा है। यदि वे पर्याप्त कार्यक्रमों द्वारा अपनाए जाते हैं, तो अंत में वे वास्तव में निर्देश को तेजी से बनाने के लिए और अधिक हार्डवेयर जोड़ते हैं। पहली पीढ़ी ''mm_dp_ps'' ऐसा करने के लिए सामान्य एसएसई या एसएसई 3 दृष्टिकोण से वास्तव में कोई तेज़ नहीं है, हालांकि सिद्धांत में यह बहुत कम कोड-ब्लोट होना चाहिए यदि आप उनमें से बहुत कुछ कर रहे हैं। –

+0

यदि आप इंटेल इंट्रिनिक्स गाइड देखते हैं: [link] (https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=SSE3,SSE4_1&cats=Arithmetic&expand=2737,2084), तो आप प्रदर्शन आंकड़े देखते हैं। इससे यह भी समझने में मदद मिलनी चाहिए कि डीडी-सॉल्यूशन हैड-सॉल्यूशन द्वारा भी बेहतर प्रदर्शन कर रहा है। – St0fF