क्या इनलाइन असेंबली लैंग्वेज देशी C ++ कोड से धीमी है?


183

मैंने इनलाइन असेंबली लैंग्वेज और C ++ कोड के प्रदर्शन की तुलना करने की कोशिश की, इसलिए मैंने एक फ़ंक्शन लिखा, जो 2000 के लिए 2000 के आकार के दो सरणियों को जोड़ता है। यहाँ कोड है:

#define TIMES 100000
void calcuC(int *x,int *y,int length)
{
    for(int i = 0; i < TIMES; i++)
    {
        for(int j = 0; j < length; j++)
            x[j] += y[j];
    }
}


void calcuAsm(int *x,int *y,int lengthOfArray)
{
    __asm
    {
        mov edi,TIMES
        start:
        mov esi,0
        mov ecx,lengthOfArray
        label:
        mov edx,x
        push edx
        mov eax,DWORD PTR [edx + esi*4]
        mov edx,y
        mov ebx,DWORD PTR [edx + esi*4]
        add eax,ebx
        pop edx
        mov [edx + esi*4],eax
        inc esi
        loop label
        dec edi
        cmp edi,0
        jnz start
    };
}

यहाँ है main():

int main() {
    bool errorOccured = false;
    setbuf(stdout,NULL);
    int *xC,*xAsm,*yC,*yAsm;
    xC = new int[2000];
    xAsm = new int[2000];
    yC = new int[2000];
    yAsm = new int[2000];
    for(int i = 0; i < 2000; i++)
    {
        xC[i] = 0;
        xAsm[i] = 0;
        yC[i] = i;
        yAsm[i] = i;
    }
    time_t start = clock();
    calcuC(xC,yC,2000);

    //    calcuAsm(xAsm,yAsm,2000);
    //    for(int i = 0; i < 2000; i++)
    //    {
    //        if(xC[i] != xAsm[i])
    //        {
    //            cout<<"xC["<<i<<"]="<<xC[i]<<" "<<"xAsm["<<i<<"]="<<xAsm[i]<<endl;
    //            errorOccured = true;
    //            break;
    //        }
    //    }
    //    if(errorOccured)
    //        cout<<"Error occurs!"<<endl;
    //    else
    //        cout<<"Works fine!"<<endl;

    time_t end = clock();

    //    cout<<"time = "<<(float)(end - start) / CLOCKS_PER_SEC<<"\n";

    cout<<"time = "<<end - start<<endl;
    return 0;
}

फिर मैं प्रोसेसर के चक्र प्राप्त करने के लिए पांच बार कार्यक्रम चलाता हूं, जिसे समय के रूप में देखा जा सकता है। हर बार मैं केवल ऊपर उल्लिखित फ़ंक्शन में से एक को कॉल करता हूं।

और यहाँ परिणाम आता है।

विधानसभा संस्करण का कार्य:

Debug   Release
---------------
732        668
733        680
659        672
667        675
684        694
Average:   677

C ++ संस्करण का कार्य:

Debug     Release
-----------------
1068      168
 999      166
1072      231
1002      166
1114      183
Average:  182

रिलीज़ मोड में C ++ कोड असेंबली कोड की तुलना में लगभग 3.7 गुना तेज है। क्यों?

मुझे लगता है कि मैंने जो असेंबली कोड लिखा था, वह उतना प्रभावी नहीं है जितना कि जीसीसी द्वारा उत्पन्न किया गया। एक आम प्रोग्रामर के लिए यह मुश्किल है कि मैं एक कंपाइलर द्वारा बनाए गए अपने प्रतिद्वंद्वी की तुलना में कोड को तेजी से लिखूं। क्या इसका मतलब है कि मुझे अपने हाथों से लिखी गई विधानसभा भाषा के प्रदर्शन पर भरोसा नहीं करना चाहिए, सी ++ पर ध्यान केंद्रित करना चाहिए और विधानसभा भाषा के बारे में भूलना चाहिए?


29
बहुत ज्यादा। हैंडकैप्ड असेंबली कुछ परिस्थितियों में उपयुक्त है, लेकिन यह सुनिश्चित करने के लिए ध्यान रखा जाना चाहिए कि असेंबली संस्करण वास्तव में उच्च स्तर की भाषा के साथ प्राप्त किया जा सकता है।
मैग्नस हॉफ

161
आपको यह संकलक द्वारा उत्पन्न कोड का अध्ययन करने के लिए शिक्षाप्रद लग सकता है, और यह समझने की कोशिश करें कि यह आपके असेंबली संस्करण की तुलना में अधिक तेज़ क्यों है।
पॉल आर।

34
हाँ, ऐसा लगता है कि संकलक आपसे लिखने के लिए बेहतर है। आधुनिक संकलक वास्तव में काफी अच्छे हैं।
डेविड हेफर्नन

20
क्या आपने उत्पादित GCC विधानसभा को देखा है? इसके संभावित जीसीसी ने एमएमएक्स निर्देशों का उपयोग किया। आपका फ़ंक्शन बहुत समानांतर है - आप संभावित रूप से 1 / N वें समय में योग की गणना करने के लिए एन प्रोसेसर का उपयोग कर सकते हैं। एक ऐसे फंक्शन की कोशिश करें, जहाँ समानांतर होने की कोई उम्मीद न हो।
क्रिस

11
हम्म, मैंने एक अच्छे कंपाइलर से ऐसा करने की अपेक्षा की है ~ 100000 गुना तेजी से ...
प्लाज़्मा एचएच

जवाबों:


261

हाँ, ज्यादातर बार।

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

आप हमेशा एक उदाहरण का निर्माण कर सकते हैं जहां हस्तनिर्मित विधानसभा कोड संकलित कोड से बेहतर है, लेकिन आमतौर पर यह एक काल्पनिक उदाहरण या एकल दिनचर्या है जो सी ++ कोड की 500.000+ लाइनों का सही कार्यक्रम नहीं है )। मुझे लगता है कि संकलक बेहतर असेंबली कोड 95% और कभी-कभी, केवल कुछ दुर्लभ समय का उत्पादन करेंगे , आपको कुछ, लघु, अत्यधिक उपयोग किए जाने वाले , महत्वपूर्ण आलोचनात्मक दिनचर्या के लिए विधानसभा कोड लिखने की आवश्यकता हो सकती है या जब आपको अपनी पसंदीदा उच्च-स्तरीय भाषा तक पहुँच प्राप्त करनी होगी उजागर नहीं करता है। क्या आप इस जटिलता का स्पर्श चाहते हैं? इस भयानक उत्तर को यहाँ SO पर पढ़ें ।

यही कारण है?

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

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

कुछ जटिल माइक्रोकंट्रोलरों के लिए भी सिस्टम लाइब्रेरी असेंबली के बजाय C में लिखी जाती है क्योंकि उनके कंपाइलर अंतिम कोड को बेहतर (और आसान बनाए रखने के लिए) बनाते हैं।

कंपाइलर कभी-कभी अपने आप ही कुछ MMX / SIMDx निर्देशों का उपयोग कर सकते हैं, और यदि आप उनका उपयोग नहीं करते हैं तो आप बस तुलना नहीं कर सकते हैं (अन्य उत्तर पहले से ही आपके विधानसभा कोड की बहुत अच्छी तरह से समीक्षा कर चुके हैं)। बस लूप्स के लिए यह लूप ऑप्टिमाइजेशन की एक छोटी सूची है जिसे आमतौर पर एक कंपाइलर द्वारा चेक किया जाता है (क्या आपको लगता है कि आप इसे खुद से कर सकते हैं जब आपका शेड्यूल C # प्रोग्राम के लिए तय किया गया है?) यदि आप असेंबली में कुछ लिखते हैं, तो मैं लगता है कि आपको कम से कम कुछ साधारण अनुकूलन पर विचार करना होगा । सरणियों के लिए स्कूल-पुस्तक का उदाहरण चक्र को अनियंत्रित करना है (इसका आकार संकलन समय पर जाना जाता है)। इसे करें और अपना परीक्षण फिर से चलाएं।

इन दिनों एक और कारण के लिए विधानसभा भाषा का उपयोग करने की आवश्यकता वास्तव में असामान्य है: विभिन्न सीपीयू का ढेर । क्या आप उन सभी का समर्थन करना चाहते हैं? प्रत्येक में एक विशिष्ट माइक्रोआर्किटेक्चर और कुछ विशिष्ट अनुदेश सेट हैं । उनके पास विभिन्न प्रकार की कार्यात्मक इकाइयाँ हैं और उन सभी को व्यस्त रखने के लिए विधानसभा निर्देशों की व्यवस्था की जानी चाहिए । यदि आप C में लिखते हैं तो आप PGO का उपयोग कर सकते हैं लेकिन असेंबली में आपको उस विशिष्ट वास्तुकला (और फिर से एक और वास्तुकला के लिए सब कुछ पुनर्विचार और फिर से करना ) के एक महान ज्ञान की आवश्यकता होगी । छोटे कार्यों के लिए कंपाइलर आमतौर पर इसे बेहतर करता है, और जटिल कार्यों के लिए आमतौर पर काम चुकाया नहीं जाता है (औरसंकलक वैसे भी बेहतर कर सकता है )।

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

सभी यह कहा, यहां तक कि जब आप एक 5 ~ 10 गुना तेजी से विधानसभा कोड का उत्पादन कर सकते हैं, अगर वे पसंद करते हैं आप अपने ग्राहकों को पूछना चाहिए भुगतान के एक सप्ताह के लिए अपना समय या करने के लिए एक 50 $ तेजी से सीपीयू खरीद । चरम अनुकूलन अधिक बार नहीं (और विशेष रूप से एलओबी अनुप्रयोगों में) बस हम में से अधिकांश की आवश्यकता नहीं है।


9
बिलकूल नही। मुझे लगता है कि ९९% लोगों में ९ ५% लोग बेहतर हैं। कभी-कभी क्योंकि यह केवल महंगा ( जटिल गणित के कारण) या समय बिताना (फिर महंगा होना) है। कभी-कभी क्योंकि हम केवल अनुकूलन के बारे में भूल गए थे ...
एड्रियानो रेपेट्टी

62
@ ja72 - नहीं, यह कोड लिखने में बेहतर नहीं है । यह अनुकूलन कोड में बेहतर है ।
माइक बैरनज़क

14
जब तक आप वास्तव में इस पर विचार नहीं करते हैं तब तक यह प्रति-सहज है। उसी तरह, वीएम आधारित मशीनें रनटाइम ऑप्टिमाइज़ेशन करना शुरू कर रही हैं जो कंपाइलर्स के पास बस बनाने के लिए जानकारी नहीं है।
बिल के

6
@ M28: कंपाइलर समान निर्देशों का उपयोग कर सकते हैं। निश्चित रूप से, वे इसके लिए द्विआधारी आकार के संदर्भ में भुगतान करते हैं (क्योंकि उन्हें उस स्थिति में एक वापसी मार्ग प्रदान करना है जो उन निर्देशों का समर्थन नहीं करते हैं)। इसके अलावा, अधिकांश भाग के लिए, "नए निर्देश" जो जोड़े जाएंगे, वैसे भी SMID निर्देश हैं, जिनका उपयोग करने में वीएम और कंपाइलर दोनों ही बहुत भयानक हैं। VM इस सुविधा के लिए भुगतान करते हैं कि उन्हें स्टार्टअप में कोड संकलित करना होगा।
बिली ओनेल

9
@ बेलक: कंपाइलर्स के लिए पीजीओ यही काम करता है।
बिली ओनेल

194

आपका असेंबली कोड सबॉप्टीमल है और इसमें सुधार किया जा सकता है:

  • आप अपने आंतरिक लूप में एक रजिस्टर ( EDX ) को पुश और पॉपिंग कर रहे हैं । इसे लूप से बाहर ले जाना चाहिए।
  • आप लूप के प्रत्येक पुनरावृत्ति में सरणी पॉइंटर्स को पुनः लोड करते हैं। यह लूप से बाहर जाना चाहिए।
  • आप loopनिर्देश का उपयोग करते हैं , जो कि अधिकांश आधुनिक सीपीयू पर धीमा गति से जाना जाता है (संभवतः एक प्राचीन विधानसभा पुस्तक का उपयोग करने का एक परिणाम *)
  • आप मैनुअल लूप का कोई लाभ नहीं उठाते हैं।
  • आप उपलब्ध SIMD निर्देशों का उपयोग नहीं करते हैं ।

इसलिए जब तक आप असेंबलर के बारे में अपने कौशल-सेट में सुधार नहीं करते हैं, तब तक आपको प्रदर्शन के लिए कोडांतरक कोड लिखने से कोई मतलब नहीं है।

* बेशक मैं नहीं जानता कि क्या आपको वास्तव में loopएक प्राचीन विधानसभा पुस्तक से निर्देश मिला है । लेकिन आप इसे लगभग कभी भी वास्तविक विश्व कोड में नहीं देख सकते हैं, क्योंकि हर कंपाइलर बाहर काफी स्मार्ट होता है loop, आप इसे केवल IMHO खराब और पुरानी पुस्तकों में नहीं देखते हैं।


loopयदि आप आकार के लिए अनुकूलन करते हैं, तो कंपाइलर अभी भी उत्सर्जन कर सकते हैं (और कई "पदावनत" निर्देश)
फुकुव

1
@ अच्छी तरह से हाँ, लेकिन मूल प्रश्न गति के बारे में था, आकार नहीं।
IGR94 12

60

विधानसभा में देरी से पहले भी, कोड परिवर्तन होते हैं जो उच्च स्तर पर मौजूद होते हैं।

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
  for (int i = 0; i < TIMES; i++) {
    for (int j = 0; j < length; j++) {
      x[j] += y[j];
    }
  }
}

लूप रोटेशन के माध्यम से रूपांतरित किया जा सकता है :

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      for (int i = 0; i < TIMES; ++i) {
        x[j] += y[j];
      }
    }
}

जो कि मेमोरी लोकलिटी के चलते बहुत बेहतर है।

यह आगे अनुकूलन हो सकता है, a += bएक्स बार करना ऐसा करने के बराबर है जो a += X * bहमें मिलता है:

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      x[j] += TIMES * y[j];
    }
}

हालाँकि ऐसा लगता है कि मेरा पसंदीदा ऑप्टिमाइज़र (LLVM) यह परिवर्तन नहीं करता है।

[संपादित करें] मैंने पाया कि यदि हमारे पास restrictक्वालिफायर xऔर है तो परिवर्तन किया जाता है y। वास्तव में इस प्रतिबंध के बिना, x[j]और y[j]उसी स्थान पर उर्फ ​​हो सकता है जो इस परिवर्तन को गलत बनाता है। [अंत संपादित करें]

वैसे भी, यह मुझे लगता है, अनुकूलित सी संस्करण है। पहले से ही यह बहुत सरल है। इसके आधार पर, यहां ASM पर मेरी दरार है (मैंने क्लैंग को इसे उत्पन्न करने दिया, मैं इस पर बेकार हूं):

calcuAsm:                               # @calcuAsm
.Ltmp0:
    .cfi_startproc
# BB#0:
    testl   %edx, %edx
    jle .LBB0_2
    .align  16, 0x90
.LBB0_1:                                # %.lr.ph
                                        # =>This Inner Loop Header: Depth=1
    imull   $100000, (%rsi), %eax   # imm = 0x186A0
    addl    %eax, (%rdi)
    addq    $4, %rsi
    addq    $4, %rdi
    decl    %edx
    jne .LBB0_1
.LBB0_2:                                # %._crit_edge
    ret
.Ltmp1:
    .size   calcuAsm, .Ltmp1-calcuAsm
.Ltmp2:
    .cfi_endproc

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


आपके उत्तर के लिए धन्यवाद। फिर, यह थोड़ा भ्रमित करने वाला है कि जब मैंने "कंपाइलर सिद्धांतों" नाम की क्लास ली, तो मुझे पता चला कि कंपाइलर कई तरीकों से हमारे कोड को ऑप्टिमाइज़ करेगा। क्या इसका मतलब है कि हमें अपने कोड को मैन्युअल रूप से अनुकूलित करने की आवश्यकता है? क्या हम कंपाइलर से बेहतर काम कर सकते हैं? यही वह सवाल है जो मुझे हमेशा भ्रमित करता है।
user957121

2
@ user957121: अधिक जानकारी होने पर हम इसे बेहतर तरीके से अनुकूलित कर सकते हैं। विशेष रूप से यहाँ क्या संकलक में बाधा उत्पन्न करता है, के बीच संभव उर्फिंग हैx और y। है यही कारण है, संकलक सुनिश्चित करें कि सभी के लिए है कि नहीं किया जा सकता i,jमें [0, length)हमारे पास x + i != y + j। यदि ओवरलैप है, तो अनुकूलन असंभव है। सी लैंग्वेज ने restrictकंपाइलर को यह बताने के लिए कीवर्ड पेश किया कि दो पॉइंटर्स अलियास नहीं कर सकते हैं, हालांकि यह एरेज़ के लिए काम नहीं करता है क्योंकि वे अभी भी ओवरलैप कर सकते हैं भले ही वे बिलकुल भी उर्फ ​​न हों।
Matthieu M.

वर्तमान जीसीसी और क्लैंग ऑटो-वेक्टराइज़ (गैर-ओवरलैप के लिए जांच के बाद यदि आप छोड़ देते हैं __restrict)। SSE2 x86-64 के लिए आधार रेखा है, और SSE2 फेरबदल के साथ एक बार में 2x 32-बिट गुणन कर सकता है (64-बिट उत्पादों का निर्माण, इसलिए परिणाम को एक साथ वापस लाने के लिए फेरबदल)। godbolt.org/z/r7F_uo । (SSE4.1 के लिए आवश्यक है pmulld: 32x32 => 32-बिट गुणा किया हुआ)। जीसीसी में शिफ्ट / ऐड (और / या घटाना) में निरंतर पूर्णांक गुणक को बदलने की एक साफ चाल है, जो कुछ बिट सेट के साथ गुणक के लिए अच्छा है। Clang का शफल-भारी कोड Intel CPUs पर फेरबदल थ्रूपुट पर अड़चन डालने वाला है।
पीटर कॉर्ड्स

41

संक्षिप्त उत्तर: हाँ।

लंबे उत्तर: हां, जब तक आप वास्तव में नहीं जानते कि आप क्या कर रहे हैं, और ऐसा करने का एक कारण है।


3
और उसके बाद ही यदि आपने इंटेल चिप्स के लिए vtune जैसे असेंबली लेवल प्रोफाइलिंग टूल चलाया है, तो यह देखने के लिए कि आप चीजों में कहां सुधार कर सकते हैं
मार्क मुलीन

1
यह तकनीकी रूप से प्रश्न का उत्तर देता है, लेकिन पूरी तरह से बेकार भी है। ए -1 मुझसे।
नविन

2
बहुत लंबा जवाब: "हाँ, जब तक आप अपना पूरा कोड बदलने का मन नहीं करते जब भी कोई नया (एर) सीपीयू इस्तेमाल किया जाता है। सर्वश्रेष्ठ एल्गोरिथ्म चुनें, लेकिन कंपाइलर को ऑप्टिमाइज़ेशन करने दें"
Tommylee2k

35

मैंने अपना asm कोड तय कर लिया है:

  __asm
{   
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,1
    mov edi,y
label:
    movq mm0,QWORD PTR[esi]
    paddd mm0,QWORD PTR[edi]
    add edi,8
    movq QWORD PTR[esi],mm0
    add esi,8
    dec ecx 
    jnz label
    dec ebx
    jnz start
};

रिलीज़ संस्करण के लिए परिणाम:

 Function of assembly version: 81
 Function of C++ version: 161

रिलीज़ मोड में असेंबली कोड C ++ से लगभग 2 गुना तेज है।


18
अब यदि आप एमएमएक्स के बजाय एसएसई का उपयोग करना शुरू करते हैं (रजिस्टर नाम के xmm0बजाय है mm0), तो आपको दो के एक कारक द्वारा एक और स्पीडअप मिलेगा
;;

8
मैं बदल गया, विधानसभा संस्करण के लिए 41 मिला। यह 4 गुना तेज है :)
साशा

3
अगर सभी xmm रजिस्टरों का उपयोग करें तो 5% तक अधिक मिल सकता है
sasha

7
अब अगर आप उस समय के बारे में सोचते हैं जो वास्तव में आपको ले गया है: विधानसभा, लगभग 10 घंटे या तो? C ++, मुझे लगता है कि कुछ मिनट? यहां एक स्पष्ट विजेता है, जब तक कि यह प्रदर्शन-महत्वपूर्ण कोड नहीं है।
कालिमो

1
एक अच्छा संकलक पहले से ही ऑटो-वेक्टर करेगा paddd xmm( आप के बीच ओवरलैप के लिए जाँच करने के बाद xऔर y, क्योंकि आपने उपयोग नहीं किया था int *__restrict x)। उदाहरण के लिए gcc यह करता है: godbolt.org/z/c2JG0- । या अंदर घुसने के बाद main, इसे ओवरलैप के लिए जाँचने की आवश्यकता नहीं होनी चाहिए क्योंकि यह आवंटन को देख सकता है और साबित कर सकता है कि वे गैर-अतिव्यापी हैं। (और यह कुछ x86-64 कार्यान्वयन पर 16-बाइट संरेखण को ग्रहण करने के लिए मिलेगा, भी, जो स्टैंड-अलोन परिभाषा के लिए मामला नहीं है।) और यदि आप संकलन करते हैं gcc -O3 -march=native, तो आप 256-बिट या 512-बिट प्राप्त कर सकते हैं। vectorization।
पीटर कॉर्डेस

24

क्या इसका मतलब यह है कि मुझे अपने हाथों से लिखी गई विधानसभा भाषा के प्रदर्शन पर भरोसा नहीं करना चाहिए

हां, इसका ठीक यही अर्थ है, और यह हर भाषा के लिए सच है । यदि आपको पता नहीं है कि भाषा X में कुशल कोड कैसे लिखा जाता है, तो आपको X में कुशल कोड लिखने की अपनी क्षमता पर भरोसा नहीं करना चाहिए। और इसलिए, यदि आप कुशल कोड चाहते हैं, तो आपको दूसरी भाषा का उपयोग करना चाहिए।

असेंबली इसके लिए विशेष रूप से संवेदनशील है, क्योंकि, ठीक है, आप जो देखते हैं वह आपको मिलता है। आप विशिष्ट निर्देशों को लिखते हैं जो आप चाहते हैं कि सीपीयू निष्पादित हो। उच्च स्तरीय भाषाओं के साथ, betweeen में एक कंपाइलर होता है, जो आपके कोड को बदल सकता है और कई अक्षमताओं को दूर कर सकता है। असेंबली के साथ, आप अपने दम पर हैं।


2
मुझे लगता है कि यह लिखने के लिए है कि विशेष रूप से एक आधुनिक x86 प्रोसेसर के लिए पाइपलाइनों, कई निष्पादन इकाइयों और हर कोर के अंदर अन्य चालबाज़ियों की उपस्थिति के कारण कुशल विधानसभा कोड लिखना असाधारण रूप से कठिन है। लेखन कोड जो उच्चतम निष्पादन गति प्राप्त करने के लिए इन सभी संसाधनों के उपयोग को संतुलित करता है, अक्सर अस्वाभाविक तर्क के साथ कोड में परिणाम होगा कि "पारंपरिक" विधानसभा ज्ञान के अनुसार "तेज" नहीं होना चाहिए। लेकिन कम जटिल सीपीयू के लिए यह मेरा अनुभव है कि सी कंपाइलर की कोड पीढ़ी को काफी बेहतर किया जा सकता है।
ओलोफ फोर्सशेल

4
C कंपाइलर कोड को आमतौर पर आधुनिक x86 CPU पर भी बेहतर बनाया जा सकता है। लेकिन आपको सीपीयू को अच्छी तरह से समझना होगा, जो कि आधुनिक x86 सीपीयू के साथ करना मुश्किल है। यही मेरा सवाल है। यदि आप उस हार्डवेयर को नहीं समझ रहे हैं जिसे आप लक्षित कर रहे हैं, तो आप इसके लिए अनुकूलन नहीं कर पाएंगे। और फिर संकलक एक बेहतर काम करने की संभावना करेंगे
jalf

1
और अगर आप वास्तव में कंपाइलर को उड़ाना चाहते हैं, तो आपको रचनात्मक होना होगा और उन तरीकों को अनुकूलित करना होगा जो कंपाइलर नहीं कर सकता। यह समय / इनाम के लिए एक व्यापार है, इसलिए सी कुछ के लिए एक स्क्रिप्टिंग भाषा है और दूसरों के लिए उच्च स्तर की भाषा के लिए मध्यवर्ती कोड है। मेरे लिए हालांकि, विधानसभा मज़ा के लिए अधिक है :)। grc.com/smgassembly.htm
Hawken

22

आजकल असेंबली भाषा का उपयोग करने का एकमात्र कारण भाषा द्वारा सुलभ नहीं कुछ सुविधाओं का उपयोग करना है।

यह इस पर लागू होता है:

  • कर्नेल प्रोग्रामिंग जिसे कुछ हार्डवेयर सुविधाओं जैसे कि MMU तक पहुंचने की आवश्यकता है
  • उच्च प्रदर्शन प्रोग्रामिंग जो आपके संकलक द्वारा समर्थित नहीं बहुत विशिष्ट वेक्टर या मल्टीमीडिया निर्देशों का उपयोग करती है।

लेकिन वर्तमान संकलक काफी स्मार्ट हैं, वे दो अलग-अलग कथनों की जगह भी ले सकते हैं जैसे d = a / b; r = a % b;कि एक एकल निर्देश के साथ जो विभाजन की गणना करता है और शेष एक में उपलब्ध है यदि यह उपलब्ध है, भले ही सी में ऐसा ऑपरेटर न हो।


10
उन दो के अलावा ASM के लिए अन्य स्थान भी हैं। आमतौर पर, झंडे और बहुस्तरीय के ऊपरी हिस्से और इस तरह से पहुंच के कारण, एक बाइनम लाइब्रेरी आमतौर पर सी की तुलना में एएसएम में काफी तेज होगी। आप इन चीजों को पोर्टेबल C में भी कर सकते हैं, लेकिन ये बहुत धीमी गति से होती हैं।
मूविंग डक

@MooDDuck को ऐसे हार्डवेयर हार्डवेयर सुविधाओं तक पहुंच माना जा सकता है जो सीधे भाषा में उपलब्ध नहीं हैं ... लेकिन जब तक आप अपने उच्च स्तरीय कोड को हाथ से विधानसभा में अनुवाद कर रहे हैं , कंपाइलर आपको हरा देगा।
फोरट्रान

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

1
यदि आप अपने कोड को ऑप्टिमाइज़ नहीं करते हैं तो @fortran अपने मूल रूप से यह कह रहा है कि यह कोड कंपाइलर को अनुकूलित कोड के रूप में तेज़ नहीं होगा। अनुकूलन पहली जगह में असेंबली लिखने का कारण है। यदि आप अनुवाद करते हैं, तो इसका मतलब है कि कोई कारण नहीं है कि कंपाइलर आपको हरा देगा जब तक आप असेंबली का अनुकूलन करने में अच्छे नहीं हैं। तो संकलक को हरा करने के लिए आपको उन तरीकों से अनुकूलन करना होगा जो संकलक नहीं कर सकते। यह बहुत आत्म व्याख्यात्मक है। असेंबली लिखने का एकमात्र कारण यह है कि यदि आप संकलक / दुभाषिया से बेहतर हैं । हमेशा विधानसभा लिखने का व्यावहारिक कारण रहा है।
हैकेन

1
सिर्फ यह कहते हुए: क्लैंग में कैरी फ्लैग की सुविधा है, 128 बिट गुणा और इसी तरह बिल्ट-इन फ़ंक्शन के माध्यम से। और यह इन सभी को अपने सामान्य अनुकूलन एल्गोरिदम में एकीकृत कर सकता है।
gnasher729

19

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

सबसे पहले आप स्पष्ट रूप से इससे भयभीत नहीं होते हैं , यह एक महान, महान प्लस है, अगला - आप अपनी गति मान्यताओं को मान्य या त्यागने के लिए प्रोफाइलिंग करके सही रास्ते पर हैं, आप अनुभवी लोगों से इनपुट मांग रहे हैं , और आप मानव जाति के लिए ज्ञात सबसे बड़ा अनुकूलन उपकरण है: एक मस्तिष्क

जैसे-जैसे आपका अनुभव बढ़ता जाता है, आप सीखेंगे कि कब और कहाँ इसका इस्तेमाल करना है (आमतौर पर आपके कोड में सबसे गहरी, अंतरतम छोरों को आप एक एल्गोरिथम स्तर पर गहराई से अनुकूलित करने के बाद)।

प्रेरणा के लिए, मैं आपको माइकल अब्रश के लेखों को देखने की सलाह दूंगा (यदि आपने उनसे नहीं सुना है, तो वह एक अनुकूलन गुरु हैं; उन्होंने क्वेक सॉफ्टवेयर रेंडरर के अनुकूलन में जॉन कार्मैक के साथ भी सहयोग किया है!)

"सबसे तेज़ कोड जैसी कोई चीज़ नहीं है" - माइकल अब्रैश


2
मेरा मानना ​​है कि माइकल अब्राश पुस्तकों में से एक ग्राफिक्स प्रोग्रामिंग ब्लैक बुक है। लेकिन वह विधानसभा का उपयोग करने वाला एकमात्र व्यक्ति नहीं है, क्रिस सॉयर ने विधानसभा में पहले दो रोलर कोस्टर टाइकून गेम लिखे।
हॉकेन

14

मैंने asm कोड बदल दिया है:

 __asm
{ 
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,2
    mov edi,y
label:
    mov eax,DWORD PTR [esi]
    add eax,DWORD PTR [edi]
    add edi,4   
    dec ecx 
    mov DWORD PTR [esi],eax
    add esi,4
    test ecx,ecx
    jnz label
    dec ebx
    test ebx,ebx
    jnz start
};

रिलीज़ संस्करण के लिए परिणाम:

 Function of assembly version: 41
 Function of C++ version: 161

रिलीज़ मोड में असेंबली कोड C ++ से लगभग 4 गुना तेज है। IMHo, असेंबली कोड की गति प्रोग्रामर से निर्भर करती है


हाँ, मेरे कोड को वास्तव में अनुकूलित करने की आवश्यकता है। आपके और धन्यवाद के लिए अच्छे काम!
user957121

5
यह चार गुना तेज है क्योंकि आप केवल एक चौथाई काम करते हैं :-) शानदार shr ecx,2है, क्योंकि सरणी लंबाई पहले से ही दी गई है intऔर बाइट में नहीं। तो आप मूल रूप से उसी गति को प्राप्त करते हैं। आप padddहरल्स के उत्तर से कोशिश कर सकते हैं , यह वास्तव में तेज़ होगा।
गनथर पीज़

13

यह बहुत ही दिलचस्प विषय है!
मैंने साशा के कोड में SSE द्वारा MMX बदल दिया है
यहां मेरे परिणाम हैं:

Function of C++ version:      315
Function of assembly(simply): 312
Function of assembly  (MMX):  136
Function of assembly  (SSE):  62

SSE के साथ असेंबली कोड C ++ से 5 गुना तेज है


12

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

उदाहरण के लिए, यहां तक ​​कि मुझे यकीन नहीं है कि यह किसी भी अधिक सही है :):

करते हुए:

mov eax,0

से अधिक साइकिल की लागत

xor eax,eax

जो ऐसा ही करता है।

संकलक इन सभी चालों को जानता है और उनका उपयोग करता है।


4
अभी भी सच है, देखें stackoverflow.com/questions/1396527/… । उपयोग किए गए चक्रों के कारण नहीं, बल्कि कम स्मृति पदचिह्न के कारण।
गनथर पाईज़

10

संकलक ने आपको हराया। मैं इसे आज़माता हूँ, लेकिन मैं कोई गारंटी नहीं दूँगा। मैं मानूंगा कि TIMES द्वारा "गुणन" का अर्थ इसे और अधिक प्रासंगिक प्रदर्शन परीक्षण बनाने के लिए है, yऔर xयह 16-संरेखित है, और यह 4 lengthका गैर-शून्य गुणक है। यह संभवतः वैसे भी सब सच है।

  mov ecx,length
  lea esi,[y+4*ecx]
  lea edi,[x+4*ecx]
  neg ecx
loop:
  movdqa xmm0,[esi+4*ecx]
  paddd xmm0,[edi+4*ecx]
  movdqa [edi+4*ecx],xmm0
  add ecx,4
  jnz loop

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


मुझे लगता है कि यदि आप कोड को बदल रहे हैं तो कॉम्प्लेक्स एड्रेसिंग आपके कोड को धीमा कर रहा है, अगर आप कोड को बदल देते हैं mov ecx, length, lea ecx,[ecx*4], mov eax,16... add ecx,eaxऔर फिर [esi + ecx] हर जगह का उपयोग करते हैं तो आप लूप लॉट को तेज करने वाले निर्देश के अनुसार 1 चक्र स्टाल से बचेंगे। (यदि आपके पास नवीनतम स्काईलेक है तो यह लागू नहीं होता है)। ऐड reg, reg सिर्फ लूप को तंग करता है, जो मदद कर सकता है या नहीं।
जोहान

@ जोहान स्टाल नहीं होना चाहिए, बस एक अतिरिक्त चक्र विलंबता, लेकिन निश्चित रूप से यह नहीं होने के लिए चोट नहीं कर सकता है .. मैंने कोर 2 के लिए यह कोड लिखा था जिसमें यह मुद्दा नहीं था। क्या r + r भी "जटिल" btw नहीं है?
हेरोल्ड

7

बस आँख बंद करके ठीक उसी एल्गोरिथ्म को लागू करना, निर्देश द्वारा निर्देश, असेंबली में संकलक की तुलना में धीमे होने की गारंटी है।

ऐसा इसलिए है क्योंकि यहां तक ​​कि सबसे छोटा अनुकूलन कंपाइलर आपके कठोर कोड से बेहतर है जिसमें कोई अनुकूलन नहीं है।

बेशक, कंपाइलर को हरा पाना संभव है, खासकर अगर यह कोड का एक छोटा, स्थानीयकृत हिस्सा है, तो मुझे भी लगभग इसे प्राप्त करने के लिए खुद ही करना होगा। 4x की गति बढ़ जाती है, लेकिन इस मामले में हमें हार्डवेयर और कई के अच्छे ज्ञान पर भरोसा करना पड़ता है, प्रतीत होता है कि काउंटर-सहज ज्ञान युक्त ट्रिक्स।


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

C / ++ कंपाइलर इस तरह के एक उपक्रम होने के साथ, और केवल 3 प्रमुख लोगों के आसपास, वे जो करते हैं, उसमें अच्छा नहीं है। यह कुछ परिस्थितियों में अभी भी (बहुत) संभव है कि हाथ से लिखी गई विधानसभा तेज हो; कई गणित पुस्तकालय बेहतर / एकाधिक मूल्यों को संभालने के लिए asm को छोड़ते हैं। इसलिए जब कि गारंटी थोड़ी बहुत मजबूत है, यह संभावना है।
ssube

@peachykeen: मेरा मतलब यह नहीं था कि विधानसभा को सामान्य तौर पर C ++ की तुलना में धीमा होने की गारंटी है। मेरा मतलब था कि उस मामले में "गारंटी" जहां आपके पास एक सी ++ कोड है और नेत्रहीन इसे लाइन से विधानसभा तक अनुवाद करते हैं। मेरे उत्तर के अंतिम पैराग्राफ को भी पढ़ें :)
vsz

5

संकलक के रूप में मैं एक लूप को बहुत सारे निष्पादन कार्यों के लिए निश्चित आकार के साथ बदल दूंगा।

int a = 10;
for (int i = 0; i < 3; i += 1) {
    a = a + i;
}

उत्पादन करेंगे

int a = 10;
a = a + 0;
a = a + 1;
a = a + 2;

और अंततः यह पता चलेगा कि "a = a + 0;" बेकार है इसलिए यह इस लाइन को हटा देगा। उम्मीद है कि आपके सिर में अब कुछ टिप्पणी के रूप में कुछ अनुकूलन विकल्प संलग्न करने को तैयार हैं। वे सभी बहुत प्रभावी अनुकूलन संकलित भाषा को तेज करेंगे।


4
और जब तक aअस्थिर नहीं होता है, एक अच्छा मौका है कि कंपाइलर बस int a = 13;शुरुआत से ही करेगा ।
vsz


4

मुझे यह उदाहरण बहुत पसंद है क्योंकि यह निम्न-स्तरीय कोड के बारे में एक महत्वपूर्ण सबक दर्शाता है। हां, आप असेंबली लिख सकते हैं जो आपके सी कोड जितना तेज़ है। यह tautologically सच है, लेकिन जरूरी कुछ भी मतलब नहीं है। स्पष्ट रूप से कोई कर सकता है, अन्यथा कोडांतरक को उपयुक्त अनुकूलन का पता नहीं होगा।

इसी तरह, यही सिद्धांत लागू होता है क्योंकि आप भाषा के अमूर्तता के पदानुक्रम को बढ़ाते हैं। हां, आप सी में एक पार्सर लिख सकते हैं जो एक त्वरित-और-गंदे पर्ल स्क्रिप्ट के रूप में तेज़ है, और बहुत से लोग करते हैं। लेकिन इसका मतलब यह नहीं है कि आप सी का इस्तेमाल करते हैं, इसलिए आपका कोड तेज होगा। कई मामलों में, उच्च-स्तरीय भाषाएं ऐसे अनुकूलन करती हैं जिन्हें आपने कभी सोचा भी नहीं होगा।


3

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

inline void set_port_high(void)
{
  (*((volatile unsigned char*)0x40001204) = 0xFF);
}

32-बिट एआरएम कोड के लिए एक कंपाइलर, ऊपर दिया गया, संभवतः इसे कुछ इस तरह प्रस्तुत करेगा:

ldr  r0,=0x40001204
mov  r1,#0
strb r1,[r0]
[a fourth word somewhere holding the constant 0x40001204]

या शायद

ldr  r0,=0x40001000  ; Some assemblers like to round pointer loads to multiples of 4096
mov  r1,#0
strb r1,[r0+0x204]
[a fourth word somewhere holding the constant 0x40001000]

हाथ से कोडित कोड में थोड़ा अनुकूलित किया जा सकता है, या तो:

ldr  r0,=0x400011FF
strb r0,[r0+5]
[a third word somewhere holding the constant 0x400011FF]

या

mvn  r0,#0xC0       ; Load with 0x3FFFFFFF
add  r0,r0,#0x1200  ; Add 0x1200, yielding 0x400011FF
strb r0,[r0+5]

दोनों हाथ से इकट्ठे दृष्टिकोण को 16 के बजाय कोड स्थान के 12 बाइट्स की आवश्यकता होगी; बाद वाला "लोड" को "ऐड" से बदल देगा, जो ARM7-TDMI पर दो चक्रों को तेजी से निष्पादित करेगा। यदि कोड को एक ऐसे संदर्भ में निष्पादित किया जा रहा है जहां r0 पता नहीं था / परवाह नहीं है, तो विधानसभा भाषा संस्करण इस प्रकार संकलित संस्करण की तुलना में कुछ बेहतर होगा। दूसरी ओर, मान लीजिए कि संकलक को पता था कि कुछ रजिस्टर [जैसे r5] एक मान रखने वाला था जो वांछित पते 0x40001204 [उदाहरण 0x40001000] के 2047 बाइट्स के भीतर था, और आगे पता था कि कुछ अन्य रजिस्टर (जैसे r7] जा रहा था ऐसा मान रखने के लिए जिसका कम बिट 0xFF था। उस स्थिति में, एक कंपाइलर कोड के C वर्जन को बस अनुकूलित कर सकता है:

strb r7,[r5+0x204]

हाथ से अनुकूलित विधानसभा कोड की तुलना में बहुत छोटा और तेज। इसके अलावा, मान लीजिए कि set_port_high संदर्भ में हुआ:

int temp = function1();
set_port_high();
function2(temp); // Assume temp is not used after this

एम्बेडेड सिस्टम के लिए कोडिंग करते समय बिल्कुल भी नहीं। यदि set_port_highअसेंबली कोड में लिखा गया है, तो function1असेंबली कोड को लागू करने से पहले कंपाइलर को r0 (जो कि रिटर्न वैल्यू रखती है ) कहीं और ले जाना होगा, और फिर उस मान को r0 बाद में ले function2जाएँगे (क्योंकि r0 में इसके पहले पैरामीटर की उम्मीद होगी) इसलिए "अनुकूलित" विधानसभा कोड को पांच निर्देशों की आवश्यकता होगी। यहां तक ​​कि अगर कंपाइलर पते या स्टोर करने के लिए मूल्य रखने वाले किसी भी रजिस्टरों के बारे में नहीं जानता था, तो इसका चार-अनुदेश संस्करण (जो यह किसी भी उपलब्ध रजिस्टर का उपयोग करने के लिए अनुकूल हो सकता है - जरूरी नहीं कि r0 और r1) "अनुकूलित" असेंबली को हरा देगा -भाषण संस्करण। यदि कंपाइलर के पास r5 और r7 में आवश्यक पता और डेटा था जैसा कि पहले बताया गया था, function1तो उन रजिस्टरों में बदलाव नहीं किया जाएगा, और इस प्रकार यह प्रतिस्थापित हो सकता हैset_port_highएक strbनिर्देश के साथ - "हाथ से अनुकूलित" विधानसभा कोड की तुलना में चार निर्देश छोटे और तेज

ध्यान दें कि हाथ से अनुकूलित विधानसभा कोड अक्सर उन मामलों में एक कंपाइलर को बेहतर बना सकता है जहां प्रोग्रामर सटीक प्रोग्राम फ्लो जानता है, लेकिन कंपाइलर्स उन मामलों में चमकते हैं जहां कोड का एक टुकड़ा उसके संदर्भ से पहले लिखा जाता है, या जहां स्रोत कोड का एक टुकड़ा हो सकता है कई संदर्भों से आह्वान किया जाता है [यदि set_port_highकोड में पचास अलग-अलग स्थानों पर उपयोग किया जाता है, तो संकलक स्वतंत्र रूप से उनमें से प्रत्येक के लिए यह तय कर सकता है कि इसका विस्तार कैसे किया जाए]।

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

[माइनर नोट: कुछ स्थान हैं जहां असेंबली कोड का उपयोग हाइपर-ऑप्टिमाइज़्ड गोय मेस की उपज के लिए किया जा सकता है; उदाहरण के लिए, कोड का एक टुकड़ा मैंने एआरएम के लिए RAM से एक शब्द लाने और मूल्य के ऊपरी छह बिट्स के आधार पर लगभग बारह दिनचर्याओं में से एक को निष्पादित करने के लिए आवश्यक था (कई मूल्य समान दिनचर्या में मैप किए गए)। मुझे लगता है कि मैंने उस कोड को कुछ इस तरह से अनुकूलित किया है:

ldrh  r0,[r1],#2! ; Fetch with post-increment
ldrb  r1,[r8,r0 asr #10]
sub   pc,r8,r1,asl #2

रजिस्टर r8 हमेशा मुख्य प्रेषण तालिका का पता रखता था (लूप के भीतर जहां कोड अपने समय का 98% खर्च करता है, कभी भी किसी अन्य उद्देश्य के लिए इसका इस्तेमाल नहीं किया गया); 256 बाइट्स में पतों के लिए संदर्भित सभी 64 प्रविष्टियाँ इसके पहले। चूंकि प्राथमिक लूप में ज्यादातर मामलों में लगभग 60 चक्रों की कठिन निष्पादन-समय सीमा थी, इसलिए उस लक्ष्य को पूरा करने के लिए नौ-चक्र लाने और प्रेषण बहुत महत्वपूर्ण था। 256 32-बिट पतों की तालिका का उपयोग करने से एक चक्र तेजी से होता, लेकिन बहुत कीमती रैम के 1KB तक चमक गया होता [फ्लैश ने एक से अधिक प्रतीक्षा अवस्था को जोड़ा होगा]। 64 32-बिट पतों का उपयोग करने के लिए एक निर्देश को जोड़ने की आवश्यकता होगी, जिसमें कुछ शब्द भ्रूण के शब्द से हटकर हों, और अभी भी मेरे द्वारा उपयोग की गई तालिका की तुलना में 192 से अधिक बाइट्स प्राप्त किए होंगे। 8-बिट ऑफ़सेट की तालिका का उपयोग करने से बहुत कॉम्पैक्ट और तेज़ कोड प्राप्त होता है, लेकिन ऐसा कुछ नहीं है जिसकी मुझे उम्मीद है कि एक कंपाइलर कभी भी साथ आएगा; मैं एक कंपाइलर से यह भी उम्मीद नहीं करूंगा कि वह एक "फुल टाइम" एक रजिस्टर समर्पित करे।

उपरोक्त कोड को स्व-निहित प्रणाली के रूप में चलाने के लिए डिज़ाइन किया गया था; यह समय-समय पर सी कोड को कॉल कर सकता है, लेकिन केवल निश्चित समय पर जब यह जिस हार्डवेयर के साथ संचार कर रहा था, उसे हर 16ms में लगभग एक-एक-मिली-सेकंड अंतराल के लिए "निष्क्रिय" स्थिति में रखा जा सकता है।


2

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


2

C ++ तब तक तेज़ है जब तक आप सही तरीके से गहन ज्ञान के साथ असेंबली भाषा का उपयोग कर रहे हैं।

जब मैं ASM में कोड करता हूं, तो मैं मैन्युअल रूप से निर्देशों को पुनर्गठित करता हूं ताकि सीपीयू तार्किक रूप से संभव होने पर समानांतर में उनमें से अधिक निष्पादित कर सके। मैं बमुश्किल रैम का उपयोग करता हूं जब मैं उदाहरण के लिए ASM में कोड करता हूं: ASM में कोड की 20000+ लाइनें हो सकती हैं और मैंने कभी पुश / पॉप का उपयोग नहीं किया।

आप संभावित रूप से स्व-संशोधित कोड के संभावित दंड के बिना कोड और व्यवहार को संशोधित करने के लिए ओपकोड के बीच में कूद सकते हैं। सीपीयू तक पहुँच रजिस्टर 1 (कभी कभी .25 टिक्स लेता है)।

मेरे पिछले ASM एडवेंचर के लिए, मैंने कभी भी RAM को एक वैरिएबल (ASM की हजारों लाइनों के लिए) स्टोर करने के लिए इस्तेमाल नहीं किया। ASM C ++ की तुलना में संभावित रूप से तेज हो सकता है। लेकिन यह बहुत सारे परिवर्तनशील कारकों पर निर्भर करता है जैसे:

1. I was writing my apps to run on the bare metal.
2. I was writing my own boot loader that was starting my programs in ASM so there was no OS management in the middle.

मैं अब C # और C ++ सीख रहा हूं क्योंकि मुझे उत्पादकता के मामलों का एहसास है !! आप खाली समय में अकेले शुद्ध एएसएम का उपयोग करके सबसे तेज कल्पनाशील कार्यक्रम करने की कोशिश कर सकते हैं। लेकिन कुछ का उत्पादन करने के लिए, कुछ उच्च स्तरीय भाषा का उपयोग करें।

उदाहरण के लिए, अंतिम प्रोग्राम जिसे मैंने कोड किया था, वह JS और GLSL का उपयोग कर रहा था और मैंने कभी भी किसी भी प्रदर्शन के मुद्दे पर ध्यान नहीं दिया, यहां तक ​​कि JS के बारे में भी बोलना जो धीमा है। ऐसा इसलिए है क्योंकि 3D के लिए GPU प्रोग्रामिंग करने की मात्र अवधारणा भाषा की गति को बनाती है जो GPU को आदेशों को लगभग अप्रासंगिक बना देती है।

अकेले नंगे धातु पर कोडांतरक की गति अकाट्य है। क्या यह C ++ के अंदर भी धीमा हो सकता है? - यह हो सकता है क्योंकि आप असेंबली का उपयोग करने के लिए असेंबली का उपयोग नहीं करने के साथ असेंबली कोड लिख रहे हैं।

मेरी व्यक्तिगत परिषद कभी भी असेंबली कोड लिखने की नहीं है यदि आप इसे पसंद कर सकते हैं, भले ही मुझे असेंबली पसंद हो।


1

यहां सभी उत्तर एक पहलू को बाहर करने के लिए प्रतीत होते हैं: कभी-कभी हम एक विशिष्ट उद्देश्य को प्राप्त करने के लिए कोड नहीं लिखते हैं, लेकिन इसके लिए बहुत मज़ेदार हैं। ऐसा करने के लिए समय का निवेश करना किफायती नहीं हो सकता है, लेकिन यकीनन मैन्युअल रूप से रोल किए गए asm विकल्प के साथ सबसे तेज संकलक अनुकूलित कोड स्निपेट की धड़कन की तुलना में अधिक संतुष्टि नहीं है।


जब आप बस संकलक को हरा देना चाहते हैं, तो आमतौर पर इसके एएसएम आउटपुट को अपने फ़ंक्शन के लिए लेना आसान होता है और इसे स्टैंड-अलोन एएसएम फ़ंक्शन में बदल दिया जाता है जिसे आप ट्वीक करते हैं। इनलाइन एएसएम का उपयोग सी ++ और एएसएम के बीच इंटरफेस को सही करने और यह जांचने के लिए अतिरिक्त काम का एक गुच्छा है कि यह इष्टतम कोड के लिए संकलन है। (लेकिन कम से कम जब यह केवल मनोरंजन के लिए कर रहा है, तो आपको इसके बारे में चिंता करने की ज़रूरत नहीं है कि निरंतरता-प्रसार जैसे अनुकूलन को हराया जाए जब फ़ंक्शन किसी और चीज़ में प्रवेश करता है। gcc.gnu.org/wiki/DontUseInlineAsm )।
पीटर कॉर्ड्स

यह भी देखें Collatz-अनुमान सी ++ बनाम हाथ से लिखा एएसएम क्यू एंड ए भी कैसे क्या आप सी संशोधित करने के लिए जानने के उपयोग करने के लिए पर सुझाव मनोरंजन के लिए संकलक की धड़कन :) और अधिक जानकारी के लिए ++ संकलक मेकअप बेहतर कोड में मदद करेगा।
पीटर कॉर्ड्स

@PeterCordes तो आप जो कह रहे हैं, आप सहमत हैं।
मडोकी

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

अच्छा, मान लिया। मैं केवल एक आदमी हुआ करता था लेकिन वह 80 का दशक था।
मडोकी

-2

संगठनात्मक स्तर पर अनुकूलन के बाद, c ++ संकलक कोड का उत्पादन करेगा जो लक्षित सीपीयू के कार्यों में निर्मित का उपयोग करेगा। एचएलएल कई कारणों से असेम्बल या आउट-परफॉर्म नहीं करेगा; 1.) HLL संकलित किया जाएगा और Accessor कोड के साथ उत्पादन, सीमा जाँच और संभवतः कचरा संग्रह में बनाया (पूर्व OOP ढंग में गुंजाइश संबोधित) सभी चक्र (flips और फ्लॉप) की आवश्यकता होती है। HLL इन दिनों एक उत्कृष्ट कार्य करता है (नए C ++ सहित और GO जैसे अन्य), लेकिन यदि वे कोडांतरक (अर्थात आपका कोड) की जरूरत है तो आपको सीपीयू दस्तावेज़ीकरण से परामर्श करना होगा- मैला कोड के साथ अपूर्णताएं निश्चित रूप से असम्बद्ध और संकलित लैंग्स जैसे कोडांतरक सभी को हल करती हैं ओएल-कोड एचएलएल के विवरणों को समाप्त कर देता है और उन्हें समाप्त नहीं करता है यदि आप होस्ट ओएस द्वारा मान्यता प्राप्त हैं तो आप ऐप नहीं चलाएंगे।

अधिकांश कोडांतरक कोड (मुख्य रूप से ऑब्जेक्ट्स) आउटपुट के लिए "हेडलेस" होते हैं, अन्य निष्पादन योग्य प्रारूपों में शामिल करने के लिए बहुत कम प्रसंस्करण की आवश्यकता होती है इसलिए यह बहुत तेज़ होगा, लेकिन कहीं अधिक असुरक्षित होगा; यदि एक निष्पादक कोडांतरक (NAsm, YAsm; इत्यादि) द्वारा आउटपुट किया जाता है, तब भी यह तेजी से चलेगा, जब तक कि यह कार्यक्षमता में HLL कोड से पूरी तरह से मेल नहीं खाता है, तब परिणाम सटीक रूप से तौले जा सकते हैं।

किसी भी प्रारूप में HLL से कोडांतरक आधारित कोड ऑब्जेक्ट को कॉल करना स्वाभाविक रूप से चर / निरंतर डेटा प्रकार (यह LLL और HLL दोनों पर लागू होता है) के लिए वैश्विक रूप से आवंटित मेमोरी का उपयोग करके मेमोरी स्पेस कॉल के अलावा प्रोसेसिंग ओवरहेड को जोड़ देगा। याद रखें कि अंतिम आउटपुट सीपीयू का उपयोग अंततः अपने एपीआई और एबी के सापेक्ष हार्डवेयर (ऑपकोड) और दोनों के रूप में करता है, कोडांतरक और "एचएलएल कंपाइलर" अनिवार्य रूप से / मौलिक रूप से समान हैं केवल एकमात्र अपवाद पठनीयता (व्याकरणिक) है।

FAsm का उपयोग कर असेंबलर में हैलो वर्ल्ड कंसोल एप्लिकेशन 1.5 KB है (और यह विंडोज में फ्रीबीएसडी और लिनक्स में भी छोटा है) और जीसीसी अपने सबसे अच्छे दिन में कुछ भी फेंक सकता है; कारणों में नोड्स के साथ अंतर्निहित पैडिंग हैं, कुछ को नाम देने के लिए मान्यता सत्यापन और सीमा की जाँच। वास्तविक लक्ष्य स्वच्छ एचएलएल लिबास और एक अनुकूलन योग्य कंपाइलर है जो "हार्डकोर" तरीके से सीपीयू को लक्षित करता है और इन दिनों (आखिरकार) करता है। GCC, YAsm से बेहतर नहीं है-यह डेवलपर की कोडिंग प्रथाओं और समझ है जो प्रश्न में हैं और "अनुकूलन" नौसिखिया अन्वेषण और अंतरिम प्रशिक्षण और अनुभव के बाद आता है।

कंपाइलरों को आउटपुट के लिए एक असेंबलर के रूप में एक असेंबलर के रूप में लिंक और असेंबल करना पड़ता है क्योंकि वे कोड वे सभी होते हैं जो एक सीपीयू (CISC या RISC [PIC भी]) को छोड़कर होगा। YAsm ने ऑप्टिमाइज़ किया और क्लीन किया, NA NA की शुरुआत में उस असेंबलर से सभी आउटपुट में तेजी आई, लेकिन फिर भी YAsm अभी भी, NAsm की तरह, बाहरी निर्भरता वाले प्रॉडक्टेबल्स का उत्पादन करता है, जो डेवलपर की ओर से OS लाइब्रेरीज़ को टारगेट करते हैं, इसलिए माइलेज अलग-अलग हो सकता है। समापन में C ++ एक ऐसे बिंदु पर है जो विशेष रूप से वाणिज्यिक क्षेत्र में 80+ प्रतिशत के लिए कोडांतरक की तुलना में अविश्वसनीय और अधिक सुरक्षित है ...


1
C और C ++ की कोई सीमा-जाँच नहीं है जब तक कि आप इसके लिए नहीं पूछते हैं, और कोई कचरा संग्रह नहीं है जब तक कि आप इसे स्वयं लागू नहीं करते हैं या लाइब्रेरी का उपयोग नहीं करते हैं। असली सवाल यह है कि क्या कंपाइलर मानव की तुलना में बेहतर लूप (और वैश्विक अनुकूलन) बनाता है। आमतौर पर हां, जब तक कि मानव वास्तव में नहीं जानता कि वे क्या कर रहे हैं और उस पर बहुत समय खर्च करते हैं
पीटर कॉर्डेस

1
आप NASM या YASM (कोई बाहरी कोड) का उपयोग करके स्थिर निष्पादन योग्य नहीं बना सकते हैं। वे दोनों फ्लैट बाइनरी प्रारूप में आउटपुट कर सकते हैं, इसलिए यदि आप वास्तव में नहीं चलाना चाहते हैं, तो आप उन्हें स्वयं ELF हेडर को इकट्ठा कर सकते हैं ld, लेकिन इससे कोई फर्क नहीं पड़ता जब तक आप फ़ाइल आकार के लिए अनुकूलित करने की कोशिश नहीं कर रहे हैं (न केवल आकार। पाठ खंड)। लिनक्स के लिए वास्तव में किशोर ईएलएफ निष्पादन बनाने पर एक बवंडर ट्यूटोरियल देखें ।
पीटर कॉर्डेस

1
शायद आप C # के बारे में सोच रहे हैं, या std::vectorडिबग मोड में संकलित हैं। C ++ सरणियाँ ऐसी नहीं हैं। कंपाइलर कंपाइल समय पर सामान की जांच कर सकते हैं, लेकिन जब तक आप अतिरिक्त सख्त विकल्प सक्षम नहीं करते, कोई रन-टाइम चेकिंग नहीं है। उदाहरण के लिए एक फ़ंक्शन देखें जो एक int array[]arg के पहले 1024 तत्वों को बढ़ाता है । Asm आउटपुट में रनटाइम चेक नहीं है: godbolt.org/g/w1HF5t । यह सब हो जाता है एक सूचक में rdi, कोई आकार जानकारी नहीं है। यह प्रोग्रामर के ऊपर निर्भर है कि इसे 1024 से छोटे एरे के साथ कभी न बुलाए जाने वाले अपरिभाषित व्यवहार से बचें।
पीटर कॉर्डेस

1
आप जो भी बात कर रहे हैं वह एक सादा सी ++ सरणी नहीं है (साथ आवंटित करें new, मैन्युअल रूप से हटाएं delete, कोई सीमा नहीं की जाँच करें)। आप सी ++ का उपयोग करके शिट्ठी फूला हुआ एसम / मशीन-कोड (जैसे अधिकांश सॉफ्टवेयर) का उत्पादन कर सकते हैं, लेकिन यह प्रोग्रामर की गलती है, सी ++ की नहीं। तुम भी allocaएक सरणी के रूप में ढेर अंतरिक्ष आवंटित करने के लिए उपयोग कर सकते हैं ।
पीटर कॉर्डेस

1
पर एक उदाहरण लिंक gcc.godbolt.org की g++ -O3एक सादे सरणी के लिए सीमा की जाँच कोड जनरेट, या जो कुछ भी कर रही है आप के बारे में बात कर रहे हैं। C ++ से फूले हुए बायनेरिज़ को उत्पन्न करना बहुत आसान हो जाता है (और वास्तव में आपको सावधान रहना होगा कि क्या आप प्रदर्शन के लिए लक्ष्य बना रहे हैं), लेकिन यह सचमुच अपरिहार्य नहीं है। यदि आप समझते हैं कि C ++ asm को कैसे संकलित करता है, तो आप कोड प्राप्त कर सकते हैं जो केवल हाथ से लिख सकने से कुछ हद तक बदतर है, लेकिन हाथ से प्रबंधित करने की तुलना में बड़े पैमाने पर inlining और निरंतर प्रसार के साथ।
पीटर कॉर्ड्स

-3

असेंबली तेज़ हो सकती है यदि आपका कंपाइलर बहुत अधिक ओओ सपोर्ट कोड बनाता है ।

संपादित करें:

नीचा दिखाने के लिए: ओपी ने लिखा "क्या मुझे ... सी ++ पर ध्यान केंद्रित करना चाहिए और विधानसभा भाषा के बारे में भूलना चाहिए?" और मैं अपने जवाब से खड़ा हूं। आपको हमेशा OO कोड उत्पन्न करने पर नजर रखने की जरूरत है, खासकर जब विधियों का उपयोग करते हुए। असेंबली लैंग्वेज के बारे में न भूलने का मतलब है कि आप समय-समय पर असेंबली की समीक्षा करेंगे कि आपका ओओ कोड जेनरेट करता है, जो मुझे लगता है कि अच्छा प्रदर्शन करने वाले सॉफ्टवेयर लिखने के लिए जरूरी है।

वास्तव में, यह केवल ओओ नहीं, बल्कि सभी बाध्यकारी कोड से संबंधित है।


2
-1: मैं किसी भी OO फीचर का इस्तेमाल नहीं करता। आपका तर्क "असेंबली भी तेज हो सकता है अगर आपका कंपाइलर एक लाख NOP जोड़ता है।"
०२

मैं स्पष्ट नहीं था, यह वास्तव में एक सी सवाल है। यदि आप C ++ कंपाइलर के लिए C कोड लिखते हैं तो आप C ++ कोड नहीं लिख रहे हैं और आपको कोई OO सामान नहीं मिलेगा। एक बार जब आप वास्तविक C ++ में लिखना शुरू कर देते हैं, तो OO सामान का उपयोग करते हुए आपको OO सपोर्ट कोड न उत्पन्न करने के लिए कंपाइलर प्राप्त करने के लिए बहुत जानकार होना चाहिए।
ओलफ फोर्शेल

तो आपका जवाब सवाल के बारे में नहीं है? (इसके अलावा, स्पष्टीकरण जवाब में जाते हैं, टिप्पणियाँ नहीं। टिप्पणियाँ बिना किसी सूचना, अधिसूचना या इतिहास के कभी भी हटा दी जा सकती हैं।
मूइंग डक

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

2
कम से कम बहुत संभावना नहीं है। C ++ में एक चीज है जिसे जीरो ओवरहेड नियम कहा जाता है, और यह ज्यादातर समय लागू होता है। OO के बारे में अधिक जानें - आपको पता चलेगा कि अंत में यह आपके कोड की पठनीयता में सुधार करता है, कोड की गुणवत्ता में सुधार करता है, कोडिंग गति बढ़ाता है, मजबूती बढ़ाता है। एम्बेडेड के लिए भी - लेकिन C ++ का उपयोग करें क्योंकि यह आपको अधिक नियंत्रण देता है, एम्बेडेड + OO जावा तरीका आपको खर्च करेगा।
ज़ेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.