बीएलएएस को इस तरह का चरम प्रदर्शन कैसे मिलता है?


108

जिज्ञासा से बाहर मैंने अपने मैट्रिक्स गुणन फ़ंक्शन बनाम BLAS कार्यान्वयन को बेंचमार्क करने का निर्णय लिया ... मुझे कहना था कि परिणाम पर सबसे कम आश्चर्य हुआ:

कस्टम कार्यान्वयन, 1000x1000 मैट्रिक्स गुणन के 10 परीक्षण:

Took: 15.76542 seconds.

BLAS कार्यान्वयन, 1000x1000 मैट्रिक्स गुणन के 10 परीक्षण:

Took: 1.32432 seconds.

यह एकल परिशुद्धता फ्लोटिंग पॉइंट नंबरों का उपयोग कर रहा है।

मेरा कार्यान्वयन:

template<class ValT>
void mmult(const ValT* A, int ADim1, int ADim2, const ValT* B, int BDim1, int BDim2, ValT* C)
{
    if ( ADim2!=BDim1 )
        throw std::runtime_error("Error sizes off");

    memset((void*)C,0,sizeof(ValT)*ADim1*BDim2);
    int cc2,cc1,cr1;
    for ( cc2=0 ; cc2<BDim2 ; ++cc2 )
        for ( cc1=0 ; cc1<ADim2 ; ++cc1 )
            for ( cr1=0 ; cr1<ADim1 ; ++cr1 )
                C[cc2*ADim2+cr1] += A[cc1*ADim1+cr1]*B[cc2*BDim1+cc1];
}

मेरे दो सवाल हैं:

  1. यह देखते हुए कि एक मैट्रिक्स-मैट्रिक्स गुणन कहता है: nxm * mxn के लिए n * n * m गुणन की आवश्यकता होती है, इसलिए 1000 ^ 3 या 1e9 ऑपरेशन से ऊपर के मामले में। BLAS को 1.32 सेकंड में 10 * 1e9 संचालन करने के लिए मेरे 2.6Ghz प्रोसेसर पर यह कैसे संभव है? भले ही गुणा एक एकल ऑपरेशन था और कुछ और नहीं किया जा रहा था, इसे ~ 4 सेकंड लगना चाहिए।
  2. मेरा कार्यान्वयन इतना धीमा क्यों है?

17
बीएलएएस को एक तरफ और दूसरे को क्षेत्र में विशेषज्ञ द्वारा अनुकूलित किया गया है। मुझे लगता है कि यह आपके चिप पर SIMD फ्लोटिंग पॉइंट यूनिट का फायदा उठा रहा है और कैशिंग व्यवहार को बेहतर बनाने के लिए बहुत सारे ट्रिक्स खेल रहा है ...
dmckee --- ex-मॉडरेटर kitten

3
फिर भी आप 2.63E9 चक्र / सेकंड प्रोसेसर पर 1.3 सेकंड में 1E10 ऑपरेशन कैसे करते हैं?
DeusAduro

9
एकाधिक निष्पादन इकाइयाँ, पाइप-लाइनिंग और सिंगल इंस्ट्रक्शन मल्टीपल डेटा ((SIMD) जिसका अर्थ है एक ही समय में एक से अधिक युग्मों पर एक ही ऑपरेशन करना)। कुछ कंपाइलर SIMD इकाइयों को आम चिप्स पर लक्षित कर सकते हैं, लेकिन आपको बस हमेशा स्पष्ट रूप से चालू करना होगा, और यह जानने में मदद करता है कि यह कैसे काम करता है ( en.wikipedia.org/wiki/SIMD )। कैश मिस के खिलाफ बीमा करना निश्चित रूप से कठिन हिस्सा है।
dmckee --- पूर्व-मध्यस्थ ने

13
धारणा गलत है। बेहतर एल्गोरिदम ज्ञात हैं, विकिपीडिया देखें।
MSalters

2
@DeusAduro: कैसे मैट्रिक्स मैट्रिक्स उत्पाद लिखने के लिए मेरे उत्तर में जो ईजेन के साथ प्रतिस्पर्धा कर सकता है? मैंने कैश कुशल मैट्रिक्स-मैट्रिक्स उत्पाद को लागू करने के तरीके पर एक छोटा सा उदाहरण पोस्ट किया।
माइकल लेहन

जवाबों:


141

रॉबर्ट ए। वैन डे गीजन और एनरिक एस। क्विंटाना-ऑर्टि द्वारा एक अच्छी शुरुआत बिंदु द साइंस ऑफ प्रोग्रामिंग प्रोग्रामिंग मैट्रिक्स कंप्यूटर्स है। वे एक मुफ्त डाउनलोड संस्करण प्रदान करते हैं।

BLAS को तीन स्तरों में विभाजित किया गया है:

  • स्तर 1 रैखिक बीजगणित कार्यों के एक सेट को परिभाषित करता है जो केवल वैक्टर पर काम करते हैं। ये फ़ंक्शंस वैश्वीकरण (जैसे SSE का उपयोग करने से) से लाभान्वित होते हैं।

  • स्तर 2 फ़ंक्शन मैट्रिक्स-वेक्टर ऑपरेशन हैं, उदाहरण के लिए कुछ मैट्रिक्स-वेक्टर उत्पाद। इन कार्यों को Level1 कार्यों के संदर्भ में लागू किया जा सकता है। हालाँकि, आप इस फ़ंक्शन के प्रदर्शन को बढ़ा सकते हैं यदि आप एक समर्पित कार्यान्वयन प्रदान कर सकते हैं जो साझा मेमोरी के साथ कुछ मल्टीप्रोसेसर आर्किटेक्चर का उपयोग करता है।

  • स्तर 3 फ़ंक्शन मैट्रिक्स-मैट्रिक्स उत्पाद की तरह संचालन हैं। फिर से आप उन्हें Level2 कार्यों के संदर्भ में कार्यान्वित कर सकते हैं। लेकिन Level3 फ़ंक्शन O (N ^ 2) डेटा पर O (N ^ 3) ऑपरेशन करते हैं। इसलिए यदि आपके प्लेटफ़ॉर्म में कैश पदानुक्रम है तो आप प्रदर्शन को बढ़ावा दे सकते हैं यदि आप एक समर्पित कार्यान्वयन प्रदान करते हैं जो कैश ऑप्टिमाइज़्ड / कैश फ्रेंडली है । यह पुस्तक में अच्छी तरह से वर्णित है। Level3 फ़ंक्शन का मुख्य बढ़ावा कैश ऑप्टिमाइज़ेशन से आता है। यह बढ़ावा समानांतरता और अन्य हार्डवेयर अनुकूलन से दूसरे बढ़ावा को काफी हद तक बढ़ा देता है।

वैसे, उच्च प्रदर्शन BLAS कार्यान्वयन के अधिकांश (या यहां तक ​​कि सभी) फोरट्रान में लागू नहीं होते हैं। C.L में ATLAS को लागू किया जाता है। GotoBLAS / OpenBLAS को C में लागू किया जाता है और असेंबलर में इसका प्रदर्शन महत्वपूर्ण होता है। केवल फोर्टिस में बीएलएएस का संदर्भ कार्यान्वयन लागू है। हालाँकि, ये सभी BLAS कार्यान्वयन एक फोरट्रान इंटरफ़ेस प्रदान करते हैं, ताकि इसे LAPACK (LAPACK द्वारा अपने सभी प्रदर्शन से लाभ प्राप्त किया जा सके) से जोड़ा जा सके।

इस संबंध में ऑप्टिमाइज्ड कंपाइलर एक छोटी भूमिका निभाते हैं (और गोटोब्लास / ओपनब्लास के लिए कंपाइलर बिल्कुल भी मायने नहीं रखता है)।

IMHO कोई BLAS कार्यान्वयन कोपरसमिथ-विनोग्राद एल्गोरिथ्म या स्ट्रैसन एल्गोरिथम जैसे एल्गोरिदम का उपयोग करता है। मैं कारण के बारे में बिल्कुल निश्चित नहीं हूं, लेकिन यह मेरा अनुमान है:

  • हो सकता है कि इन एल्गोरिदम का कैश अनुकूलित कार्यान्वयन प्रदान करना संभव न हो (यानी आप अधिक ढीले होंगे तो आप जीत जाएंगे)
  • ये एल्गोरिदम संख्यात्मक रूप से स्थिर नहीं हैं। चूंकि BLAS, LAPACK का कम्प्यूटेशनल कर्नेल है, यह एक नो-गो है।

संपादित करें / अद्यतन:

इस विषय का नया और ग्राउंड ब्रेकिंग पेपर BLIS पेपर हैं । वे असाधारण रूप से अच्छी तरह से लिखे गए हैं। मेरे व्याख्यान के लिए "उच्च प्रदर्शन कम्प्यूटिंग के लिए सॉफ़्टवेयर मूल बातें" मैंने उनके पेपर के बाद मैट्रिक्स-मैट्रिक्स उत्पाद को लागू किया। वास्तव में मैंने मैट्रिक्स-मैट्रिक्स उत्पाद के कई संस्करण लागू किए। सबसे सरल संस्करण पूरी तरह से सादे सी में लिखे गए हैं और कोड की 450 से कम लाइनें हैं। अन्य सभी संस्करण केवल छोरों का अनुकूलन करते हैं

    for (l=0; l<MR*NR; ++l) {
        AB[l] = 0;
    }
    for (l=0; l<kc; ++l) {
        for (j=0; j<NR; ++j) {
            for (i=0; i<MR; ++i) {
                AB[i+j*MR] += A[i]*B[j];
            }
        }
        A += MR;
        B += NR;
    }

मैट्रिक्स-मैट्रिक्स उत्पाद का समग्र प्रदर्शन केवल इन छोरों पर निर्भर करता है। यहां लगभग 99.9% समय व्यतीत होता है। दूसरे वेरिएंट में मैंने परफॉर्मेंस को बेहतर बनाने के लिए इंट्रिंसिक्स और असेंबलर कोड का इस्तेमाल किया। आप यहाँ सभी प्रकार के ट्यूटोरियल देख सकते हैं:

ulmBLAS: GEMM (मैट्रिक्स-मैट्रिक्स उत्पाद) पर ट्यूटोरियल

BLIS पेपर्स के साथ मिलकर यह समझना काफी आसान हो जाता है कि Intel MKL जैसी लाइब्रेरी इस तरह का प्रदर्शन कैसे हासिल कर सकती हैं। और इससे कोई फर्क नहीं पड़ता कि आप पंक्ति या स्तंभ प्रमुख भंडारण का उपयोग करते हैं!

अंतिम बेंचमार्क यहां हैं (हमने अपनी परियोजना को ulmBLAS कहा है):

UlmBLAS, BLIS, MKL, openBLAS और Eigen के लिए बेंचमार्क

एक और संपादन / अद्यतन:

मैंने कुछ ट्यूटोरियल भी लिखे कि कैसे बीएलएएस को संख्यात्मक रेखीय बीजगणित की समस्याओं के लिए उपयोग किया जाता है जैसे कि रैखिक समीकरणों की एक प्रणाली को हल करना:

उच्च प्रदर्शन एलयू फैक्टराइजेशन

(यह LU फ़ैक्टराइज़ेशन उदाहरण के लिए मैटलैब द्वारा रैखिक समीकरणों की प्रणाली को हल करने के लिए उपयोग किया जाता है।)

मैं समय को खोजने के लिए आशा का वर्णन और तरीका प्रदर्शित करने में की तरह LU गुणन के एक उच्च स्केलेबल समानांतर कार्यान्वयन का एहसास करने के लिए ट्यूटोरियल का विस्तार करने के प्लाज्मा

ठीक है, यहाँ आप जाते हैं: कैशिंग एक अनुकूलित ऑप्टिमाइज़ समानांतर एलयू फैक्टराइजेशन

पुनश्च: मैंने uBLAS के प्रदर्शन को बेहतर बनाने के लिए कुछ प्रयोग किए हैं। यह वास्तव में बढ़ावा देने के लिए बहुत आसान है (हाँ, शब्दों पर खेलें :)) uBLAS का प्रदर्शन:

UBLAS पर प्रयोग

यहाँ BLAZE के साथ एक समान परियोजना है :

ब्लाजी पर प्रयोग


3
नई लिंक "ulmBLAS, BLIS, MKL, openBLAS और Eigen के लिए बेंचमार्क": apfel.mathematik.uni-ulm.de/~lehn/ulmBLAS/#toc3
अहमद फसीह

यह पता चला है कि आईबीएम का ईएसएसएल स्ट्रैसोन एल्गोरिथ्म के भिन्नता का उपयोग करता है - ibm.com/support/knowledgecenter/en/SSFHY8/essl_welcome.html
ben-albrecht

2
अधिकांश लिंक मृत हैं
औरेलियन पियरे

TSoPMC की पीडीएफ लेखक की पृष्ठ पर पाया जा सकता है, पर cs.utexas.edu/users/rvdg/tmp/TSoPMC.pdf
एलेक्स Shpilkin

हालाँकि Coppersmith-Winograd एल्गोरिथ्म में पेपर पर एक अच्छा समय जटिलता है, बिग ओ अंकन एक बहुत बड़े स्थिरांक को छुपाता है, इसलिए यह केवल हास्यास्पद रूप से बड़े मैट्रिसेस के लिए व्यवहार्य होने लगता है।
डाईहार्ड.ट्रायहार्ड

26

इसलिए सबसे पहले बीएलएएस लगभग 50 कार्यों का एक इंटरफ़ेस है। इंटरफ़ेस के कई प्रतिस्पर्धात्मक कार्यान्वयन हैं।

सबसे पहले मैं उन चीजों का उल्लेख करूंगा जो काफी हद तक असंबंधित हैं:

  • फोरट्रान बनाम सी, कोई फर्क नहीं पड़ता
  • उन्नत मैट्रिक्स एल्गोरिदम जैसे स्ट्रैसेन, कार्यान्वयन उन्हें उपयोग नहीं करते हैं क्योंकि वे अभ्यास में मदद नहीं करते हैं

अधिकांश कार्यान्वयन प्रत्येक ऑपरेशन को छोटे आयाम मैट्रिक्स या वेक्टर ऑपरेशन में अधिक या कम स्पष्ट तरीके से तोड़ते हैं। उदाहरण के लिए एक बड़े 1000x1000 मैट्रिक्स गुणा 50x50 मैट्रिक्स गुणा के अनुक्रम में टूट सकता है।

ये निश्चित-आकार के छोटे-छोटे आयाम संचालन (जिसे कर्नेल कहा जाता है) को सीपीयू-विशिष्ट असेंबली कोड में उनके लक्ष्य की कई सीपीयू विशेषताओं का उपयोग करके हार्डकोड किया जाता है:

  • SIMD- शैली निर्देश
  • निर्देश स्तर समानता
  • कैश जागरूकता

इसके अलावा इन गुठली को विशिष्ट मानचित्र-कम डिज़ाइन पैटर्न में कई थ्रेड्स (सीपीयू कोर) का उपयोग करके एक दूसरे के संबंध में समानांतर रूप से निष्पादित किया जा सकता है।

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

(टिप: यही कारण है कि यदि आप एटलस का उपयोग कर रहे हैं, तो आप अपने विशेष मशीन के लिए लाइब्रेरी का निर्माण और हाथ खींचना बेहतर मानते हैं, फिर एक पूर्वनिर्मित का उपयोग करके।)


एटलस अब सबसे अधिक इस्तेमाल किया जाने वाला ओपन सोर्स BLAS कार्यान्वयन नहीं है। इसे ओपनबीएलएएस (गोटोब्लास का एक कांटा) और बीएलआईएस (गोटोब्लास का एक खंडन) ने पीछे छोड़ दिया है।
राबर्ट वैन डे गीजन

1
@ ulaff.net: हो सकता है। यह 6 साल पहले लिखा गया था। मुझे लगता है कि वर्तमान में (इंटेल के पाठ्यक्रम पर) सबसे तेज़ बीएलएएस कार्यान्वयन इंटेल एमकेएल है, लेकिन यह खुला स्रोत नहीं है।
एंड्रयू टॉमज़ोस

14

सबसे पहले, आपके द्वारा उपयोग किए जाने वाले मैट्रिक्स गुणन के लिए अधिक कुशल एल्गोरिदम हैं।

दूसरा, आपका CPU एक समय में एक से अधिक निर्देश कर सकता है।

आपका CPU प्रति चक्र 3-4 निर्देशों को निष्पादित करता है, और यदि SIMD इकाइयों का उपयोग किया जाता है, तो प्रत्येक निर्देश 4 फ्लोट या 2 डबल्स को संसाधित करता है। (बेशक यह आंकड़ा सही नहीं है, क्योंकि सीपीयू आमतौर पर प्रति चक्र केवल एक SIMD निर्देश को संसाधित कर सकता है)

तीसरा, आपका कोड इष्टतम से बहुत दूर है:

  • आप कच्चे पॉइंटर्स का उपयोग कर रहे हैं, जिसका अर्थ है कि संकलक को यह अनुमान लगाना है कि वे उर्फ ​​हो सकते हैं। संकलक-विशिष्ट कीवर्ड या झंडे हैं जो आप संकलक को यह बताने के लिए निर्दिष्ट कर सकते हैं कि वे उपनाम नहीं हैं। वैकल्पिक रूप से, आपको कच्चे पॉइंटर्स के अलावा अन्य प्रकारों का उपयोग करना चाहिए, जो समस्या का ध्यान रखते हैं।
  • आप इनपुट मैट्रिसेस की प्रत्येक पंक्ति / कॉलम का एक भोला-भाला प्रदर्शन करके कैश को फेंक रहे हैं। मैट्रिक्स के एक छोटे से ब्लॉक पर जितना संभव हो उतना काम करने के लिए आप ब्लॉकिंग का उपयोग कर सकते हैं, जो कि सीपीयू कैश में फिट बैठता है, अगले ब्लॉक पर जाने से पहले।
  • विशुद्ध रूप से संख्यात्मक कार्यों के लिए, फोरट्रान बहुत अपराजेय है, और सी ++ एक समान गति तक पहुंचने के लिए बहुत अधिक सहवास करता है। यह किया जा सकता है, और कुछ पुस्तकालय हैं जो इसे प्रदर्शित करते हैं (आमतौर पर अभिव्यक्ति टेम्पलेट्स का उपयोग करते हुए), लेकिन यह तुच्छ नहीं है, और यह बस नहीं होता है।

धन्यवाद, मैंने जस्टिस के सुझाव के अनुसार सही कोड को प्रतिबंधित कर दिया है, बहुत सुधार नहीं देखा है, मुझे ब्लॉकवाइज विचार पसंद है। जिज्ञासा से बाहर, सीपीयू के कैश आकार को जाने बिना एक सही इष्टतम कोड कैसे होगा?
DeusAduro

2
तुम नहीं। इष्टतम कोड प्राप्त करने के लिए, आपको सीपीयू के कैश आकार को जानना होगा। बेशक इसका नकारात्मक पक्ष यह है कि आप CPU के एक परिवार पर सर्वश्रेष्ठ प्रदर्शन के लिए प्रभावी रूप से अपने कोड को हार्डकोड कर रहे हैं ।
जलफ

2
कम से कम यहां का आंतरिक लूप तार के भार से बचा जाता है। ऐसा लगता है कि यह एक मैट्रिक्स के लिए लिखा गया है जिसे पहले से ही ट्रांसपोज़ किया जा रहा है। यही कारण है कि यह BLAS की तुलना में "धीमी" केवल एक परिमाण का क्रम है! लेकिन हाँ, यह अभी भी कैश-ब्लॉकिंग की कमी के कारण जोर दे रहा है। क्या आप सुनिश्चित हैं कि फोरट्रान बहुत मदद करेगा? मुझे लगता है कि आप यहाँ सभी लाभ प्राप्त करेंगे restrict(C (C ++) के विपरीत, कोई भी अन्य चीज़ डिफ़ॉल्ट नहीं है। (और दुर्भाग्यवश ISO C ++ में एक restrictकीवर्ड नहीं है , इसलिए आपको ऐसे __restrict__कंपाइलरों पर उपयोग करना होगा जो इसे एक्सटेंशन के रूप में प्रदान करते हैं)।
पीटर कॉर्ड्स

11

मुझे स्पष्ट रूप से BLAS कार्यान्वयन के बारे में नहीं पता है, लेकिन मैट्रिक्स गुणन के लिए अधिक कुशल alogorithms हैं जो O (n3) जटिलता से बेहतर हैं। एक अच्छी तरह से पता है कि स्ट्रैसेन एल्गोरिथम है


8
स्ट्रैसेन एल्गोरिथम का उपयोग संख्यात्मक कारणों में दो कारणों से नहीं किया जाता है: 1) यह स्थिर नहीं है। 2) आप कुछ संगणनाओं को बचाते हैं लेकिन यह उस मूल्य के साथ आता है जिससे आप कैश पदानुक्रम का फायदा उठा सकते हैं। व्यवहार में आप ढीला प्रदर्शन भी करते हैं।
माइकल Lehn

4
स्ट्रैसेन एल्गोरिथम के व्यावहारिक कार्यान्वयन के लिए बीएलएएस लाइब्रेरी सोर्स कोड पर कसकर बनाया गया है, एससी 16 में " स्ट्रैसेन एलगोरिदम रीलोडेड " : हाल ही में एक प्रकाशन है , जो समस्या से संबंधित डेटा एक्सएक्सएक्स 1000 के लिए भी बीएलएएस से उच्च प्रदर्शन प्राप्त करता है।
जियानयू हुआंग

4

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

    void vector(int m, double ** a, double ** b, double ** c) {
      int i, j, k;
      for (i=0; i<m; i++) {
        double * ci = c[i];
        for (k=0; k<m; k++) ci[k] = 0.;
        for (j=0; j<m; j++) {
          double aij = a[i][j];
          double * bj = b[j];
          for (k=0; k<m; k++)  ci[k] += aij*bj[k];
        }
      }
    }

एक और टिप्पणी: यह कार्यान्वयन मेरे कंप्यूटर पर BLAS रूटीन cblas_dgemm (सभी को अपने कंप्यूटर पर आज़माएं!) की जगह लेने से बेहतर है। लेकिन बहुत तेज (1: 4) सीधे फोरट्रान पुस्तकालय के dgemm_ को बुला रहा है। मुझे लगता है कि यह दिनचर्या वास्तव में फोरट्रान नहीं बल्कि कोडांतरक कोड है (मुझे नहीं पता कि पुस्तकालय में क्या है, मेरे पास स्रोत नहीं हैं)। मेरे लिए पूरी तरह से अस्पष्ट है कि cblas_dgemm मेरे ज्ञान के बाद से इतनी तेजी से नहीं है क्योंकि यह dgemm_ के लिए केवल एक आवरण है।


3

यह एक यथार्थवादी गति है। C ++ कोड पर SIMD कोडांतरक के साथ क्या किया जा सकता है, इसके उदाहरण के लिए, कुछ उदाहरण देखें iPhone मैट्रिक्स फ़ंक्शंस - ये C संस्करण की तुलना में 8x से अधिक तेज़ थे, और असेंबली "अनुकूलित" भी नहीं हैं - अभी तक कोई पाइप-लाइनिंग नहीं है और वहाँ है अनावश्यक स्टैक ऑपरेशन है।

इसके अलावा आपका कोड " प्रतिबंधित सही नहीं है " - कंपाइलर को कैसे पता चलता है कि जब वह C को संशोधित करता है, तो वह A और B को संशोधित नहीं कर रहा है?


सुनिश्चित करें कि यदि आप फ़ंक्शन को mmult (ए ..., ए ..., ए) कहते हैं; आपको निश्चित रूप से अपेक्षित परिणाम नहीं मिलेगा। फिर भी मैं बीएलएएस को हरा / फिर से लागू करने की कोशिश नहीं कर रहा था, बस यह देख रहा था कि यह वास्तव में कितनी तेजी से है, इसलिए त्रुटि की जांच करना मूल कार्यक्षमता नहीं थी।
DeusAduro

3
क्षमा करें, स्पष्ट होने के लिए, जो मैं कह रहा हूं वह यह है कि यदि आप अपने संकेत पर "प्रतिबंधित" लगाते हैं, तो आपको बहुत तेज़ कोड मिलेगा। इसका कारण यह है कि हर बार जब आप सी मोडिफाई करते हैं, तो कंपाइलर को ए और बी को फिर से लोड नहीं करना पड़ता है - नाटकीय रूप से इनर लूप को गति देता है। यदि आप मुझ पर विश्वास नहीं करते हैं, तो डिससैस की जांच करें।
जस्टिकल

@DeusAduro: यह त्रुटि जांच नहीं है - यह संभव है कि कंपाइलर आंतरिक लूप में B [] सरणी तक पहुंच का अनुकूलन करने में असमर्थ है क्योंकि यह पता लगाने में सक्षम नहीं हो सकता है कि ए और सी पॉइंटर्स कभी बी को उर्फ ​​नहीं करते हैं सरणी। यदि उर्फिंग कर रहे थे, तो बी सरणी में मूल्य के लिए संभव होगा, जबकि आंतरिक लूप निष्पादित हो रहा है। आंतरिक लूप से बाहर [बी] के मूल्य तक पहुंच को रोकना और इसे स्थानीय चर में डालने से कंपाइलर को बी [] तक लगातार पहुंच से बचने में सक्षम हो सकता है।
माइकल बूर

1
हम्म्म, इसलिए मैंने पहली बार वीएस 2008 में '__restrict' कीवर्ड का उपयोग करने की कोशिश की, ए, बी और सी पर लागू किया। इससे परिणाम में कोई बदलाव नहीं हुआ। हालाँकि, B तक पहुंच को अंतरतम लूप से बाहर लूप तक ले जाने से समय में सुधार हुआ ~ 10%।
DeusAduro

1
क्षमा करें, मुझे वीसी के बारे में निश्चित नहीं है, लेकिन जीसीसी के साथ आपको सक्षम करने की आवश्यकता है -fstrict-aliasing। यहाँ भी "प्रतिबंधित" की बेहतर व्याख्या है: cellperformance.beyond3d.com/articles/2006/05/…
Justicle

2

एमएम में मूल कोड के संबंध में गुणा, अधिकांश ऑपरेशन के लिए मेमोरी संदर्भ खराब प्रदर्शन का मुख्य कारण है। मेमोरी कैश की तुलना में 100-1000 गुना धीमी गति से चल रही है।

MM में इस ट्रिपल लूप फ़ंक्शन के लिए लूप ऑप्टिमाइज़ेशन तकनीकों को नियोजित करने से सबसे अधिक गति होती है। दो मुख्य लूप अनुकूलन तकनीकों का उपयोग किया जाता है; अनियंत्रित और अवरुद्ध। अनरोलिंग के संबंध में, हम बाहरी दो सबसे लूपों को अनियंत्रित करते हैं और इसे कैश में डेटा पुन: उपयोग के लिए ब्लॉक करते हैं। पूरे ऑपरेशन के दौरान अलग-अलग समय पर एक ही डेटा में मेमोरी रेफरेंस की संख्या को कम करके अस्थायी रूप से आउटर लूप को डेटा-एक्सेस को ऑप्टिमाइज़ करने में मदद मिलती है। लूप इंडेक्स को विशिष्ट संख्या पर ब्लॉक करना, कैश में डेटा को बनाए रखने में मदद करता है। आप L2 कैश या L3 कैश के लिए ऑप्टिमाइज़ करना चुन सकते हैं।

https://en.wikipedia.org/wiki/Loop_nest_optimization


-24

कई कारणों से।

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

एक और बात यह है कि फोरट्रान सामान को सामान रूप से स्टोर करता है, जबकि सी डेटा पंक्ति-वार स्टोर करता है। मैंने आपके कोड की जाँच की, लेकिन आपने उत्पाद कैसे प्रदर्शन किया, इससे सावधान रहें। C में आपको पंक्ति वार स्कैन करना होगा: इस तरह से आप सन्निहित स्मृति के साथ अपने सरणी को स्कैन करते हैं, कैश मिस को कम करते हैं। कैशे मिस अक्षमता का पहला स्रोत है।

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

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

उदाहरण के लिए, आप अपने कोड को इस तरह से काम कर सकते हैं

template<class ValT>
void mmult(const ValT* A, int ADim1, int ADim2, const ValT* B, int BDim1, int BDim2, ValT* C)
{
if ( ADim2!=BDim1 ) throw std::runtime_error("Error sizes off");

memset((void*)C,0,sizeof(ValT)*ADim1*BDim2);
int cc2,cc1,cr1, a1,a2,a3;
for ( cc2=0 ; cc2<BDim2 ; ++cc2 ) {
    a1 = cc2*ADim2;
    a3 = cc2*BDim1
    for ( cc1=0 ; cc1<ADim2 ; ++cc1 ) {
          a2=cc1*ADim1;
          ValT b = B[a3+cc1];
          for ( cr1=0 ; cr1<ADim1 ; ++cr1 ) {
                    C[a1+cr1] += A[a2+cr1]*b;
           }
     }
  }
} 

यह कोशिश करो, मुझे यकीन है कि आप कुछ बचा लेंगे।

यदि आप एक तुच्छ एल्गोरिथ्म का उपयोग करते हैं, तो # 1 प्रश्न पर, इसका कारण यह है कि मैट्रिक्स गुणन पैमाना O (n ^ 3) है। ऐसे एल्गोरिदम हैं जो बहुत बेहतर पैमाने पर हैं


36
यह उत्तर पूरी तरह से गलत है। बीएलएएस कार्यान्वयन फोरट्रान में नहीं लिखे गए हैं। परफॉर्मेंस-क्रिटिकल कोड असेंबली में लिखा जाता है, और इन दिनों सबसे ज्यादा जो लिखा जाता है वह है C से ऊपर। इसके अलावा BLAS इंटरफ़ेस के भाग के रूप में पंक्ति / स्तंभ क्रम को निर्दिष्ट करता है, और कार्यान्वयन किसी भी संयोजन को संभाल सकते हैं।
एंड्रयू टॉमाज़ोस

10
हाँ, यह जवाब है पूरी तरह से गलत। दुर्भाग्य से यह सामान्य गैर-समझ से भरा है, उदाहरण के लिए दावा BLAS फोरट्रान की वजह से तेज था। 20 (!) सकारात्मक रेटिंग होना एक बुरी बात है। अब यह गैर-बराबरी स्टैकओवरफ्लो की लोकप्रियता के कारण और भी फैल गई!
माइकल लेहैन

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

5
@KyleKanos: हाँ, यहाँ ATLAS का स्रोत है: sourceforge.net/projects/math-atlas/files/Stable/3.10.1 जहां तक ​​मुझे पता है कि यह सबसे अधिक इस्तेमाल किया जाने वाला ओपन सोर्स पोर्टेबल एसएएस कार्यान्वयन है। यह C / ASM में लिखा गया है। इंटेल की तरह उच्च प्रदर्शन सीपीयू निर्माता भी विशेष रूप से अपने चिप्स के लिए अनुकूलित बीएलएएस कार्यान्वयन प्रदान करते हैं। मुझे लगता है कि Intels लाइब्रेरी के निचले स्तर के हिस्सों में (duuh) x86 असेंबली में लिखा गया है, और मुझे पूरा यकीन है कि मिड-लेवल पार्ट्स C या C ++ में लिखे जाएंगे।
एंड्रयू टोमाज़ोस

9
@KyleKanos: आप भ्रमित हैं। नेटलिब BLAS संदर्भ कार्यान्वयन है। अनुकूलित कार्यान्वयन की तुलना में संदर्भ कार्यान्वयन बहुत धीमा है ( प्रदर्शन तुलना देखें )। जब कोई कहता है कि वे एक क्लस्टर पर netlib BLAS का उपयोग कर रहे हैं, तो इसका मतलब यह नहीं है कि वे वास्तव में netlib संदर्भ कार्यान्वयन का उपयोग कर रहे हैं। वह सिर्फ मूर्खतापूर्ण होगा। इसका सीधा सा मतलब है कि वे नेटलीब ब्लास के समान इंटरफेस के साथ एक लिब का उपयोग कर रहे हैं।
एंड्रयू टॉमाज़ोस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.