2048x2048 बनाम 2047x2047 सरणी गुणन में भारी प्रदर्शन क्यों है?


127

मैं कुछ मैट्रिक्स गुणा बेंचमार्किंग कर रहा हूं, जैसा कि पहले उल्लेख किया गया है कि मैट्रिक्स गुणन में MATLAB इतनी तेजी से क्यों है?

अब मुझे एक और मुद्दा मिला है, जब दो 2048x2048 मैट्रिसेस को गुणा करते हुए, C # और अन्य के बीच एक बड़ा अंतर है। जब मैं केवल 2047x2047 मैट्रिस गुणा करता हूं, तो यह सामान्य लगता है। मजबूरी के लिए कुछ और लोगों को भी जोड़ा।

1024x1024 - 10 सेकंड।

1027x1027 - 10 सेकंड।

2047x2047 - 90 सेकंड।

2048x2048 - 300 सेकंड।

2049x2049 - 91 सेकंड। (अपडेट करें)

2500x2500 - 166 सेकंड

कि 2k मामले के लिए 2k के लिए साढ़े तीन मिनट का अंतर है।

2dim सरणियों का उपयोग करना

//Array init like this
int rozmer = 2048;
float[,] matice = new float[rozmer, rozmer];

//Main multiply code
for(int j = 0; j < rozmer; j++)
{
   for (int k = 0; k < rozmer; k++)
   {
     float temp = 0;
     for (int m = 0; m < rozmer; m++)
     {
       temp = temp + matice1[j,m] * matice2[m,k];
     }
     matice3[j, k] = temp;
   }
 }

23
यह एक उन्नत स्तर की सी प्रोग्रामिंग या ओएस डिज़ाइन वर्ग के लिए एक महान परीक्षा प्रश्न होगा ;-)
द साने

क्या आपने दोनों बहुआयामी [,] और दांतेदार [] [] सरणियों के साथ-साथ ३२ और ६४ बिट का परीक्षण करने की कोशिश की है? मैंने केवल कुछ ही बार परीक्षण किया लेकिन दांतेदार अपने परिणामों के साथ अधिक इन-लाइन लग रहा था लेकिन दांतेदार 64 बिट उच्च थे, मुझे नहीं पता कि क्या जीट में कोई उत्तराधिकार हैं जो इस स्थिति पर लागू होते हैं या यदि इसका कैश पहले से सुझाए अनुसार संबंधित है। यदि आप एक GPGPU समाधान चाहते हैं तो research.microsoft.com/en-us/projects/accelerator है जो आपके अन्य पोस्ट में समय के साथ प्रतिस्पर्धी होना चाहिए।
क्रिश

कुछ हद तक भोला सवाल, लेकिन दो वर्ग मैट्रिसेस को गुणा करने में कितने ऑप्स (जोड़ना / गुणा करना) शामिल हैं?
निक टी

जवाबों:


61

यह संभवतः आपके एल 2 कैश में संघर्ष के साथ है।

मैटिस 1 पर कैश मिस नहीं होती क्योंकि वे क्रमिक रूप से एक्सेस किए जाते हैं। हालांकि matice2 के लिए अगर L2 में एक पूर्ण स्तंभ फिट बैठता है (यानी जब आप matice2 [0, 0], matice2 [1, 0], matice2 [2, 0] ... आदि का उपयोग करते हैं, तो कुछ भी निष्कासित नहीं होता) से कोई समस्या नहीं है या तो matice2 के साथ कैश की याद आती है।

अब गहराई में जाने के लिए कि कैश कैसे काम करता है, यदि आपके चर का बाइट पता X है, तो इसके लिए कैश लाइन की तुलना में (X >> 6) & (L - 1) होगा। जहां L आपके कैश में कुल कैश लाइनों की संख्या है। L हमेशा की शक्ति है। छह इस तथ्य से आता है कि 2 ^ 6 == 64 बाइट्स कैश लाइन का मानक आकार है।

अब इसका क्या मतलब है? वैसे इसका मतलब यह है कि अगर मेरा पता X है और पता Y और (X >> 6) - (Y >> 6) L से विभाज्य है (यानी 2 की कोई बड़ी शक्ति), तो उन्हें उसी कैशलाइन में संग्रहीत किया जाएगा।

अब अपनी समस्या पर वापस जाने के लिए 2048 और 2049 के बीच क्या अंतर है,

जब 2048 आपका आकार है:

अगर आप & matice2 [x, k] और matice2 [y, k] का अंतर लेते हैं (और matice2 [x, k] >> 6) - (और matice2 [y, k] >> >> 6) 2048 * 4 (आकार) से विभाज्य हो जाएगा की नाव)। तो 2 की एक बड़ी शक्ति।

इस प्रकार आपके L2 के आकार के आधार पर आपके पास बहुत सारी कैश लाइन की उलझनें होंगी, और कॉलम को स्टोर करने के लिए अपने L2 के छोटे हिस्से का उपयोग करें, इस प्रकार आप वास्तव में अपने कैश में पूर्ण कॉलम को स्टोर करने में सक्षम नहीं होंगे, इस प्रकार आपको खराब प्रदर्शन मिलेगा ।

जब आकार 2049 है, तो अंतर 2049 * 4 है जो 2 की शक्ति नहीं है, इस प्रकार आपके पास कम संघर्ष होंगे और आपका कॉलम सुरक्षित रूप से आपके कैश में फिट होगा।

अब इस सिद्धांत का परीक्षण करने के लिए कुछ चीजें हैं जो आप कर सकते हैं:

इस matice2 [razmor, 4096] की तरह अपनी सरणी matice2 सरणी आवंटित करें, और razmor = 1024, 1025 या किसी भी आकार के साथ चलाएं, और आपको पहले की तुलना में बहुत खराब प्रदर्शन देखना चाहिए। ऐसा इसलिए है क्योंकि आप सभी स्तंभों को एक दूसरे के साथ संघर्ष करने के लिए बलपूर्वक संरेखित करते हैं।

फिर matice2 [razmor, 4097] आज़माएं और इसे किसी भी आकार के साथ चलाएं और आपको बहुत बेहतर प्रदर्शन देखना चाहिए।


क्या आपने अपने पिछले 2 पैराग्राफ में गलती की थी? दोनों कोशिशें बिल्कुल एक जैसी हैं। :)
Xeo

कैश एसोसिएटिविटी भी एक भूमिका निभाती है।
बेन जैक्सन

20

शायद एक कैशिंग प्रभाव। मैट्रिक्स आयामों के साथ जो दो की बड़ी शक्तियां हैं, और एक कैशे आकार है जो दो की शक्ति भी है, आप अपने एल 1 कैश के एक छोटे से हिस्से का उपयोग करके केवल चीजों को धीमा कर सकते हैं। Naive मैट्रिक्स गुणा आमतौर पर कैश में डेटा लाने की आवश्यकता से विवश होता है। टाइलिंग (या कैश-गुमनामी एल्गोरिदम) का उपयोग करके अनुकूलित एल्गोरिदम एल 1 कैश का बेहतर उपयोग करने पर ध्यान केंद्रित करते हैं।

यदि आप अन्य जोड़े (2 ^ n-1,2 ^ n) का समय देते हैं, तो मुझे उम्मीद है कि आप इसी तरह के प्रभाव देखेंगे।

अधिक पूरी तरह से समझाने के लिए, आंतरिक लूप में, जहां आप matice2 [m, k] का उपयोग करते हैं, संभावना है कि matice2 [m, k] और matice2 [m + 1, k] 2048 * sizeof (float) द्वारा एक दूसरे से ऑफसेट होते हैं और इस प्रकार L1 कैश में एक ही इंडेक्स पर मैप करें। एन-वे एसोसिएटिव कैश के साथ आपके पास आमतौर पर इन सभी के लिए 1-8 कैश स्थान होंगे। इस प्रकार लगभग सभी एक्सेस एक L1 कैश बेदखली को ट्रिगर करेंगे, और धीमे कैश या मुख्य मेमोरी से डेटा प्राप्त करेंगे।


+1। संभावना लगती है। एक को कैश एसोसिएटिविटी से सावधान रहना होगा।
मैके

16

यह आपके cpu कैश के आकार के साथ करना पड़ सकता है। यदि मैट्रिक्स मैट्रिक्स की 2 पंक्तियाँ फिट नहीं होती हैं, तो आप रैम से तत्वों में स्वैपिंग का समय कम कर देंगे। अतिरिक्त 4095 तत्व केवल पंक्तियों को फिटिंग से रोकने के लिए पर्याप्त हो सकते हैं।

आपके मामले में, 2047 2d मैट्रिसेस के लिए 2 पंक्तियाँ 16KB मेमोरी (32 बिट प्रकार मानते हुए) के भीतर आती हैं। उदाहरण के लिए, यदि आपके पास 64KB का L1 कैश (बस में सीपीयू के सबसे नजदीक) है, तो आप एक बार में कम से कम 4 पंक्तियों (2047 * 32) को कैश में फिट कर सकते हैं। लंबी पंक्तियों के साथ अगर कोई पेडिंग आवश्यक है जो पंक्तियों के जोड़े को 16KB से आगे बढ़ाता है, तो चीजें गड़बड़ होने लगती हैं। इसके अलावा, हर बार जब आप कैश को मिस करते हैं, तो किसी अन्य कैश या मुख्य मेमोरी से डेटा स्वैपिंग चीजों को डिलीट कर देता है।

मेरा अनुमान है कि अलग-अलग आकार के मेट्रिसेस के साथ आप जो रन रन समय में देख रहे हैं, वह इस बात से प्रभावित होता है कि ऑपरेटिंग सिस्टम कितनी आसानी से उपलब्ध कैश का उपयोग कर सकता है (और कुछ संयोजन बस समस्याग्रस्त हैं)। निश्चित रूप से यह मेरी ओर से सभी का सरलीकरण है।


2
लेकिन यह बहुत संभावना नहीं है कि उसके पास 16.7 एमबी का सीपीयू कैश है
मैरिनो

मैंने 2049x2049 - 91 सेकंड के साथ परिणाम अपडेट किए। यदि यह "कैश की समस्या" थी, तो क्या यह अभी भी 300+ नहीं होना चाहिए?
वुल्फ

@ मेरिनो जवाब को अपडेट किया गया है ताकि इसे ध्यान में रखा जा सके।
द साने

1
मुझे ऐसा लगता है कि इनमें से कोई भी स्पष्टीकरण विभिन्न और विरल आकारों के बारे में नए विवरणों को पर्याप्त रूप से संबोधित नहीं कर सकता है जो समस्या से अलग हैं, दूसरों के साथ अप्रभावित होने के बीच।
केन रॉकॉट

2
मुझे नहीं लगता कि यह स्पष्टीकरण सही है। यह समस्या कैश की क्षमता का पूरी तरह से उपयोग नहीं करने के कारण कैशे लाइन के टकराव के कारण होती है, जब आकार 2 की शक्ति है। हार्डवेयर में। OS का डेटा संरेखण के साथ कुछ करना है, लेकिन इस मामले में यह सब है कि कैसे C # डेटा आवंटित करने का निर्णय लेता है और मेमोरी में 2D सरणी का प्रतिनिधित्व कैसे करता है, OS का इससे कोई लेना-देना नहीं है।

10

लुई ब्रांडी ने इस मुद्दे का विश्लेषण करते हुए दो ब्लॉग पोस्ट लिखे:

अधिक कैश पागलपन और कम्प्यूटेशनल प्रदर्शन - एक शुरुआती मामला कुछ दिलचस्प आंकड़ों के साथ अध्ययन करता है और व्यवहार को अधिक विस्तार से समझाने का प्रयास करता है, यह वास्तव में कैश आकार सीमाओं के नीचे आता है।


5

यह देखते हुए कि समय बड़े आकार में गिर रहा है, यह कैश संघर्ष होने की अधिक संभावना नहीं होगी, विशेष रूप से समस्याग्रस्त मैट्रिक्स आकारों के लिए 2 की शक्तियों के साथ? मैं कैशिंग मुद्दों पर कोई विशेषज्ञ नहीं हूं, लेकिन यहां कैश संबंधित प्रदर्शन मुद्दों पर उत्कृष्ट जानकारी है


कैश एसोसिएटिविटी पर लिंक की धारा 5 विशेष रूप से लागू होती है।
द साने

4

जब आप matice2सरणी को लंबवत रूप से एक्सेस कर रहे हैं , तो इसे कैश के अंदर और बाहर स्वैप किया जाएगा। यदि आप सरणी को तिरछे दर्पण करते हैं, ताकि आप [k,m]इसके बजाय इसका उपयोग कर सकें, तो [m,k]कोड बहुत तेज़ी से चलेगा।

मैंने 1024x1024 मैट्रिस के लिए यह परीक्षण किया, और यह लगभग दोगुना है। 2048x2048 मैट्रिस के लिए यह लगभग दस गुना तेज है।


यह स्पष्ट नहीं करता है कि 2049 2048 से अधिक तेज़ क्यों है।
मैके

@ मैके: ऐसा इसलिए है क्योंकि यह मेमोरी कैशिंग में कुछ सीमा पार कर जाता है, जिससे कि बहुत अधिक कैश मिस हो जाते हैं।
गुफ़ा

क्यों होता है पतन? यदि आप यह नहीं कहते कि आप क्या सोचते हैं तो यह गलत है, इससे उत्तर में सुधार नहीं हो सकता।
गुफा

किसी भी स्पष्टीकरण के बिना एक और गिरावट ... क्या यह है कि मेरे उत्तर में बहुत कम "शायद", "अनुमान" और "चाहिए", जैसे उन उत्तरों को जो सबसे अधिक उत्थान प्राप्त करते हैं ...?
गुफ़ा

4

कैश अलियासिंग

या कैश थ्रैशिंग , अगर मैं एक शब्द को गढ़ा जा सकता है।

कैश कम ऑर्डर बिट्स के साथ अनुक्रमण द्वारा और उच्च ऑर्डर बिट्स के साथ टैगिंग द्वारा काम करते हैं।

इमेजिंग करना कि आपके कैश में 4 शब्द हैं और आपका मैट्रिक्स 4 x 4 है। जब एक कॉलम एक्सेस किया जाता है और पंक्ति दो में से किसी भी पावर की होती है, तो मेमोरी में प्रत्येक कॉलम एलिमेंट उसी कैश एलिमेंट में मैप होगा।

इस समस्या के लिए एक पावर ऑफ टू-प्लस-वन वास्तव में इष्टतम के बारे में है। प्रत्येक नया कॉलम एलिमेंट अगले कैश स्लॉट में मैप करेगा, जैसे कि पंक्ति द्वारा एक्सेस करना।

वास्तविक जीवन में, एक टैग कई क्रमिक रूप से बढ़ते पते को कवर करता है जो एक पंक्ति में कई आसन्न तत्वों को कैश करेगा। प्रत्येक नई पंक्ति के बकेट को ऑफसेट करने से, कॉलम को पीछे करने से पिछली प्रविष्टि को प्रतिस्थापित नहीं किया जाता है। जब अगला कॉलम ट्रेस होता है, तो पूरा कैश अलग-अलग पंक्तियों से भरा होगा और प्रत्येक पंक्ति अनुभाग जो कैश में फिट होगा, कई कॉलमों के लिए हिट होगा।

चूंकि कैश DRAM की तुलना में बहुत अधिक तेज है (ज्यादातर ऑन-चिप होने के कारण) हिट रेट सब कुछ है।


2

आप कैश आकार सीमा को हिट करते हैं, या शायद आपके समय में पुनरावृत्ति की कुछ समस्याएं हैं।

जो भी समस्या है, आपको बस C # में मैट्रिक्स गुणन को नहीं लिखना चाहिए और इसके बजाय BLAS के एक अनुकूलित संस्करण का उपयोग करना चाहिए। मैट्रिक्स का वह आकार किसी भी आधुनिक मशीन पर एक सेकंड के अंदर गुणा किया जाना चाहिए।


1
मुझे BLAS के बारे में पता है, लेकिन यह कार्य संभव के रूप में इसे सबसे तेज़ बनाने के लिए नहीं था, लेकिन इसे विभिन्न भाषाओं में लिखना और परीक्षण करना था। यह मेरे लिए बहुत अजीब समस्या है और Iam वास्तव में उत्सुक हैं कि परिणाम वे जैसे क्यों हैं।
वुल्फ

3
@Wolf मुझे इस बारे में उत्साहित होना मुश्किल है कि क्या कुछ ऐसा होना चाहिए जो 90 सेकंड या 300 सेकंड ले रहा है।
डेविड हेफर्नन

4
यह जानने का सबसे अच्छा तरीका है कि कोई चीज़ कैसे काम करती है, इसे स्वयं लिखें और देखें कि आप अपने कार्यान्वयन को कैसे बेहतर बना सकते हैं; यह (उम्मीद है) वुल्फ क्या कर रहा है।
कैलम रोजर्स

@ कल्लुम रोजर्स, सहमत। इस तरह मैंने फाइल कॉपी ऑपरेशंस में बफर साइज का महत्व सीखा।
केली एस। फ्रेंच

1

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


हम्म - अभी तक 2 डी के रूप में घोषित एक सरणी (फ्लोट [,] मैटिस = नया फ्लोट [रोज़र, रोज़मर];) केवल रैम में एक आयामी सरणी और हुड के नीचे की गई पंक्ति / स्ट्राइड गणना के रूप में आवंटित किया गया है। तो क्यों इसे 1D घोषित करना और मैन्युअल पंक्ति / स्ट्राइड गणना करना अधिक तेज़ होगा? क्या आपका मतलब यह है कि छोटे टाइलों के सरणी के रूप में एक बड़ा सरणी आवंटित किया जाता है, जिनमें से प्रत्येक कैश में फिट हो सकता है जहां बड़ा सरणी नहीं होगा?
एरिक एम

1
यदि आपका पुस्तकालय या जो भी उपकरण आप उपयोग कर रहे हैं वह टाइलिंग करता है, तो आपको इसकी आवश्यकता नहीं है। लेकिन अगर आप C / C ++ में पारंपरिक 2D सरणी का उपयोग करने के लिए थे, तो टाइलिंग प्रदर्शन में सुधार करेगी।
अर्लेन

0

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

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


1
गलत। 2049 2048 से अधिक तेज़ है, जो आपके दावे का खंडन करता है।
मैके

@ मैके: यह काफी संभव है। लेकिन इस बात की थोड़ी सी संभावना है कि उनके प्रोसेसर में इस्तेमाल की जाने वाली कैश पॉलिसी अभी भी इस निराशा को कम कर सकती है। यह बहुत संभावना में नहीं है, लेकिन यह अकल्पनीय नहीं है।
Automatico
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.