2012-04-03 14 views
8

का उपयोग करके विशाल सी सरणी के साथ स्पीड समस्या मुझे बफर (लगभग 20gig) में बड़ी मात्रा में डेटा पढ़ने की आवश्यकता है। मेरे पास 1 9 2 जीबी बहुत तेज डीडीआरएम उपलब्ध है, इसलिए स्मृति आकार के साथ कोई समस्या नहीं है। हालांकि, मुझे लगता है कि निम्न कोड धीमा और धीमा चलता है और आगे बफर में आता है। विजुअल सी प्रोफाइलर मुझे बताता है कि 12 मिनट के निष्पादन समय का 68% myFunc() में लूप के अंदर 2 बयानों में है। मैं 2 सीपीयू, 6 भौतिक कोर प्रत्येक (24 लॉजिकल कोर) के साथ बहुत तेज डेल पर 647, 64 बिट चला रहा हूं, और इसे चलाने के दौरान सभी 24 कोर पूरी तरह से अधिकतम हो गए हैं।64 बिट विज़ुअल सी

#define TREAM_COUNT 9000 
#define ARRAY_SIZE ONE_BILLION 

#define offSet(a,b,c,d) (((size_t) ARRAY_SIZE * (a)) + ((size_t) TREAM_COUNT * 800 * (b)) + ((size_t) 800 * (c)) + (d)) 

void myFunc(int dogex, int ptxIndex, int xtreamIndex, int carIndex) 
{ 
    short *ptx = (short *) calloc(ARRAY_SIZE * 20, sizeof(short)); 

    #pragma omp parallel for 
    for (int bIndex = 0; bIndex < 800; ++bIndex) 
      doWork(dogex, ptxIndex, carIndex); 
} 

void doWork(int dogex, int ptxIndex, int carIndex) 
{ 

    for (int treamIndex = 0; treamIndex < ONE_BILLION; ++treamIndex) 
    { 
     short ptxValue  = ptx[ offSet(dogex, ptxIndex, treamIndex, carIndex) ]; 
     short lastPtxValue = ptx[ offSet(dogex, ptxIndex-1, treamIndex, carIndex) ]; 

     // .... 
    } 

} 
+0

आप गुणाओं से छुटकारा पाने के द्वारा कोड को अनुकूलित कर सकते हैं (या तो स्थानांतरित या जोड़कर)। लेकिन फिर भी वह लूप की धीमी गति से आपकी समस्या को हल नहीं कर सकता है। – thumbmunkeys

+0

आप लूप में ptxValue और lastPtxValue क्यों निर्दिष्ट कर रहे हैं? दोनों असाइनमेंट लूपिंग से स्वतंत्र प्रतीत होते हैं। – ArjunShankar

+0

मेरी माफ़ी ... कोड को सरल बनाने की कोशिश में, मुझे यह गलत मिला (संपादित संस्करण ऊपर है)। 'फॉर' लूप के अंदर एक बदलती हुई कीमत है, यही कारण है कि कैल्क्स को बार-बार किया जाना चाहिए। – PaeneInsula

उत्तर

6

कोड एक अरब शॉर्टकट के 20 ब्लॉक आवंटित किया गया। एक 64-बिट विंडोज बॉक्स पर, एक छोटा int 2 बाइट है। तो आवंटन ~ 40 गीगाबाइट है।

आप कहते हैं कि 24 कोर हैं और वे सभी अधिकतम हो गए हैं। जैसा कि कोड है, समानांतरता दिखाने के लिए प्रतीत नहीं होता है। जिस तरह से कोड समानांतर है, प्रदर्शन पर गहरा असर हो सकता है। आपको अधिक जानकारी प्रदान करने की आवश्यकता हो सकती है।

-

आपका मूल समस्या, मुझे लगता है, कैश व्यवहार और स्मृति का उपयोग सीमा के आसपास घूमती है।

सबसे पहले, प्रत्येक छह कोर के दो भौतिक CPUs के साथ, आप पूरी तरह से अपनी मेमोरी बस को संतृप्त करेंगे। शायद आपके पास एक NUMA आर्किटेक्चर है, लेकिन आपके कॉलक() आवंटित करने के बारे में कोड में कोई नियंत्रण नहीं है (उदा। आप स्मृति में संग्रहीत बहुत सारे कोड प्राप्त कर सकते हैं जिसके लिए एकाधिक होप्स तक पहुंचने की आवश्यकता होती है)।

हाइपरथ्रेडिंग चालू है। यह प्रभावी रूप से कैश आकार को रोकता है। कोड को देखते हुए गणना बाध्य की बजाय मेमोरी बस बाध्य है, हाइपरथ्रेडिंग हानिकारक है। (ऐसा कहकर, अगर गणना लगातार कैश सीमा से बाहर है, तो यह ज्यादा नहीं बदलेगा)।

यह स्पष्ट नहीं है (कुछ/अधिक?) कोड हटा दिया गया है, कैसे सरणी का उपयोग किया जा रहा है और कैश ऑप्टिमाइज़ेशन का सम्मान करने के लिए उस पैटर्न का एक्सेस पैटर्न और ऑप्टिमाइज़ेशन प्रदर्शन की कुंजी है।

मुझे क्या लगता है कि ऑफसेट() को कैसे लगाया जाता है यह है कि कोड को लगातार नए आभासी की भौतिक पता लुकअप की पीढ़ी की आवश्यकता होती है - जिनमें से प्रत्येक को चार या पांच मेमोरी एक्सेस की आवश्यकता होती है। यह अपने आप से प्रदर्शन प्रदर्शन कर रहा है।

मेरी मूल सलाह स्तर 2 कैश आकार के ब्लॉक में सरणी को तोड़ देगी, प्रत्येक सीपीयू को एक ब्लॉक दें और इसे उस ब्लॉक को संसाधित करने दें। आप इसे समानांतर में कर सकते हैं। असल में, आप कैश को प्री-लोड करने के लिए हाइपरथ्रेडिंग का उपयोग करने में सक्षम हो सकते हैं, लेकिन यह एक और अधिक उन्नत तकनीक है।

+0

क्या आपने '#pragma omp समानांतर 'नोट किया था? इस उत्तर को पोस्ट करने के बाद – valdo

+0

userxxxx ने इसे जोड़ा। – Gui13

+0

आपके उत्तर के लिए धन्यवाद। क्या आप जानते हैं कि मैं इन चीजों के बारे में और कहां पढ़ सकता हूं (उदाहरण के लिए, मेमोरी बस सीमा, एल 2 और एल 3 कैश मेमोरी आवंटन आदि के संबंध में आकार बदल रहा है)? धन्यवाद। – PaeneInsula

2

यह अनुकूलन धीमी गति से गुणा से छुटकारा मिल जाएगा:

... 
    int idx1 = offSet(dogex, ptxIndex, 0, carIndex); 
    int idx2 = offSet(dogex, ptxIndex-1, 0, carIndex); 

    for (int treamIndex = 0; treamIndex < ONE_BILLION; ++treamIndex) 
    {    
     short ptxValue  = ptx[ idx1 ]; 
     short lastPtxValue = ptx[ idx2 ]; 
     idx1+=800; 
     idx2+=800;    ... 
+1

+1। हालांकि मुझे लगता है कि यह एमयूएल नहीं है जो इसे धीमा कर देता है, लेकिन स्मृति से लोड। – ArjunShankar

+0

हां यह भी समझा नहीं जाता है कि समय के साथ कोड धीमा क्यों होगा, लेकिन यह एक सुधार है :) – thumbmunkeys

+0

हाँ। इसलिए: +1 – ArjunShankar

2

आप और अधिक रैखिक फैशन में सरणी तक पहुँचने के लिए यदि संभव हो तो कोशिश करनी चाहिए। यह शायद कैश मिस की अत्यधिक मात्रा का कारण बनता है।

2

मुझे लगता है कि इस कोड की समस्या इसकी स्मृति पहुंच पैटर्न है। सभी धागे स्मृति के एक ही हिस्से, जो एल 2/L3 कैश में पहले से लोड और कुशलता से है का उपयोग शुरू में

  1. : तथ्य यह है कि प्रत्येक थ्रेड बड़े (2 * 800 बाइट्स) में स्मृति वेतन वृद्धि तक पहुँचता 2 नकारात्मक परिणाम है हर धागे द्वारा प्रयोग किया जाता है। बाद में, धागे थोड़ा अलग गति से आगे बढ़ते हैं और स्मृति के विभिन्न टुकड़ों तक पहुंचते हैं, जिसके परिणामस्वरूप कैश कचरा होता है (एक थ्रेड डेटा को कैश करने के लिए लोड करता है और वहां से डेटा को उजागर करता है, जिसे अभी तक अन्य धागे द्वारा पढ़ा नहीं जाता था)।नतीजतन, स्मृति का एक ही टुकड़ा कैश को कई बार पढ़ा जाता है (सबसे खराब मामले में, 12 बार, एक सीपीयू में धागे की संख्या से)। चूंकि मेमोरी बस अपेक्षाकृत धीमी है, यह पूरे कार्यक्रम को धीमा कर देती है।
  2. एल 1 कैश का भी बहुत कुशलता से उपयोग नहीं किया जाता है: प्रत्येक कैश लाइन में डेटा का केवल छोटा हिस्सा CPU कोर द्वारा उपयोग किया जाता है।

समाधान प्रत्येक धागा (offSet(a,b,c,d) मैक्रो की c और d तर्कों का आदान प्रदान की तरह) स्मृति क्रमिक रूप से उपयोग करने के लिए अनुमति देने के लिए है।

संबंधित मुद्दे