2010-11-13 27 views
11

का उपयोग कर 4 डॉट उत्पादों को सी में एक संगत सरणी में स्टोर करने का सबसे प्रभावी तरीका एसएसई इंट्रिनिक्स का उपयोग करके इंटेल x86 नेहलेम माइक्रो-आर्किटेक्चर के लिए कुछ कोड अनुकूलित कर रहा हूं।एसएसई इंट्रिनिक्स

मेरे प्रोग्राम का एक हिस्सा 4 डॉट उत्पादों की गणना करता है और प्रत्येक परिणाम को प्रत्येक मान को सरणी के एक संगत खंड में जोड़ता है। विशेष रूप से,

tmp0 = _mm_dp_ps(A_0m, B_0m, 0xF1); 
tmp1 = _mm_dp_ps(A_1m, B_0m, 0xF2); 
tmp2 = _mm_dp_ps(A_2m, B_0m, 0xF4); 
tmp3 = _mm_dp_ps(A_3m, B_0m, 0xF8); 

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 
tmp0 = _mm_add_ps(tmp0, C_0n); 

_mm_storeu_ps(C_2, tmp0); 

सूचना है कि मैं 4 अस्थायी XMM का उपयोग करके इस बारे में जा रहा हूँ प्रत्येक डॉट उत्पाद का परिणाम धारण करने के लिए पंजीकृत करता है।

tmp0 = R0-शून्य-शून्य-शून्य

tmp1 = शून्य: प्रत्येक XMM रजिस्टर में, परिणाम रिश्तेदार अन्य अस्थायी XMM रजिस्टरों ऐसी है कि अंतिम परिणाम इस तरह दिखता है के लिए एक अनूठा 32 बिट में रखा जाता है -R1-शून्य-शून्य

tmp2 = शून्य-शून्य-R2-शून्य

tmp3 = शून्य-शून्य-शून्य-R3

मैं के बाद एक XMM चर में मान प्रत्येक tmp चर में निहित गठबंधन निम्नलिखित निर्देशों के साथ उन्हें संक्षेप में:

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 

अंत में, मैं एक सरणी का एक सन्निहित भाग को डॉट उत्पादों के सभी 4 परिणाम युक्त ताकि सरणी के अनुक्रमित तो की तरह, एक डॉट उत्पाद से बढ़ती है, रजिस्टर जोड़ने (C_0n 4 मूल्यों में हैं सरणी जिसे अद्यतन किया जाना है;

tmp0 = _mm_add_ps(tmp0, C_0n); 
_mm_storeu_ps(C_2, tmp0); 

मैं अगर वहाँ डॉट उत्पादों के परिणामों लेने के लिए और उनमें से सटे हिस्सा में जोड़ने के लिए एक कम राउंड के बारे में, अधिक कुशलता से जानना चाहता हूँ: C_2) पता इनमें से 4 मूल्यों की ओर इशारा करते है सरणी इस तरह, मैं रजिस्टरों के बीच 3 जोड़ कर रहा हूं जिनमें केवल 1 गैर-शून्य मान है। ऐसा लगता है कि इसके बारे में जाने का एक और अधिक प्रभावी तरीका होना चाहिए।

मैं सभी मदद की सराहना करता हूं। धन्यवाद।

उत्तर

6

इस तरह के कोड के लिए, मैं ए और बी के "ट्रांसपोज़" को स्टोर करना चाहता हूं, ताकि {A_0m.x, A_1m.x, A_2m.x, A_3m.x} एक वेक्टर आदि में संग्रहीत हो। फिर आप केवल गुणा और जोड़ों का उपयोग करके डॉट उत्पाद कर सकते हैं, और जब आप पूरा कर लेंगे, तो आपके पास बिना किसी शफल के एक वेक्टर में सभी 4 डॉट उत्पाद हैं।

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


मौजूदा कोड पर एक छोटा सा दक्षता ध्यान दें: इस

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 
tmp0 = _mm_add_ps(tmp0, C_0n); 

के बजाय यह थोड़ा बेहतर हो सकता है ऐसा करने के लिए:

tmp0 = _mm_add_ps(tmp0, tmp1); // 0 + 1 -> 0 
tmp2 = _mm_add_ps(tmp2, tmp3); // 2 + 3 -> 2 
tmp0 = _mm_add_ps(tmp0, tmp2); // 0 + 2 -> 0 
tmp0 = _mm_add_ps(tmp0, C_0n); 

पहले दो mm_add_ps के हैं अब पूरी तरह से स्वतंत्र इसके अलावा, मैं बनाम शफलिंग जोड़ने के सापेक्ष समय को नहीं जानता, लेकिन यह थोड़ा तेज़ हो सकता है।


आशा है कि मदद करता है।

1

आप कम शब्द में डॉट उत्पाद परिणाम छोड़ने का प्रयास कर सकते हैं और स्केलर स्टोर सेशन _mm_store_ss का उपयोग करके प्रत्येक एम 128 रजिस्टर से उस फ्लोट को सरणी के उचित स्थान में सहेजने के लिए उपयोग कर सकते हैं। नेहलेम के स्टोर बफर को एक ही पंक्ति पर लगातार लिखना चाहिए और उन्हें बैचों में एल 1 तक पहुंचा देना चाहिए।

ऐसा करने का समर्थक तरीका सेलियन का ट्रांसपोज़ दृष्टिकोण है। एमएसवीसी के _MM_TRANSPOSE4_PS मैक्रो आपके लिए ट्रांसपोजर करेगा।

+0

आपको स्टोर से पहले प्रत्येक डॉट उत्पाद में पुराना मान (C_0n) जोड़ना होगा। वे सभी स्वतंत्र होंगे, इसलिए यह बहुत धीमी नहीं हो सकती है, लेकिन यह बहुत सुंदर नहीं है :) – celion

3

एसएसई 3 हड का उपयोग करना भी संभव है। यह कुछ छोटे परीक्षणों में _dot_ps का उपयोग करने से तेज़ हो गया। यह 4 डॉट उत्पादों को लौटाता है जिन्हें जोड़ा जा सकता है।

static inline __m128 dot_p(const __m128 x, const __m128 y[4]) 
{ 
    __m128 z[4]; 

    z[0] = x * y[0]; 
    z[1] = x * y[1]; 
    z[2] = x * y[2]; 
    z[3] = x * y[3]; 
    z[0] = _mm_hadd_ps(z[0], z[1]); 
    z[2] = _mm_hadd_ps(z[2], z[3]); 
    z[0] = _mm_hadd_ps(z[0], z[2]); 

    return z[0]; 
} 
1

मुझे एहसास है कि यह प्रश्न पुराना है, लेकिन _mm_add_ps का उपयोग क्यों करें? साथ बदलें:

tmp0 = _mm_or_ps(tmp0, tmp1); 
tmp2 = _mm_or_ps(tmp2, tmp3); 
tmp0 = _mm_or_ps(tmp0, tmp2); 

आप शायद _mm_dp_ps विलंबता से कुछ छुपा सकते हैं। पहले _mm_or_ps अंतिम 2 डॉट उत्पादों की प्रतीक्षा नहीं करता है, और यह एक (तेज़) बिट-वार ऑपरेशन है। अंत में:

_mm_storeu_ps(C_2, _mm_add_ps(tmp0, C_0)); 
संबंधित मुद्दे