c - सगढ - सी से विधानसभा कब तेजी से होती है?




छत्तीसगढ़ विधानसभा चुनाव कब होगा (20)

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

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

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


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

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

सीपीयू के एनओपी (कोई ऑपरेशन - कुछ भी नहीं) निर्देश का एक अन्य कारण यह है कि वास्तव में आपका पूरा एप्लिकेशन तेजी से चल सकता है।

[संपादित करें] बेशक, तकनीक एक विशिष्ट हार्डवेयर सेटअप पर निर्भर करती है। मुख्य कारण यह था कि कई अमीगा गेम्स तेजी से CPUs का सामना नहीं कर सके: निर्देशों का समय बंद था।


एक बिंदु जो उत्तर नहीं है।
यहां तक ​​कि यदि आप इसमें कभी भी प्रोग्राम नहीं करते हैं, तो मुझे कम से कम एक असेंबलर निर्देश सेट को जानना उपयोगी लगता है। यह प्रोग्रामर का हिस्सा है और अधिक जानने के लिए कभी खत्म होने वाली खोज नहीं है और इसलिए बेहतर हो। फ्रेमवर्क में कदम उठाने के दौरान भी उपयोगी है, आपके पास स्रोत कोड नहीं है और कम से कम एक मोटा विचार है कि क्या हो रहा है। यह आपको JavaByteCode और .Net IL को समझने में भी मदद करता है क्योंकि वे दोनों असेंबलर के समान हैं।

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

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

मेरा दोस्त माइक्रो नियंत्रकों पर काम करता है, वर्तमान में छोटे इलेक्ट्रिक मोटरों को नियंत्रित करने के लिए चिप्स। वह निम्न स्तर सी और असेंबली के संयोजन में काम करता है। उन्होंने एक बार मुझे काम पर एक अच्छे दिन के बारे में बताया जहां उन्होंने मुख्य लूप को 48 निर्देशों से 43 तक घटा दिया। उन्हें 256 के चिप को भरने के लिए कोड बढ़ने के विकल्प भी सामने आए हैं और व्यवसाय एक नई सुविधा चाहते हैं, क्या आप

  1. एक मौजूदा सुविधा को हटा दें
  2. प्रदर्शन की लागत पर कुछ या सभी मौजूदा सुविधाओं के आकार को कम करें।
  3. वकील एक उच्च चिप, उच्च शक्ति खपत और बड़े रूप कारक के साथ एक बड़ी चिप में आगे बढ़ रहा है।

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

मुझे पता है कि मैंने सवाल का जवाब दिया है कि "मुझे असेंबलर क्यों सीखना चाहिए" लेकिन मुझे लगता है कि यह एक और महत्वपूर्ण सवाल है, तो यह तेज़ी से कब होता है।

तो चलिए एक बार फिर कोशिश करें आपको असेंबली के बारे में सोचना चाहिए

  • निम्न स्तर के ऑपरेटिंग सिस्टम समारोह पर काम कर रहे हैं
  • एक कंपाइलर पर काम करना।
  • एक बेहद सीमित चिप, एम्बेडेड सिस्टम आदि पर काम करना

यह देखने के लिए उत्पन्न संकलक को अपनी असेंबली की तुलना करना याद रखें जो तेज़ / छोटा / बेहतर है।

डेविड।


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

#include "stdafx.h"
#include <windows.h>

float KahanSum
(
  const float *data,
  int n
)
{
   float
     sum = 0.0f,
     C = 0.0f,
     Y,
     T;

   for (int i = 0 ; i < n ; ++i)
   {
      Y = *data++ - C;
      T = sum + Y;
      C = T - sum - Y;
      sum = T;
   }

   return sum;
}

float AsmSum
(
  const float *data,
  int n
)
{
  float
    result = 0.0f;

  _asm
  {
    mov esi,data
    mov ecx,n
    fldz
    fldz
l1:
    fsubr [esi]
    add esi,4
    fld st(0)
    fadd st(0),st(2)
    fld st(0)
    fsub st(0),st(3)
    fsub st(0),st(2)
    fstp st(2)
    fstp st(2)
    loop l1
    fstp result
    fstp result
  }

  return result;
}

int main (int, char **)
{
  int
    count = 1000000;

  float
    *source = new float [count];

  for (int i = 0 ; i < count ; ++i)
  {
    source [i] = static_cast <float> (rand ()) / static_cast <float> (RAND_MAX);
  }

  LARGE_INTEGER
    start,
    mid,
    end;

  float
    sum1 = 0.0f,
    sum2 = 0.0f;

  QueryPerformanceCounter (&start);

  sum1 = KahanSum (source, count);

  QueryPerformanceCounter (&mid);

  sum2 = AsmSum (source, count);

  QueryPerformanceCounter (&end);

  cout << "  C code: " << sum1 << " in " << (mid.QuadPart - start.QuadPart) << endl;
  cout << "asm code: " << sum2 << " in " << (end.QuadPart - mid.QuadPart) << endl;

  return 0;
}

और मेरे पीसी से कुछ संख्याएं एक डिफ़ॉल्ट रिलीज बिल्ड * चला रही हैं :

  C code: 500137 in 103884668
asm code: 500137 in 52129147

ब्याज से, मैंने लूप को एक डीसी / जेएनजेड के साथ बदल दिया और इससे समय में कोई फर्क नहीं पड़ता - कभी-कभी तेज, कभी-कभी धीमा। मुझे लगता है कि स्मृति सीमित पहलू अन्य अनुकूलन बौने करता है।

ओह, मैं कोड का थोड़ा अलग संस्करण चला रहा था और इसने संख्याओं को गलत तरीके से गोल किया (यानी सी तेज था!)। फिक्स्ड और परिणाम अद्यतन।


किसी विशिष्ट उदाहरण या प्रोफाइलर सबूत देने के बिना, जब आप कंपाइलर से अधिक जानते हैं तो आप कंपाइलर से बेहतर असेंबलर लिख सकते हैं।

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

दूसरी तरफ, ऐसे मामले हैं जहां कंपाइलर के पास ज्यादा जानकारी नहीं है - मैं मुख्य रूप से बाहरी हार्डवेयर के विभिन्न रूपों के साथ काम करते समय कहूंगा, जिसमें संकलक को कोई ज्ञान नहीं है। प्राथमिक उदाहरण शायद डिवाइस चालक होने के नाते, जहां असेंबलर हार्डवेयर के मानव के घनिष्ठ ज्ञान के साथ मिलकर सवाल उठाता है, सी संकलक के मुकाबले बेहतर परिणाम मिल सकता है।

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


मुझे आश्चर्य है कि कोई भी यह नहीं कहता है। विधानसभा में लिखे गए strlen() फ़ंक्शन बहुत तेज हैं! सी में, सबसे अच्छी बात यह है कि आप कर सकते हैं

int c;
for(c = 0; str[c] != '\0'; c++) {}

जबकि असेंबली में आप इसे काफी तेज कर सकते हैं:

mov esi, offset string
mov edi, esi
xor ecx, ecx

lp:
mov ax, byte ptr [esi]
cmp al, cl
je  end_1
cmp ah, cl
je end_2
mov bx, byte ptr [esi + 2]
cmp bl, cl
je end_3
cmp bh, cl
je end_4
add esi, 4
jmp lp

end_4:
inc esi

end_3:
inc esi

end_2:
inc esi

end_1:
inc esi

mov ecx, esi
sub ecx, edi

लंबाई ecx में है। यह समय पर 4 वर्णों की तुलना करता है, इसलिए यह 4 गुना तेज है। और ईएक्स और ईबीएक्स के उच्च आदेश शब्द का उपयोग करने के बारे में सोचें, यह पिछले सी दिनचर्या से 8 गुना तेज हो जाएगा!


मेरे काम में, मुझे असेंबली जानने और उपयोग करने के तीन कारण हैं। महत्व के क्रम में:

  1. डिबगिंग - मुझे अक्सर लाइब्रेरी कोड मिलता है जिसमें बग या अपूर्ण दस्तावेज होते हैं। मैं समझता हूं कि यह असेंबली स्तर पर कदम उठाकर क्या कर रहा है। मुझे सप्ताह में एक बार ऐसा करना है। मैं इसे समस्याओं को डीबग करने के लिए एक उपकरण के रूप में भी उपयोग करता हूं जिसमें मेरी आंखें सी / सी ++ / सी # में बेवकूफ त्रुटि नहीं डालती हैं। असेंबली को देखते हुए वह पिछले हो जाता है।

  2. अनुकूलन - संकलक अनुकूलन में काफी अच्छा करता है, लेकिन मैं अधिक से अधिक एक अलग ballpark में खेलते हैं। मैं छवि प्रसंस्करण कोड लिखता हूं जो आमतौर पर कोड के साथ शुरू होता है जो इस तरह दिखता है:

    for (int y=0; y < imageHeight; y++) {
        for (int x=0; x < imageWidth; x++) {
           // do something
        }
    }
    

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

  3. कुछ ऐसा करने से मुझे भाषा नहीं देगी। इनमें शामिल हैं - प्रोसेसर आर्किटेक्चर और विशिष्ट प्रोसेसर फीचर्स प्राप्त करना, सीपीयू में नहीं झंडे तक पहुंचना (आदमी, मैं वास्तव में चाहता हूं कि सी ने आपको लेयर फ्लैग तक पहुंच दी हो), आदि। मैं इसे साल या दो साल में एक बार कर सकता हूं।


संक्षिप्त जवाब? कभी कभी।

तकनीकी रूप से प्रत्येक अमूर्तता की लागत होती है और एक प्रोग्रामिंग भाषा सीपीयू कैसे काम करती है इसके लिए एक अमूर्त है। सी हालांकि बहुत करीब है। सालों पहले मुझे अपने यूनिक्स खाते में लॉग इन करते समय जोर से हँसते हुए याद आया और निम्नलिखित भाग्य संदेश मिला (जब ऐसी चीजें लोकप्रिय थीं):

सी प्रोग्रामिंग भाषा - एक ऐसी भाषा जो विधानसभा भाषा की शक्ति के साथ असेंबली भाषा की लचीलापन को जोड़ती है।

यह मजाकिया है क्योंकि यह सच है: सी पोर्टेबल असेंबली भाषा की तरह है।

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

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

मैं आपको ये सब बताता हूं क्योंकि सी और असेंबलर की गति के बारे में कोई कंबल नियम नहीं है क्योंकि सी के लिए कोई उद्देश्य मानक नहीं है।

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

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

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

अभी भी अनुकूलक हैं जो आप असेंबलर में कर सकते हैं कि कोई कंपाइलर नहीं बना सकता है और एक अच्छी तरह लिखित असेंबलर एल्गोर्थम इसके सी समकक्ष की तुलना में तेज या तेज़ होगा। बड़ा सवाल यह है: क्या यह इसके लायक है?

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


हालांकि सी 8-बिट, 16-बिट, 32-बिट, 64-बिट डेटा के निम्न-स्तरीय हेरफेर के "बंद" है, फिर भी कुछ गणितीय संचालन सी द्वारा समर्थित नहीं हैं जिन्हें अक्सर कुछ असेंबली निर्देशों में सुंदर ढंग से निष्पादित किया जा सकता है सेट:

  1. फिक्स्ड-पॉइंट गुणा: दो 16-बिट संख्याओं का उत्पाद 32-बिट संख्या है। लेकिन सी में नियम कहते हैं कि दो 16-बिट संख्याओं का उत्पाद 16-बिट संख्या है, और दो 32-बिट संख्याओं का उत्पाद 32-बिट संख्या है - दोनों मामलों में नीचे आधा। यदि आप चाहते हैं कि 16x16 गुणा या अधिकतम 32x32 गुणा करें, तो आपको कंपाइलर के साथ गेम खेलना होगा। सामान्य विधि एक बड़ी से अधिक आवश्यक चौड़ाई में डालना है, गुणा करें, स्थानांतरित करें, और वापस कास्ट करें:

    int16_t x, y;
    // int16_t is a typedef for "short"
    // set x and y to something
    int16_t prod = (int16_t)(((int32_t)x*y)>>16);`
    

    इस मामले में संकलक यह जानकर काफी समझदार हो सकता है कि आप वास्तव में 16x16 गुणा के शीर्ष आधे हिस्से को प्राप्त करने की कोशिश कर रहे हैं और मशीन के मूल 16x16multiply के साथ सही काम करते हैं। या यह बेवकूफ हो सकता है और 32x32 को गुणा करने के लिए लाइब्रेरी कॉल की आवश्यकता होती है क्योंकि आपको केवल उत्पाद की 16 बिट्स की आवश्यकता होती है - लेकिन सी मानक आपको स्वयं को व्यक्त करने का कोई तरीका नहीं देता है।

  2. कुछ बिट्सफिफ्टिंग ऑपरेशंस (रोटेशन / कैरीज़):

    // 256-bit array shifted right in its entirety:
    uint8_t x[32];
    for (int i = 32; --i > 0; )
    {
       x[i] = (x[i] >> 1) | (x[i-1] << 7);
    }
    x[0] >>= 1;
    

    यह सी में बहुत ही सुरुचिपूर्ण नहीं है, लेकिन फिर भी, जब तक कि संकलक यह समझने के लिए पर्याप्त समझदार न हो कि आप क्या कर रहे हैं, यह बहुत सारे "अनावश्यक" काम करने जा रहा है। कई असेंबली निर्देश सेट आपको लेयर रजिस्टर में परिणाम के साथ बाएं / दाएं घुमाने या स्थानांतरित करने की अनुमति देते हैं, ताकि आप ऊपर दिए गए 34 निर्देशों को पूरा कर सकें: सरणी की शुरुआत में पॉइंटर लोड करें, लेयर साफ़ करें, और 32 8- पॉइंटर पर ऑटो-इंक्रिमेंट का उपयोग करके बिट राइट-शिफ्ट।

    एक और उदाहरण के लिए, रैखिक फीडबैक शिफ्ट रजिस्ट्रार (एलएफएसआर) हैं जो असेंबली में सुंदर ढंग से प्रदर्शन किए जाते हैं: एन बिट्स (8, 16, 32, 64, 128, इत्यादि) का एक हिस्सा लें, पूरी चीज को 1 से सही करें (ऊपर देखें एल्गोरिदम), तो यदि परिणामी वाहक 1 है तो आप XOR को थोड़ा पैटर्न में दर्शाते हैं जो बहुपद का प्रतिनिधित्व करता है।

ऐसा कहकर, मैं इन तकनीकों का सहारा नहीं लेगा जब तक कि मुझे गंभीर प्रदर्शन बाधाएं न हों। जैसा कि अन्य ने कहा है, सी कोड की तुलना में दस्तावेज़ / डीबग / परीक्षण / रखरखाव के लिए असेंबली बहुत कठिन है: प्रदर्शन लाभ कुछ गंभीर लागतों के साथ आता है।

संपादित करें: 3. असेंबली में अतिप्रवाह पहचान संभव है (वास्तव में इसे सी में नहीं कर सकता), इससे कुछ एल्गोरिदम बहुत आसान हो जाते हैं।



A few examples from my experience:

  • Access to instructions that are not accessible from C. For instance, many architectures (like x86-64, IA-64, DEC Alpha, and 64-bit MIPS or PowerPC) support a 64 bit by 64 bit multiplication producing a 128 bit result. GCC recently added an extension providing access to such instructions, but before that assembly was required. And access to this instruction can make a huge difference on 64-bit CPUs when implementing something like RSA - sometimes as much as a factor of 4 improvement in performance.

  • Access to CPU-specific flags. The one that has bitten me a lot is the carry flag; when doing a multiple-precision addition, if you don't have access to the CPU carry bit one must instead compare the result to see if it overflowed, which takes 3-5 more instructions per limb; and worse, which are quite serial in terms of data accesses, which kills performance on modern superscalar processors. When processing thousands of such integers in a row, being able to use addc is a huge win (there are superscalar issues with contention on the carry bit as well, but modern CPUs deal pretty well with it).

  • SIMD. Even autovectorizing compilers can only do relatively simple cases, so if you want good SIMD performance it's unfortunately often necessary to write the code directly. Of course you can use intrinsics instead of assembly but once you're at the intrinsics level you're basically writing assembly anyway, just using the compiler as a register allocator and (nominally) instruction scheduler. (I tend to use intrinsics for SIMD simply because the compiler can generate the function prologues and whatnot for me so I can use the same code on Linux, OS X, and Windows without having to deal with ABI issues like function calling conventions, but other than that the SSE intrinsics really aren't very nice - the Altivec ones seem better though I don't have much experience with them). As examples of things a (current day) vectorizing compiler can't figure out, read about bitslicing AES or SIMD error correction - one could imagine a compiler that could analyze algorithms and generate such code, but it feels to me like such a smart compiler is at least 30 years away from existing (at best).

On the other hand, multicore machines and distributed systems have shifted many of the biggest performance wins in the other direction - get an extra 20% speedup writing your inner loops in assembly, or 300% by running them across multiple cores, or 10000% by running them across a cluster of machines. And of course high level optimizations (things like futures, memoization, etc) are often much easier to do in a higher level language like ML or Scala than C or asm, and often can provide a much bigger performance win. So, as always, there are tradeoffs to be made.


How about creating machine code at run-time?

My brother once (around 2000) realised an extremely fast real-time ray-tracer by generating code at run-time. I can't remember the details, but there was some kind of main module which was looping through objects, then it was preparing and executing some machine code which was specific to each object.

However, over time, this method was outruled by new graphics hardware, and it became useless.

Today, I think that possibly some operations on big-data (millions of records) like pivot tables, drilling, calculations on-the-fly, etc. could be optimized with this method. The question is: is the effort worth it?


I can't give the specific examples because it was too many years ago, but there were plenty of cases where hand-written assembler could out-perform any compiler. Reasons why:

  • You could deviate from calling conventions, passing arguments in registers.

  • You could carefully consider how to use registers, and avoid storing variables in memory.

  • For things like jump tables, you could avoid having to bounds-check the index.

Basically, compilers do a pretty good job of optimizing, and that is nearly always "good enough", but in some situations (like graphics rendering) where you're paying dearly for every single cycle, you can take shortcuts because you know the code, where a compiler could not because it has to be on the safe side.

In fact, I have heard of some graphics rendering code where a routine, like a line-draw or polygon-fill routine, actually generated a small block of machine code on the stack and executed it there, so as to avoid continual decision-making about line style, width, pattern, etc.

That said, what I want a compiler to do is generate good assembly code for me but not be too clever, and they mostly do that. In fact, one of the things I hate about Fortran is its scrambling the code in an attempt to "optimize" it, usually to no significant purpose.

Usually, when apps have performance problems, it is due to wasteful design. These days, I would never recommend assembler for performance unless the overall app had already been tuned within an inch of its life, still was not fast enough, and was spending all its time in tight inner loops.

Added: I've seen plenty of apps written in assembly language, and the main speed advantage over a language like C, Pascal, Fortran, etc. was because the programmer was far more careful when coding in assembler. He or she is going to write roughly 100 lines of code a day, regardless of language, and in a compiler language that's going to equal 3 or 400 instructions.


I have read all the answers (more than 30) and didn't find a simple reason: assembler is faster than C if you have read and practiced the Intel® 64 and IA-32 Architectures Optimization Reference Manual , so the reason why assembly may be slower is that people who write such slower assembly didn't read the Optimization Manual .

In the good old days of Intel 80286, each instruction was executed at a fixed count of CPU cycles, but since Pentium Pro, released in 1995, Intel processors became superscalar, utilizing Complex Pipelining: Out-of-Order Execution & Register Renaming. Before that, on Pentium, produced 1993, there were U and V pipelines: dual pipe lines that could execute two simple instructions at one clock cycle if they didn't depend on one another; but this was nothing to compare of what is Out-of-Order Execution & Register Renaming appeared in Pentium Pro, and almost left unchanged nowadays.

To explain in a few words, fastest code is where instructions do not depend on previous results, eg you should always clear whole registers (by movzx) or use add rax, 1 instead or inc rax to remove dependency on previous state of flags, etc.

You can read more on Out-of-Order Execution & Register Renaming if time permits, there is plenty information available in the Internet.

There are also other important issues like branch prediction, number of load and store units, number of gates that execute micro-ops, etc, but the most important thing to consider is namely the Out-of-Order Execution.

Most people are simply not aware about the Out-of-Order Execution, so they write their assembly programs like for 80286, expecting their instruction will take a fixed time to execute regardless of context; while C compilers are aware of the Out-of-Order Execution and generate the code correctly. That's why the code of such unaware people is slower, but if you will become aware, your code will be faster.


I think the general case when assembler is faster is when a smart assembly programmer looks at the compiler's output and says "this is a critical path for performance and I can write this to be more efficient" and then that person tweaks that assembler or rewrites it from scratch.


Longpoke, there is just one limitation: time. When you don't have the resources to optimize every single change to code and spend your time allocating registers, optimize few spills away and what not, the compiler will win every single time. You do your modification to the code, recompile and measure. Repeat if necessary.

Also, you can do a lot in the high-level side. Also, inspecting the resulting assembly may give the IMPRESSION that the code is crap, but in practice it will run faster than what you think would be quicker. उदाहरण:

int y = data[i]; // do some stuff here.. call_function(y, ...);

The compiler will read the data, push it to stack (spill) and later read from stack and pass as argument. Sounds shite? It might actually be very effective latency compensation and result in faster runtime.

// optimized version call_function(data[i], ...); // not so optimized after all..

The idea with the optimized version was, that we have reduced register pressure and avoid spilling. But in truth, the "shitty" version was faster!

Looking at the assembly code, just looking at the instructions and concluding: more instructions, slower, would be a misjudgment.

The thing here to pay attention is: many assembly experts think they know a lot, but know very little. The rules change from architecture to next, too. There is no silver-bullet x86 code, for example, which is always the fastest. These days is better to go by rules-of-thumb:

  • memory is slow
  • cache is fast
  • try to use cached better
  • how often you going to miss? do you have latency compensation strategy?
  • you can execute 10-100 ALU/FPU/SSE instructions for one single cache miss
  • application architecture is important..
  • .. but it does't help when the problem isn't in the architecture

Also, trusting too much into compiler magically transforming poorly-thought-out C/C++ code into "theoretically optimum" code is wishful thinking. You have to know the compiler and tool chain you use if you care about "performance" at this low-level.

Compilers in C/C++ are generally not very good at re-ordering sub-expressions because the functions have side effects, for starters. Functional languages don't suffer from this caveat but don't fit the current ecosystem that well. There are compiler options to allow relaxed precision rules which allow order of operations to be changed by the compiler/linker/code generator.

This topic is a bit of a dead-end; for most it's not relevant, and the rest, they know what they are doing already anyway.

It all boils down to this: "to understand what you are doing", it's a bit different from knowing what you are doing.


Matrix operations using SIMD instructions is probably faster than compiler generated code.


One of the more famous snippets of assembly is from Michael Abrash's texture mapping loop ( expained in detail here ):

add edx,[DeltaVFrac] ; add in dVFrac
sbb ebp,ebp ; store carry
mov [edi],al ; write pixel n
mov al,[esi] ; fetch pixel n+1
add ecx,ebx ; add in dUFrac
adc esi,[4*ebp + UVStepVCarry]; add in steps

Nowadays most compilers express advanced CPU specific instructions as intrinsics, ie, functions that get compiled down to the actual instruction. MS Visual C++ supports intrinsics for MMX, SSE, SSE2, SSE3, and SSE4, so you have to worry less about dropping down to assembly to take advantage of platform specific instructions. Visual C++ can also take advantage of the actual architecture you are targetting with the appropriate /ARCH setting.


One of the posibilities to the CP/M-86 version of PolyPascal (sibling to Turbo Pascal) was to replace the "use-bios-to-output-characters-to-the-screen" facility with a machine language routine which in essense was given the x, and y, and the string to put there.

This allowed to update the screen much, much faster than before!

There was room in the binary to embed machine code (a few hundred bytes) and there was other stuff there too, so it was essential to squeeze as much as possible.

It turnes out that since the screen was 80x25 both coordinates could fit in a byte each, so both could fit in a two-byte word. This allowed to do the calculations needed in fewer bytes since a single add could manipulate both values simultaneously.

To my knowledge there is no C compilers which can merge multiple values in a register, do SIMD instructions on them and split them out again later (and I don't think the machine instructions will be shorter anyway).


Tight loops, like when playing with images, since an image may cosist of millions of pixels. Sitting down and figuring out how to make best use of the limited number of processor registers can make a difference. Here's a real life sample:

http://danbystrom.se/2008/12/22/optimizing-away-ii/

Then often processors have some esoteric instructions which are too specialized for a compiler to bother with, but on occasion an assembler programmer can make good use of them. Take the XLAT instruction for example. Really great if you need to do table look-ups in a loop and the table is limited to 256 bytes!

Updated: Oh, just come to think of what's most crucial when we speak of loops in general: the compiler has often no clue on how many iterations that will be the common case! Only the programmer know that a loop will be iterated MANY times and that it therefore will be beneficial to prepare for the loop with some extra work, or if it will be iterated so few times that the set-up actually will take longer than the iterations expected.


You don't actually know whether your well-written C code is really fast if you haven't looked at the disassembly of what compiler produces. Many times you look at it and see that "well-written" was subjective.

So it's not necessary to write in assembler to get fastest code ever, but it's certainly worth to know assembler for the very same reason.





assembly