2010-05-27 21 views
6

के लिए मैं एक पाशSSE SIMD अनुकूलन लूप

for(int i = 0; i < n; i++) 
{ 
    u[i] = c * u[i] + s * b[i]; 
} 

तो में कुछ कोड है, यू और ख एक ही लंबाई के वैक्टर, और ग कर रहे हैं और रों scalars हैं। क्या यह कोड स्पीडअप प्राप्त करने के लिए एसएसई के साथ उपयोग के लिए वेक्टरेशन के लिए एक अच्छा उम्मीदवार है?

अद्यतन

मैं vectorization सीखा है और SSE में मेरे पाश लागू किया (यह इतनी मेहनत करता है, तो आप intrinsics का उपयोग नहीं है पता चला है)। हालांकि, वीसी ++ कंपाइलर में एसएसई 2 ध्वज सेट करते समय, मुझे अपने स्वयं के एसएसई कोड के समान प्रदर्शन मिलता है। दूसरी तरफ इंटेल कंपाइलर मेरे एसएसई कोड या वीसी ++ कंपाइलर से बहुत तेज था।

यहाँ कोड मैं संदर्भ

double *u = (double*) _aligned_malloc(n * sizeof(double), 16); 
for(int i = 0; i < n; i++) 
{ 
    u[i] = 0; 
} 

int j = 0; 
__m128d *uSSE = (__m128d*) u; 
__m128d cStore = _mm_set1_pd(c); 
__m128d sStore = _mm_set1_pd(s); 
for (j = 0; j <= i - 2; j+=2) 
{ 
    __m128d uStore = _mm_set_pd(u[j+1], u[j]); 

    __m128d cu = _mm_mul_pd(cStore, uStore); 
    __m128d so = _mm_mul_pd(sStore, omegaStore); 

    uSSE[j/2] = _mm_add_pd(cu, so); 
} 
for(; j <= i; ++j) 
{ 
    u[j] = c * u[j] + s * omegaCache[j]; 
} 
+0

[नोट VC11 अब अपने अनुकूलन में SIMD का उपयोग करता है] (http://blogs.microsoft.co.il/blogs/sasha/archive/2011/10/17/simd-optimized-c-code-in -विज़ुअल-स्टूडियो-11.aspx) – bobobobo

उत्तर

5

हां, यह वेक्टरेशन के लिए एक उत्कृष्ट उम्मीदवार है। लेकिन, ऐसा करने से पहले, सुनिश्चित करें कि आपने यह सुनिश्चित करने के लिए अपना कोड प्रोफाइल किया है कि यह वास्तव में अनुकूलित करने योग्य है। जिसके अनुसार, vectorization कुछ इस तरह जाना होगा:

int i; 
for(i = 0; i < n - 3; i += 4) 
{ 
    load elements u[i,i+1,i+2,i+3] 
    load elements b[i,i+1,i+2,i+3] 
    vector multiply u * c 
    vector multiply s * b 
    add partial results 
    store back to u[i,i+1,i+2,i+3] 
} 

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4) 
for(; i < n; i++) 
    u[i] = c * u[i] + s * b[i]; 

भी अधिक प्रदर्शन के लिए, आप आगे सरणी तत्वों प्रीफ़ेचिंग विचार कर सकते हैं, और/या पाश unrolling और software pipelining का उपयोग कर स्मृति के साथ एक पाश में गणना बिछा को एक अलग पुनरावृत्ति से उपयोग करता है।

+0

निश्चित रूप से इस कोड को एक बाधा के रूप में मिला। यह जांचने के लिए एक सवाल है कि मुझे वेक्टरिंग सीखना और कार्यान्वित करना एक बर्बाद प्रयास नहीं है - कंपाइलर्स आमतौर पर इस तरह के कोड को स्वचालित रूप से सदिश नहीं करेंगे? –

+1

@ प्रोजेक्टाइल अगर आप एलियासिंग के बारे में कंपाइलर को बताते हैं, तो आमतौर पर यह होगा। अपने अनुभव से, संकलक से बहुत महत्वपूर्ण प्रयास किए बिना बेहतर कोड उत्पन्न करना बहुत असामान्य है। – Anycorn

-1

के लिए लिखा था कि यह कैसे आप यू और ख स्मृति में रखा पर निर्भर करता है। यदि दोनों मेमोरी ब्लॉक एक-दूसरे से दूर हैं, तो एसएसई इस परिदृश्य में ज्यादा बढ़ावा नहीं देगा।

यह सुझाव दिया जाता है कि एसओए (सरणी की संरचना) के बजाय सरणी यू और बी एओई (संरचना की सरणी) हैं, क्योंकि आप दोनों को एकल निर्देश में पंजीकरण में लोड कर सकते हैं।

+1

मैं इस बात से असहमत हूं कि यहां एक एओएस का उपयोग एसओए पर फायदेमंद होगा। आप अभी भी प्रत्येक स्टोर के लिए 2 लोड कर रहे हैं, और एओएस के साथ अब आपको प्रत्येक 4 इकाइयों में से केवल 2 लिखना होगा। एसओए के साथ, आप 'यू' से 4 इकाइयों को लोड कर सकते हैं, 4' बी 'से 4, और उसके बाद 4 को वापस' यू 'लिख सकते हैं बिना किसी शफलिंग या मास्किंग करने की आवश्यकता के। –

+0

दोनों बिंदुओं पर यिनफी के साथ असहमत। एसओए आमतौर पर ऊर्ध्वाधर सिम के लिए बेहतर है। मेमोरी दूरी कैशिंग के कारण एक प्रासंगिक कारक नहीं है - भले ही एओएस कम कैश लाइनों का उपयोग करने की अनुमति देता है (उदाहरण के लिए अत्यधिक संरेखण पैडिंग की वजह से, जो सिमडाइजेशन के लिए अच्छे उम्मीदवार में होने की संभावना नहीं है), फिर भी उनको पुन: स्थापित करना बेहतर होगा कैश लाइन एसओए होने के लिए। – mabraham

1

शायद हां, लेकिन आपको कुछ संकेतों के साथ कंपाइलर की मदद करनी है। __restrict__ पॉइंटर्स पर रखे गए संकलक को बताते हैं कि दो पॉइंटर्स के बीच कोई उपनाम नहीं है। यदि आप अपने वैक्टरों के संरेखण को जानते हैं, तो संकलक को संवाद करें (विज़ुअल सी ++ में कुछ सुविधा हो सकती है)।

मैं विजुअल सी ++ से परिचित नहीं हूं, लेकिन मैंने सुना है कि यह वेक्टरेशन के लिए अच्छा नहीं है। इसके बजाय इंटेल कंपाइलर का उपयोग करने पर विचार करें। इंटेल उत्पन्न असेंबली पर बहुत बढ़िया नियंत्रण की अनुमति देता है: http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm

+1

जो इंटेल प्रोसेसर को खुद से बेहतर जानते हैं? :) – YeenFei

1

_mm_set_pd वेक्टरकृत नहीं है। यदि शाब्दिक रूप से लिया जाता है, तो यह स्केलर परिचालनों का उपयोग करके दो युगल पढ़ता है, फिर दो स्केलर युगल जोड़ता है और उन्हें एसएसई रजिस्टर में कॉपी करता है। इसके बजाय _mm_load_pd का उपयोग करें।

1

हां, यह वेक्टरिजटन के लिए एक महान उम्मीदवार है, मानते हुए कि यू और बी सरणी का कोई ओवरलैप नहीं है। लेकिन कोड मेमोरी एक्सेस (लोड/स्टोर) से बंधे हैं। वेक्टरेशन प्रति लूप चक्र को कम करने में मदद करता है, लेकिन यू और बी सरणी पर कैश-मिस के कारण निर्देश बंद हो जाएंगे। इंटेल सी/सी ++ कंपाइलर ज़ीऑन x5500 प्रोसेसर के लिए डिफ़ॉल्ट ध्वज के साथ निम्न कोड उत्पन्न करता है। संकलक 8 तक लूप को अनलोल करता है और xmm [0-16] सिम रजिस्टरों का उपयोग करके सिमड एडीडी (एडीपीडी) और बहुपक्षीय (mulpd) निर्देशों को नियोजित करता है। प्रत्येक चक्र में, प्रोसेसर 4-तरफा स्केलर आईएलपी प्रदान करने वाले 2 सिमड निर्देश जारी कर सकता है, मानते हैं कि आपके पास रजिस्टरों में डेटा तैयार है।

यहां यू, बी, सी और एस डबल प्रेसिजन (8 बाइट्स) हैं।

..B1.14:      # Preds ..B1.12 ..B1.10 
    movaps %xmm1, %xmm3         #5.1 
    unpcklpd %xmm3, %xmm3         #5.1 
    movaps %xmm0, %xmm2         #6.12 
    unpcklpd %xmm2, %xmm2         #6.12 
     # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3 
    ..B1.15:  # Preds ..B1.15 ..B1.14 
    movsd  (%rsi,%rcx,8), %xmm4       #6.21 
    movhpd 8(%rsi,%rcx,8), %xmm4       #6.21 
    mulpd  %xmm2, %xmm4         #6.21 
    movaps (%rdi,%rcx,8), %xmm5       #6.12 
    mulpd  %xmm3, %xmm5         #6.12 
    addpd  %xmm4, %xmm5         #6.21 
    movaps 16(%rdi,%rcx,8), %xmm7      #6.12 
    movaps 32(%rdi,%rcx,8), %xmm9      #6.12 
    movaps 48(%rdi,%rcx,8), %xmm11      #6.12 
    movaps %xmm5, (%rdi,%rcx,8)       #6.3 
    mulpd  %xmm3, %xmm7         #6.12 
    mulpd  %xmm3, %xmm9         #6.12 
    mulpd  %xmm3, %xmm11         #6.12 
    movsd  16(%rsi,%rcx,8), %xmm6      #6.21 
    movhpd 24(%rsi,%rcx,8), %xmm6      #6.21 
    mulpd  %xmm2, %xmm6         #6.21 
    addpd  %xmm6, %xmm7         #6.21 
    movaps %xmm7, 16(%rdi,%rcx,8)      #6.3 
    movsd  32(%rsi,%rcx,8), %xmm8      #6.21 
    movhpd 40(%rsi,%rcx,8), %xmm8      #6.21 
    mulpd  %xmm2, %xmm8         #6.21 
    addpd  %xmm8, %xmm9         #6.21 
    movaps %xmm9, 32(%rdi,%rcx,8)      #6.3 
    movsd  48(%rsi,%rcx,8), %xmm10      #6.21 
    movhpd 56(%rsi,%rcx,8), %xmm10      #6.21 
    mulpd  %xmm2, %xmm10         #6.21 
    addpd  %xmm10, %xmm11        #6.21 
    movaps %xmm11, 48(%rdi,%rcx,8)      #6.3 
    addq  $8, %rcx          #5.1 
    cmpq  %r8, %rcx          #5.1 
    jl  ..B1.15  # Prob 99%      #5.1 
संबंधित मुद्दे