सी से अधिक तेजी से विधानसभा कब होती है?


475

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

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

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


17
वास्तव में संकलित कोड में सुधार करना काफी तुच्छ है। असेंबली लैंग्वेज और C के ठोस ज्ञान वाला कोई भी व्यक्ति उत्पन्न कोड की जांच करके इसे देख सकता है। जब आप संकलित संस्करण में डिस्पोजेबल रजिस्टरों से बाहर निकलते हैं, तो कोई भी आसान पहला प्रदर्शन क्लिफ होता है। औसतन कंपाइलर किसी बड़े प्रोजेक्ट के लिए इंसान से बेहतर प्रदर्शन करेगा, लेकिन संकलित कोड में प्रदर्शन के मुद्दों को खोजने के लिए एक सभ्य आकार की परियोजना में यह मुश्किल नहीं है।
old_timer

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

11
मैं दृढ़ता से असहमत हूं कि इस सवाल का जवाब "राय आधारित" होने की आवश्यकता है - वे काफी उद्देश्यपूर्ण हो सकते हैं - यह पसंदीदा पालतू भाषाओं के प्रदर्शन की तुलना करने की कोशिश करने जैसा कुछ नहीं है, जिसके लिए प्रत्येक के पास मजबूत बिंदु होंगे और बैक ड्रॉ होंगे। यह समझने का विषय है कि संकलक हमें कितना दूर ले जा सकते हैं, और किस बिंदु से इसे लेना बेहतर है।
jsbueno

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

3
जब तक आपके मस्तिष्क में एक -O3झंडा नहीं होता है, आप शायद सी कंपाइलर के लिए अनुकूलन छोड़ना बेहतर
समझते हैं

जवाबों:


271

यहाँ एक वास्तविक दुनिया उदाहरण है: पुराने संकलक पर फिक्स्ड बिंदु गुणक।

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


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

  • 64 बिट पूर्णांक गुणन का उच्च भाग प्राप्त करना : uint64_t32x32 => 64-बिट गुणकों का उपयोग करने वाला एक पोर्टेबल संस्करण 64-बिट सीपीयू पर अनुकूलन करने में विफल रहता है, इसलिए आपको आंतरिक या __int12864-बिट सिस्टम पर कुशल कोड की आवश्यकता होती है।
  • Windows 32 बिट्स पर _um128 : MSVC हमेशा एक अच्छा काम नहीं करता है, जब 32-बिट पूर्णांकों को 64 में गुणा किया जाता है, इसलिए आंतरिक लोगों ने बहुत मदद की।

C के पास पूर्ण-गुणन ऑपरेटर नहीं है (N-बिट इनपुट्स से 2N-बिट परिणाम)। सी में इसे व्यक्त करने का सामान्य तरीका इनपुट को व्यापक प्रकार में डालना है और उम्मीद है कि संकलक पहचानता है कि इनपुट के ऊपरी बिट्स दिलचस्प हैं:

// on a 32-bit machine, int can hold 32-bit fixed-point integers.
int inline FixedPointMul (int a, int b)
{
  long long a_long = a; // cast to 64 bit.

  long long product = a_long * b; // perform multiplication

  return (int) (product >> 16);  // shift by the fixed point bias
}

इस कोड के साथ समस्या यह है कि हम कुछ ऐसा करते हैं जिसे सीधे सी-भाषा में व्यक्त नहीं किया जा सकता है। हम दो 32 बिट संख्याओं को गुणा करना चाहते हैं और एक 64 बिट परिणाम प्राप्त करते हैं, जिसमें से हम मध्य 32 बिट को वापस करते हैं। हालांकि, सी में यह बहुतायत से मौजूद नहीं है। आप केवल इतना कर सकते हैं कि पूर्णांकों को 64 बिट तक बढ़ावा दें और 64 * 64 = 64 गुणा करें।

x86 (और ARM, MIPS और अन्य) हालांकि एकल निर्देश में गुणा कर सकते हैं। कुछ संकलक इस तथ्य को अनदेखा करते थे और कोड उत्पन्न करते थे जो गुणा करने के लिए रनटाइम लाइब्रेरी फ़ंक्शन को कॉल करता है। 16 से बदलाव भी अक्सर एक पुस्तकालय दिनचर्या द्वारा किया जाता है (x86 इस तरह के बदलाव भी कर सकता है)।

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

यदि आप कोड (इनलाइन) कोड में समान कोड को फिर से लिखते हैं तो आप एक महत्वपूर्ण गति को बढ़ावा दे सकते हैं।

इसके अतिरिक्त: एएसएम का उपयोग समस्या को हल करने का सबसे अच्छा तरीका नहीं है। अधिकांश कंपाइलर आपको आंतरिक रूप में कुछ कोडांतरक निर्देशों का उपयोग करने की अनुमति देते हैं यदि आप उन्हें सी में व्यक्त नहीं कर सकते हैं। उदाहरण के लिए VS.NET2008 कंपाइलर __emul के रूप में 32 * 32 = 64 बिट mul और 64 बिट शिफ्ट __ll_rshift के रूप में उजागर करता है।

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

संदर्भ के लिए: वीएस.नेट कंपाइलर के लिए नियत-बिंदु mul के लिए अंतिम परिणाम है:

int inline FixedPointMul (int a, int b)
{
    return (int) __ll_rshift(__emul(a,b),16);
}

निश्चित बिंदु विभाजन का प्रदर्शन अंतर और भी बड़ा है। मैंने असम्-लाइनों के एक जोड़े को लिखकर डिवीजन हेवी फिक्स्ड पॉइंट कोड के लिए कारक 10 तक सुधार किया था।


दृश्य C ++ 2013 का उपयोग करना दोनों तरीकों के लिए एक ही असेंबली कोड देता है।

2007 से gcc4.1 शुद्ध सी संस्करण को भी अच्छी तरह से अनुकूलित करता है। (गॉडबॉल्ट कंपाइलर एक्सप्लोरर में gcc का कोई भी पुराना संस्करण स्थापित नहीं है, लेकिन संभवतः पुराने GCC संस्करण बिना इन्टिरिनिक्स के भी ऐसा कर सकते हैं।)

God86t संकलक एक्सप्लोरर पर x86 (32-बिट) और एआरएम के लिए स्रोत + एएसएम देखें । (दुर्भाग्य से यह सरल पुराने सी संस्करण से खराब कोड का उत्पादन करने के लिए पुराना कोई भी कंपाइलर नहीं है।)


आधुनिक CPUs बातें सी के लिए ऑपरेटरों की जरूरत नहीं है क्या कर सकते हैं सब पर की तरह, popcntया थोड़ा-स्कैन प्रथम या अंतिम सेट सा लगता है । (POSIX में एक ffs()फ़ंक्शन है, लेकिन इसका शब्दार्थ x86 bsf/ से मेल नहीं खाता है bsrhttps://en.wikipedia.org/wiki/Find_first_set देखें )।

कुछ संकलक कभी-कभी एक लूप को पहचान सकते हैं जो एक पूर्णांक में सेट बिट्स की संख्या को गिनता है और इसे एक popcntनिर्देश पर संकलित करता है (यदि संकलन समय पर सक्षम है), लेकिन यह __builtin_popcntGNU C, या x86 पर उपयोग करने के लिए बहुत अधिक विश्वसनीय है यदि आप केवल हैं SSE4.2 के साथ हार्डवेयर लक्षित करना: _mm_popcnt_u32से<immintrin.h>

या C ++ में, a std::bitset<32>और use को असाइन करें .count()। (यह एक ऐसा मामला है जहां भाषा ने मानक पुस्तकालय के माध्यम से पॉपकाउंट के एक अनुकूलित कार्यान्वयन को आंशिक रूप से उजागर करने का एक तरीका पाया है, एक तरह से जो हमेशा कुछ सही संकलन करेगा, और जो भी लक्ष्य का समर्थन करता है उसका लाभ उठा सकता है।) https भी देखें : //en.wikipedia.org/wiki/ Hamming_weight#Language_support

इसी तरह, कुछ सी कार्यान्वयनों पर (x86 32-बिट बाइट स्वैप फॉर एंडियन रूपांतरण) के लिए ntohlसंकलन कर सकते हैं bswap


आंतरिक या हाथ से लिखे हुए एसएसएम के लिए एक अन्य प्रमुख क्षेत्र SIMD निर्देशों के साथ मैनुअल वेक्टरकरण है। सरल लूप जैसे कंपाइलर खराब नहीं होते हैं dst[i] += src[i] * 10.0;, लेकिन जब चीजें अधिक जटिल हो जाती हैं, तो अक्सर बुरी तरह से या ऑटो-वेक्टर नहीं करते हैं। उदाहरण के लिए, आपको कुछ भी प्राप्त होने की संभावना नहीं है कि SIMD का उपयोग करके Atoi कैसे लागू किया जाए? स्केलर कोड से संकलक द्वारा स्वचालित रूप से उत्पन्न।


6
कैसे चीजों के बारे में {x = c% d; y = c / d;}, कंपाइलर काफी चालाक होते हैं जो कि एक एकल div या idiv बनाते हैं?
जेन्स ब्योर्नहार्गर 30'10

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

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

6
@ स्लैकर वास्तव में, यहां कोड काफी पठनीय है: इनलाइन कोड एक अद्वितीय ऑपरेशन करता है, जो कि विधि हस्ताक्षर को पढ़ने के तुरंत समझ में आता है। कोड केवल धीरे-धीरे पठनीयता में खो जाता है जब अस्पष्ट निर्देश का उपयोग किया जाता है। यहाँ क्या मायने रखता है, हमारे पास एक ऐसा तरीका है जो केवल एक स्पष्ट रूप से पहचान योग्य ऑपरेशन करता है, और यह वास्तव में पठनीय कोड इन परमाणु कार्यों का उत्पादन करने का सबसे अच्छा तरीका है। वैसे, यह इतनी छोटी सी टिप्पणी नहीं है जैसे / * (a * b) >> 16 * / इसे तुरंत समझा नहीं सकते।
डेरेकसन

5
निष्पक्ष होना, यह उदाहरण है एक गरीब, कम से कम आज। सी कंपाइलर लंबे समय से 32x32 करने में सक्षम हैं -> 64 गुणा भले ही भाषा इसे सीधे पेश न करे: वे मानते हैं कि जब आप 32-बिट तर्क 64-बिट में डालते हैं और फिर उन्हें गुणा करते हैं, तो इसकी आवश्यकता नहीं होती है एक पूर्ण 64-बिट गुणा करें, लेकिन एक 32x32 -> 64 ठीक काम करेगा। मैंने जांच की और उनके वर्तमान संस्करण में सभी क्लैंग, जीसीसी और एमएसवीसी को यह अधिकार प्राप्त है । यह नया नहीं है - मुझे याद है कि कंपाइलर आउटपुट को देखना और यह एक दशक पहले की सूचना है।
मधुमक्खी पालन

143

कई साल पहले मैं किसी को सी में प्रोग्राम करना सिखा रहा था। एक्सरसाइज को ग्राफिक को 90 डिग्री से घुमाना था। वह एक समाधान के साथ वापस आया जिसे पूरा करने में कई मिनट लगे, मुख्यतः क्योंकि वह कई गुणा और भाग आदि का उपयोग कर रहा था।

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

मुझे अभी-अभी एक अनुकूलन कंपाइलर मिला था और उसी कोड ने ग्राफिक को <5 सेकंड में घुमाया। मैंने असेंबली कोड को देखा कि कंपाइलर जेनरेट कर रहा था, और जो मैंने वहां देखा, उससे तय हुआ और फिर कोड लिखने के मेरे दिन खत्म हो गए।


3
हाँ, यह एक बिट मोनोक्रोम सिस्टम था, विशेष रूप से यह अटारी एसटी पर मोनोक्रोम छवि ब्लॉक था।
lilburne

16
क्या अनुकूलन संकलनकर्ता मूल कार्यक्रम या आपके संस्करण को संकलित करता है?
थोर्बोजर्न रावन एंडरसन

किस प्रोसेसर पर? 8086 पर, मुझे उम्मीद है कि 8x8 रोटेट के लिए इष्टतम कोड add di,di / adc al,al / add di,di / adc ah,ahसभी आठ 8-बिट रजिस्टरों के लिए SI, रिपीट आदि का उपयोग करके 16 बिट डेटा के साथ DI को लोड करेगा , फिर सभी 8 रजिस्टरों को फिर से करेगा, और फिर पूरी प्रक्रिया तीन को दोहराएगा। अधिक बार, और अंत में कुल्हाड़ी / bx / cx / dx में चार शब्दों को सहेजें। किसी भी तरह से एक असेंबलर के करीब आने वाला नहीं है।
सुपरकैट

1
मैं वास्तव में किसी भी प्लेटफ़ॉर्म के बारे में नहीं सोच सकता, जहां एक कंपाइलर एक कारक के भीतर या 8x8 घुमाव के लिए इष्टतम कोड के दो होने की संभावना हो।
सुपरकैट

65

बहुत ज्यादा किसी भी समय कंपाइलर फ्लोटिंग पॉइंट कोड देखता है, यदि आप एक पुराने खराब कंपाइलर का उपयोग कर रहे हैं तो एक हाथ से लिखा संस्करण जल्दी हो जाएगा। ( 2019 अपडेट: यह आधुनिक कंपाइलरों के लिए सामान्य रूप से सही नहीं है। विशेष रूप से x87 के अलावा किसी अन्य चीज के लिए संकलन करते समय, कंपाइलरों के पास SSE2 या AVX के साथ स्केलर गणित के लिए एक आसान समय होता है, या किसी भी गैर- x86 के साथ एक फ्लैट FP रजिस्टर सेट होता है, x87 के विपरीत स्टैक पंजीकृत करें।)

प्राथमिक कारण यह है कि कंपाइलर कोई मजबूत अनुकूलन नहीं कर सकता है। विषय पर चर्चा के लिए MSDN के इस लेख को देखें । यहाँ एक उदाहरण है जहां असेंबली संस्करण C संस्करण के रूप में गति से दोगुना है (VS2K5 के साथ संकलित):

#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

रुचि से बाहर, मैंने एक डिक / jnz के साथ लूप को स्वैप किया और इससे समय पर कोई फर्क नहीं पड़ा - कभी तेज, कभी धीमा। मुझे लगता है कि स्मृति सीमित पहलू अन्य ऑप्टिमाइज़ेशन को बौना करता है। (संपादक का ध्यान दें: अधिक संभावना है कि एफपी लेटेंसी टोंटी की अतिरिक्त लागत को छिपाने के लिए पर्याप्त है loop। विषम / समान तत्वों के समानांतर दो कहन योग करना, और अंत में उन लोगों को जोड़ना, शायद 2 के कारक द्वारा इसे गति दे सकते हैं। )

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


20
या जीसीसी में, आप फ़्लैगिंग पॉइंट ऑप्टिमाइज़ेशन पर कंपाइलर के हाथों को खोल सकते हैं (जब तक कि आप फ़्लैग का उपयोग करके असीम या NaNs के साथ कुछ भी नहीं करने का वादा करते हैं) -ffast-math। उनके पास एक अनुकूलन स्तर है, -Ofastजो वर्तमान में इसके बराबर है -O3 -ffast-math, लेकिन भविष्य में अधिक अनुकूलन शामिल हो सकते हैं जो कोने के मामलों में गलत कोड पीढ़ी (जैसे कोड जो IEEE NaNs पर निर्भर करता है) हो सकता है।
डेविड स्टोन

2
हाँ, फ़्लोट्स कम्यूटेटिव नहीं हैं, कंपाइलर को वही करना चाहिए जो आपने लिखा है, मूल रूप से @DavidStone ने जो कहा है।
एलेक टीले

2
क्या आपने SSE गणित की कोशिश की? प्रदर्शन एक कारण था कि MS ने x87_64 में पूरी तरह से x87 को छोड़ दिया और x86 में 80-बिट लंबा डबल
phuclv

4
@Praxeolitic: FP ऐड कम्यूटेटिव ( a+b == b+a) है, लेकिन एसोसिएटिव ( ऑपरेशंस को रीऑर्डर करना नहीं है, इसलिए इंटरमीडिएट की राउंडिंग अलग है)। पुन: यह कोड: मुझे नहीं लगता कि अधूरा x87 और एक loopनिर्देश तेज गति का एक बहुत ही भयानक प्रदर्शन है। loopजाहिरा तौर पर एफपी विलंबता के कारण वास्तव में एक अड़चन नहीं है। मुझे यकीन नहीं है कि वह एफपी ऑपरेशन को पाइपलाइन कर रहा है या नहीं; x87 मनुष्य के लिए पढ़ना कठिन है। fstp resultsअंत में दो शिलालेख स्पष्ट रूप से इष्टतम नहीं हैं। स्टैक से अतिरिक्त परिणाम को चुनना गैर-स्टोर के साथ बेहतर होगा। fstp st(0)IIRC की तरह ।
पीटर कॉर्ड्स

2
@PeterCordes: जोड़-घटाव करने का एक दिलचस्प परिणाम यह है कि जबकि 0 + x और x + 0 एक-दूसरे के बराबर हैं, न तो हमेशा x के बराबर होता है।
सुपरकैट

58

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

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

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

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


आम तौर पर, यह कथन सत्य है। कंपाइलर यह DWIW के लिए सबसे अच्छा है, लेकिन कुछ एज मामलों में कोडिंग असेंबलर को काम मिल जाता है, जब रियलटाइम प्रदर्शन एक होना चाहिए।
स्पूल्सन

1
@ लिडमैन: "यह एक मानव की तुलना में निर्देशों को तेज करने की कोशिश कर सकता है"। OCaml को तेजी से और, आश्चर्यजनक रूप से, इसके मूल-कोड संकलक के ocamloptअनुदेशों को x86 पर शेड्यूल करने के लिए जाना जाता है और इसके बजाय, इसे सीपीयू तक छोड़ देता है क्योंकि यह रन-टाइम पर अधिक प्रभावी ढंग से पुनः व्यवस्थित हो सकता है।
जॉन हैरोप

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

48

मेरी नौकरी में, मेरे लिए विधानसभा को जानने और उपयोग करने के तीन कारण हैं। महत्व के क्रम में:

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

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

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

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

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


आप अपने छोरों टाइल नहीं है? :-)
जॉन हैरोप

1
@ प्रश्न: आप "स्क्रैपिंग साइकिल" का क्या मतलब है?
lang2

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

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

@ जेम्सएम.ले: यदि आप केवल हर तत्व को एक बार छूते हैं, तो बेहतर ट्रैवर्सल ऑर्डर आपको स्थानिक स्थान दे सकता है। (उदाहरण के लिए, एक कैश लाइन के सभी बाइट्स का उपयोग करें जिसे आपने छुआ था, बजाय एक प्रति कैश लाइन का उपयोग करके मैट्रिक्स के कॉलम को लूप करने के लिए।)
पीटर कॉर्ड्स

42

केवल कुछ विशेष उद्देश्य निर्देश का उपयोग करते समय संकलक समर्थन नहीं करता है।

एक आधुनिक सीपीयू की कंप्यूटिंग शक्ति को कई पाइपलाइनों और भविष्य कहनेवाला शाखाओं के साथ अधिकतम करने के लिए आपको असेंबली प्रोग्राम को इस तरह से तैयार करने की आवश्यकता होती है जिससे इसे a) मानव के लिए लिखना मुश्किल हो जाता है b) बनाए रखना और भी असंभव।

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


4
+1, भले ही अंतिम वाक्य वास्तव में इस चर्चा में शामिल नहीं है - कोई यह मान लेगा कि एल्गोरिदम आदि के सभी संभावित सुधारों के बाद ही असेंबलर खेलने में आता है।
मेघी

18
@Matt: हाथ से लिखा एएसएम अक्सर एक है बहुत छोटे सीपीयू ईई के काम उस के साथ भद्दा विक्रेता संकलक समर्थन में से कुछ पर बेहतर है।
ज़ैन लिंक्स

5
"केवल कुछ विशेष प्रयोजन निर्देश सेट का उपयोग करते समय" ?? आपने शायद पहले कभी हाथ से अनुकूलित एएसएम कोड का एक टुकड़ा नहीं लिखा है। जिस आर्किटेक्चर पर आप काम कर रहे हैं उसका एक मामूली अंतरंग ज्ञान आपके कंपाइलर से बेहतर कोड (आकार और गति) उत्पन्न करने का अच्छा मौका देता है। जाहिर है, जैसा कि @mghie ने टिप्पणी की है, आप हमेशा सबसे अच्छे एल्गो को कोड करना शुरू करते हैं जो आप समस्या के लिए आ सकते हैं। यहां तक ​​कि बहुत अच्छे संकलक के लिए, आपको वास्तव में अपना सी कोड इस तरह से लिखना होगा जो कंपाइलर को सबसे अच्छे संकलित कोड की ओर ले जाए। अन्यथा, उत्पन्न कोड उप-इष्टतम होगा।
ysap

2
@ysap - वास्तविक दुनिया के उपयोग में वास्तविक कंप्यूटरों पर (छोटे छोटे ताक़तवर एम्बेडेड चिप्स नहीं), "इष्टतम" कोड तेज़ नहीं होने वाला है क्योंकि किसी भी बड़े डेटा सेट के लिए जो आपके द्वारा प्रदर्शन किया जा रहा है वह मेमोरी एक्सेस और पेज दोष () द्वारा सीमित होने वाला है और अगर आपके पास कोई बड़ा डेटा सेट नहीं है तो यह तेजी से किसी भी तरह से होने जा रहा है और इसका कोई मतलब नहीं है) - उन दिनों मैं ज्यादातर C # में काम करता हूं (सी भी नहीं) और कॉम्पैक्टिंग मेमोरी मैनेजर से प्रदर्शन लाभ- कचरे के संग्रह के ओवरहेड वजन, कॉम्पैक्टिंग और जेआईटी संकलन।
Nir

4
संकलक (esp। JIT) मनुष्यों की तुलना में बेहतर काम कर सकते हैं, यह बताने के लिए +1 यदि वे उस हार्डवेयर के लिए अनुकूलित होते हैं जिस पर वे चलते हैं।
सेबस्टियन

38

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

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

    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. विधानसभा में अतिप्रवाह का पता लगाना संभव है (वास्तव में सी में ऐसा नहीं कर सकते हैं), इससे कुछ एल्गोरिदम बहुत आसान हो जाते हैं।


23

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

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

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

यह सच है क्योंकि यह सच है: सी पोर्टेबल विधानसभा भाषा की तरह है।

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

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

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

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

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

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

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

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


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

3
"... क्योंकि सी के लिए कोई व्यक्तिपरक मानक नहीं है।" तुम उद्देश्य से मतलब है
थॉमस

@AndrewM: हाँ, मैंने लगभग 10 वर्षों के लिए BASIC और ARM असेंबलर में मिश्रित-भाषा अनुप्रयोग लिखे। मैंने उस दौरान C सीखा लेकिन यह बहुत उपयोगी नहीं था क्योंकि यह कोडांतरक और धीमी के रूप में बोझिल है। नॉरक्रॉफ्ट ने कुछ भयानक अनुकूलन किए, लेकिन मुझे लगता है कि सशर्त निर्देश सेट दिन के संकलक के लिए एक समस्या थी।
जॉन हैरोप

1
@AndrewM: ठीक है, वास्तव में ARM एक तरह से RISC है जो पीछे की तरफ किया जाता है। अन्य आरआईएससी आईएसए को एक कंपाइलर का उपयोग करने के साथ शुरू किया गया था। एआरएम आईएसए को लगता है कि सीपीयू प्रदान करता है (बैरल शिफ्टर, शर्त झंडे → जो उन्हें हर निर्देश में उजागर करते हैं) के साथ शुरू किया गया है।
नंजल

16

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

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

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

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


Amiga में चिप रैम के 16 एमबी नहीं थे, चिपसेट के आधार पर 512 केबी से 2 एमबी तक अधिक। इसके अलावा, बहुत से अमीगा गेम आपके द्वारा बताई गई तकनीकों के कारण तेजी से सीपीयू के साथ काम नहीं करते हैं।
bk1e

1
@ bk1e - अमीगा ने कंप्यूटर के विभिन्न मॉडलों की एक बड़ी रेंज का उत्पादन किया, Amiga 500 को 512K RAM के साथ भेजकर मेरे मामले में 1Meg तक बढ़ा दिया गया। amigahistory.co.uk/amiedevsys.html 128Meg Ram के साथ एक एमिगा है
डेविड वाटर्स

@ bk1e: मैं सही हूं। मेरी मेमोरी मुझे विफल कर सकती है लेकिन पहले 24bit एड्रेस स्पेस (यानी 16MB) पर चिप RAM प्रतिबंधित नहीं था? और फास्ट उस से ऊपर मैप किया गया था?
आरोन दिगुल्ला

@Aaron Digulla: विकिपीडिया में चिप / फास्ट / स्लो रैम के बीच अंतर के बारे में अधिक जानकारी है: en.wikipedia.org/wiki/Amiga_Chip_RAM
bk1e

@ bk1e: मेरी गलती है। 68k CPU में केवल 24 एड्रेस लेन थे, इसीलिए मेरे सिर में 16MB था।
आरोन दिगुल्ला

15

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

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

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

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

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

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

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

इसलिए एक बार और प्रयास करें आप विधानसभा के बारे में सोच रहे होंगे

  • निम्न स्तर के ऑपरेटिंग सिस्टम फ़ंक्शन पर काम करना
  • कंपाइलर पर काम करना।
  • एक अत्यंत सीमित चिप, एम्बेडेड सिस्टम आदि पर काम करना

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

डेविड।


4
छोटे चिप्स पर एम्बेडेड अनुप्रयोगों पर विचार करने के लिए +1। यहां बहुत सारे सॉफ्टवेयर इंजीनियर या तो एम्बेडेड पर विचार नहीं करते हैं या सोचते हैं कि स्मार्ट फोन (32 बिट, एमबी रैम, एमबी फ्लैश) का मतलब है।
मार्टिन

1
समय एम्बेडेड अनुप्रयोग एक महान उदाहरण हैं! हार्डवेयर के सीमित ज्ञान के कारण अक्सर अजीब निर्देश (यहां तक ​​कि वास्तव में सरल जैसे कि एवर sbiऔर cbi) होते हैं, जो कंपाइलर (और कभी-कभी अभी भी करते हैं) का पूरा फायदा नहीं उठाते हैं।
felixphew

15

मुझे आश्चर्य है कि किसी ने यह नहीं कहा। 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 गुना तेज है। और लगता है कि eax और ebx के उच्च क्रम वाले शब्द का उपयोग करते हुए, यह पिछली C दिनचर्या से 8 गुना तेज हो जाएगा !


3
यह strchr.nfshost.com/optimized_strlen_function में लोगों के साथ तुलना कैसे करता है ?
नवजाल

@njjalj: वे एक ही चीज़ हैं :) मैंने सोचा नहीं था कि इसे सी में इस तरह किया जा सकता है। मुझे लगता है कि इसमें थोड़ा सुधार किया जा सकता है
BlackBear

C कोड में प्रत्येक तुलना से पहले अभी भी एक बिटवाइज़ और ऑपरेशन है। यह संभव है कि उच्च और निम्न बाइट की तुलना को कम करने के लिए कंपाइलर पर्याप्त स्मार्ट होगा, लेकिन मैं इस पर पैसा नहीं लगाऊंगा। वहाँ वास्तव में एक तेज लूप एल्गोरिदम है जो उस संपत्ति पर आधारित है जो (word & 0xFEFEFEFF) & (~word + 0x80808080)शब्द में सभी बाइट्स शून्य शून्य है, गैर-शून्य हैं।
user2310967

@MichaWiedenmann सच, मुझे कुल्हाड़ी में दो पात्रों की तुलना करने के बाद bx लोड करना चाहिए। धन्यवाद
ब्लैकबियर

14

SIMD निर्देशों का उपयोग करके मैट्रिक्स ऑपरेशन शायद संकलित कोड की तुलना में तेज है।


कुछ कंपाइलर (वेक्टरसी, अगर मुझे सही याद है) SIMD कोड उत्पन्न करते हैं, तो यह भी कि शायद अब असेंबली कोड का उपयोग करने का कोई तर्क नहीं है।
ओरेगनगॉस्ट

कंपाइलर SSE जागरूक कोड बनाते हैं, ताकि तर्क सही न हो
vartec

5
उन स्थितियों में से कई के लिए आप असेंबली के बजाय SSE इंट्रिसिक्स का उपयोग कर सकते हैं। यह आपके कोड को और अधिक पोर्टेबल बना देगा (gcc visual c ++, 64bit, 32bit etc) और आपको रजिस्टर आवंटन नहीं करना है।
लेजरालयन

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

9
मेहरदाद हालांकि सही है। SSE का अधिकार प्राप्त करना कंपाइलर के लिए काफी कठिन होता है और यहाँ तक कि स्पष्ट रूप से (मनुष्यों के लिए भी) ऐसी परिस्थितियाँ होती हैं जिनमें से अधिकांश कंपाइलर इसे नियोजित नहीं करते हैं।
कोनराड रुडोल्फ

13

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

  • आप सम्मेलनों में कॉल करने से बच सकते हैं, रजिस्टर में तर्क दे सकते हैं।

  • आप सावधानी से विचार कर सकते हैं कि रजिस्टर का उपयोग कैसे करें, और मेमोरी में वेरिएबल स्टोर करने से बचें।

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

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

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

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

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

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


8
+1: "आप सम्मेलनों को कॉल करने से विचलित हो सकते हैं"। C / C ++ कंपाइलर कई मान वापस करने के लिए चूसना करते हैं। वे अक्सर sret फॉर्म का उपयोग करते हैं जहां कॉलर स्टैक एक संरचना के लिए एक सन्निहित ब्लॉक आवंटित करता है और इसे भरने के लिए कैली के लिए एक संदर्भ पारित किया है। रजिस्टरों में कई मानों को वापस करना कई गुना तेज है।
जॉन हैरोप

1
@Jon: C / C ++ कंपाइलर ठीक करते हैं कि जब फ़ंक्शन इनलाइन हो जाता है (नॉन-इन-लिस्ट फ़ंक्शन को ABI के अनुरूप होना पड़ता है, यह C और C ++ की सीमा नहीं है, लेकिन लिंकिंग मॉडल)
बेन Voigt

@BenVoigt: यहाँ एक काउंटर उदाहरण फ्लाइंगफ्रॉगब्लॉग .blogspot.co.uk
जॉन

2
मुझे वहाँ कोई भी फ़ंक्शन कॉल इनलाइन दिखाई नहीं दे रहा है।
बेन वोइगट

13

मेरे अनुभव से कुछ उदाहरण:

  • सी से निर्देश तक पहुंच नहीं है। उदाहरण के लिए, कई आर्किटेक्चर (जैसे x86-64, IA-64, DEC अल्फा और 64-बिट MIPS या PowerPC) 64 बिट गुणा का समर्थन करते हुए 128 बिट परिणाम का उत्पादन करते हैं। जीसीसी ने हाल ही में इस तरह के निर्देशों तक पहुंच प्रदान करते हुए एक एक्सटेंशन जोड़ा, लेकिन इससे पहले विधानसभा की आवश्यकता थी। और इस निर्देश तक पहुंच 64-बिट सीपीयू पर भारी अंतर डाल सकती है जब आरएसए जैसी चीज को लागू किया जाता है - कभी-कभी प्रदर्शन में 4 सुधार के कारक के रूप में।

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

  • SIMD। यहां तक ​​कि ऑटोव्रॉइज़िंग कंपाइलर केवल अपेक्षाकृत सरल मामले कर सकते हैं, इसलिए यदि आप अच्छा SIMD प्रदर्शन चाहते हैं तो दुर्भाग्य से अक्सर कोड को सीधे लिखना आवश्यक है। बेशक आप असेंबली के बजाय आंतरिक का उपयोग कर सकते हैं, लेकिन एक बार जब आप आंतरिक स्तर पर होते हैं, तो आप मूल रूप से असेंबली को वैसे भी लिख रहे होते हैं, बस संकलक का उपयोग एक रजिस्टर आवंटनकर्ता और (नाममात्र) निर्देश अनुसूचक में करते हैं। (मैं SIMD के लिए आंतरिक रूप से केवल इसलिए उपयोग कर रहा हूं क्योंकि कंपाइलर मेरे लिए फ़ंक्शन प्रस्ताव और व्हाट्सएप उत्पन्न कर सकता है, इसलिए मैं फ़ंक्शन कॉलिंग जैसे ABI मुद्दों से निपटने के लिए बिना Linux, OS X, और Windows पर समान कोड का उपयोग कर सकता हूं, लेकिन अन्य इससे बेहतर है कि SSE इंट्रेंसिक्स वास्तव में बहुत अच्छे नहीं हैं - Altivec वाले बेहतर लगते हैं, हालांकि मुझे उनके साथ ज्यादा अनुभव नहीं है)।AES या SIMD त्रुटि सुधार - एक ऐसा कंपाइलर, जो एल्गोरिदम का विश्लेषण कर सकता है और इस तरह का कोड उत्पन्न कर सकता है, लेकिन मुझे ऐसा लगता है कि ऐसा स्मार्ट कंपाइलर मौजूदा (सबसे अच्छे) से कम से कम 30 साल दूर है।

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


2
@ डेनिस ने इसीलिए लिखा है 'बेशक आप असेंबली के बजाय इंट्रिंसिक्स का उपयोग कर सकते हैं, लेकिन एक बार जब आप इंट्रिनिक्स लेवल पर होते हैं तो आप मूल रूप से असेंबली लिख रहे होते हैं, सिर्फ कंपाइलर का उपयोग करके एक रजिस्टर एलोकेटर और (नाममात्र) इंस्ट्रक्शन शेड्यूलर।
जैक लॉयड

इसके अलावा, आंतरिक आधारित SIMD कोड , कोडांतरक में लिखे एक ही कोड की तुलना में कम पठनीय होता है: बहुत SIMD कोड वैक्टर में डेटा की अंतर्निहित पुनर्व्याख्या पर निर्भर करता है, जो डेटा प्रकार कंपाइलर इंट्रिंसिक्स प्रदान करने के लिए एक PITA है।
विस्फ़ोटक -

10

चुस्त लूप, जैसे कि छवियों के साथ खेलते समय, चूंकि एक छवि लाखों पिक्सेल के कॉस्टिस्ट हो सकती है। नीचे बैठकर पता लगाना कि कैसे सीमित संख्या में प्रोसेसर रजिस्टरों का सबसे अच्छा उपयोग किया जा सकता है। यहाँ एक वास्तविक जीवन नमूना है:

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

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

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


3
प्रोफाइल निर्देशित अनुकूलन कंपाइलर जानकारी देता है कि एक लूप का उपयोग कितनी बार किया जाता है।
ज़ैन लिंक्स

10

अधिक बार आपको लगता है कि, सी को उन चीजों को करने की ज़रूरत है जो असेंबली कोडर के दृष्टिकोण से अनावश्यक हैं, क्योंकि सी मानकों का कहना है।

उदाहरण के लिए पूर्णांक संवर्धन। यदि आप C में एक चर चर को स्थानांतरित करना चाहते हैं, तो एक आमतौर पर उम्मीद करेगा कि कोड वास्तव में बस एक ही बदलाव करेगा।

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


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

9

आप वास्तव में नहीं जानते हैं कि आपका संकलित सी कोड वास्तव में तेज़ है यदि आपने संकलक के उत्पादन की गड़बड़ी को नहीं देखा है। कई बार आप इसे देखते हैं और देखते हैं कि "अच्छी तरह से लिखा गया" व्यक्तिपरक था।

इसलिए सबसे तेजी से कोड प्राप्त करने के लिए कोडांतरक में लिखना आवश्यक नहीं है, लेकिन यह निश्चित रूप से बहुत ही कारण से कोडांतरक को जानने के लायक है।


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

@cmaster मेरे अनुभव में संकलक उत्पादन अच्छी तरह से, यादृच्छिक है। कभी-कभी यह वास्तव में अच्छा और इष्टतम होता है और कभी-कभी "यह कचरा कैसे उत्सर्जित हो सकता है"।
sharptooth

9

मैंने सभी उत्तरों को पढ़ा है (30 से अधिक) और एक सरल कारण नहीं मिला: कोडांतरक C से तेज है यदि आपने Intel® 64 और IA-32 आर्किटेक्चर ऑप्टिमाइज़ेशन रेफरेंस मैनुअल को पढ़ा और अभ्यास किया है , तो इसका कारण असेंबली हो सकता है। धीरज रखो कि इस तरह की धीमी सभा लिखने वाले लोगों ने अनुकूलन मैनुअल नहीं पढ़ा

इंटेल 80286 के अच्छे पुराने दिनों में, प्रत्येक निर्देश को सीपीयू चक्रों की एक निश्चित गणना में निष्पादित किया गया था, लेकिन चूंकि 1995 में जारी पेंटियम प्रो, इंटेल प्रोसेसर सुपरस्केलर बन गया, जो कॉम्प्लेक्स पाइपलाइनिंग का उपयोग कर रहा था: आउट-ऑफ-ऑर्डर एक्जामिनेशन और रजिस्टर नामकरण। इससे पहले, पेंटियम पर, 1993 में निर्मित, यू और वी पाइपलाइन थे: दोहरी पाइप लाइनें जो एक घड़ी चक्र पर दो सरल निर्देशों को निष्पादित कर सकती थीं यदि वे एक दूसरे पर निर्भर नहीं थे; लेकिन यह आउट-ऑफ-ऑर्डर निष्पादन और रजिस्टर नामकरण पेंटियम प्रो में दिखाई देने वाले की तुलना करने के लिए कुछ भी नहीं था, और आजकल लगभग अपरिवर्तित छोड़ दिया गया है।

कुछ शब्दों में समझाने के लिए, सबसे तेज़ कोड वह है जहाँ निर्देश पिछले परिणामों पर निर्भर नहीं होते हैं, उदाहरण के लिए, आपको हमेशा पूरे रजिस्टर (Movzx द्वारा) को साफ़ करना चाहिए या add rax, 1इसके बजाय उपयोग करना चाहिएinc rax झंडे की पिछली स्थिति पर निर्भरता को दूर करना चाहिए, आदि।

यदि आप समय की अनुमति देते हैं तो आप आउट-ऑफ-ऑर्डर निष्पादन और रजिस्टर नामकरण पर अधिक पढ़ सकते हैं, इंटरनेट में काफी जानकारी उपलब्ध है।

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

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


8

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


7

यह सब आपके कार्यभार पर निर्भर करता है।

दिन-प्रतिदिन के संचालन के लिए, C और C ++ ठीक हैं, लेकिन कुछ कार्यभार (वीडियो (संपीड़न, विघटन, छवि प्रभाव, आदि) से संबंधित कोई भी परिवर्तन हैं) जो बहुत अधिक असेंबली करने के लिए आवश्यक हैं।

वे आमतौर पर सीपीयू विशिष्ट चिपसेट एक्सटेंशन (एमएमई / एमएमएक्स / एसएसई / जो भी) का उपयोग करते हैं, उन प्रकार के ऑपरेशन के लिए तैयार होते हैं।


6

मेरे पास बिट्स के ट्रांसपोज़ेशन का एक ऑपरेशन है, जिसे 192 या 256 बिट्स पर हर रुकावट की जरूरत होती है, जो हर 50 माइक्रोसेकंड पर होता है।

यह एक निश्चित मानचित्र (हार्डवेयर की कमी) द्वारा होता है। सी का उपयोग करते हुए, इसे बनाने में लगभग 10 माइक्रोसेकंड लगे। जब मैंने असेंबलर के लिए इसका अनुवाद किया, तो इस नक्शे की विशिष्ट विशेषताओं, विशिष्ट रजिस्टर कैशिंग और बिट ओरिएंटेड ऑपरेशंस का उपयोग करते हुए; इसे प्रदर्शन करने के लिए 3.5 माइक्रोसेकंड से कम समय लगा।


6

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


5

LInux Assembly howto , यह सवाल पूछता है और असेंबली का उपयोग करने के पेशेवरों और विपक्षों को देता है।


5

सरल उत्तर ... जो अच्छी तरह से विधानसभा जानता है (उर्फ उसके पास संदर्भ है, और हर छोटे प्रोसेसर कैश और पाइपलाइन सुविधा आदि का लाभ उठा रहा है) किसी से भी अधिक तेज कोड का उत्पादन करने में सक्षम होने की गारंटी है संकलक की ।

हालाँकि इन दिनों अंतर केवल विशिष्ट अनुप्रयोग में मायने नहीं रखता है।


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

मैं सहमत हूँ। समय बचाने और तेजी से विकास करने के लिए कभी-कभी आप मैक्रो और स्क्रिप्ट का उपयोग असेंबली कोड उत्पन्न करने के लिए करते हैं। इन दिनों अधिकांश असेंबलरों में मैक्रोज़ होते हैं; यदि नहीं, तो आप एक (काफी सरल RegEx) पर्ल स्क्रिप्ट का उपयोग करके एक (सरल) मैक्रो प्री-प्रोसेसर बना सकते हैं।

इस। यकीनन। डोमेन विशेषज्ञों को पीटने के लिए कंपाइलर का आविष्कार अभी तक नहीं किया गया है।
cmaster - मोनिका

4

पॉलीपास्कल के सीपी / एम -86 संस्करण (टर्बो पास्कल के लिए सहोदर) में से एक के लिए "मशीन-बायोस-टू-आउटपुट-कैरेक्टर्स-टू-द-स्क्रीन" सुविधा को एक मशीन भाषा के साथ बदलना था जो कि सार में होता है वहाँ x, और y, और स्ट्रिंग को दिया गया था।

इससे स्क्रीन को पहले की तुलना में बहुत तेज़ी से अपडेट किया जा सकता है!

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

यह पता चला है कि चूंकि स्क्रीन 80x25 थी, दोनों निर्देशांक प्रत्येक एक बाइट में फिट हो सकते थे, इसलिए दोनों एक दो-बाइट शब्द में फिट हो सकते थे। इसने कम बाइट्स में आवश्यक गणना करने की अनुमति दी क्योंकि एकल ऐड दोनों मानों को एक साथ जोड़ सकता है।

मेरी जानकारी के लिए, कोई सी कंपाइलर नहीं है जो एक रजिस्टर में कई मानों को मर्ज कर सकता है, उन पर SIMD निर्देश कर सकता है और बाद में उन्हें फिर से विभाजित कर सकता है (और मुझे नहीं लगता कि मशीन निर्देश वैसे भी कम होंगे)।


4

असेंबली के अधिक प्रसिद्ध स्निपेट्स में से एक माइकल अब्राश की टेक्सचर मैपिंग लूप ( विस्तार में यहां बताई गई ) से है:

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

आजकल अधिकांश संकलक उन्नत सीपीयू विशिष्ट निर्देशों को आंतरिक के रूप में व्यक्त करते हैं, अर्थात, ऐसे कार्य जो वास्तविक अनुदेश के लिए संकलित हो जाते हैं। MS Visual C ++ MMX, SSE, SSE2, SSE3 और SSE4 के लिए आंतरिक का समर्थन करता है, इसलिए आपको प्लेटफ़ॉर्म विशिष्ट निर्देशों का लाभ उठाने के लिए असेंबली को छोड़ने के बारे में कम चिंता करनी होगी। विजुअल C ++ उन वास्तविक आर्किटेक्चर का भी लाभ उठा सकता है जिन्हें आप उपयुक्त / ARCH सेटिंग के साथ लक्षित कर रहे हैं।


इससे भी बेहतर, वे SSE इंट्रेंसिक्स इंटेल द्वारा निर्दिष्ट किए गए हैं ताकि वे वास्तव में काफी पोर्टेबल हो।
जेम्स

4

सही प्रोग्रामर को देखते हुए, असेंबलर प्रोग्राम को हमेशा अपने सी समकक्षों (कम से कम मामूली) की तुलना में तेजी से बनाया जा सकता है। सी प्रोग्राम बनाना मुश्किल होगा जहां आप असेंबलर के कम से कम एक निर्देश को नहीं निकाल सकते।


यह थोड़ा और सही होगा: "यह एक n nrivrivial सी प्रोग्राम बनाना मुश्किल होगा जहाँ ..." वैकल्पिक रूप से, आप कह सकते हैं: " एक वास्तविक-विश्व C प्रोग्राम खोजना मुश्किल होगा जहाँ ..." प्वाइंट है , वहाँ तुच्छ छोरों हैं जिसके लिए कंपाइलर्स इष्टतम उत्पादन करते हैं। फिर भी, अच्छा जवाब।
cmaster - मोनिका


4

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


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

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

+1: जीसीसी निश्चित रूप से तेज कोड उत्पन्न करने में प्रतिस्पर्धी नहीं है, लेकिन मुझे यकीन नहीं है कि यह पोर्टेबल है। LLVM पोर्टेबल है और मैंने इसे GCC की तुलना में कोड 4x तेजी से बनाया है।
जॉन हैरोप

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

4

लॉन्गपोक, बस एक सीमा है: समय। जब आपके पास कोड को आवंटित करने और रजिस्टर आवंटित करने में अपना समय बदलने के लिए हर एक परिवर्तन का अनुकूलन करने के लिए संसाधन नहीं हैं, तो कुछ स्पिल का अनुकूलन करें और क्या नहीं, कंपाइलर हर बार जीत जाएगा। आप अपने संशोधन को कोड, recompile और माप में करते हैं। यदि आवश्यक हो तो दोहराएं।

इसके अलावा, आप उच्च-स्तरीय पक्ष में बहुत कुछ कर सकते हैं। इसके अलावा, परिणामी असेंबली का निरीक्षण करने से यह अनुमान लग सकता है कि कोड बकवास है, लेकिन व्यवहार में यह आपके विचार से अधिक तेज चलेगा। उदाहरण:

int y = data [i]; // कुछ सामान यहां करें .. call_function (y, ...);

कंपाइलर डेटा को पढ़ेगा, इसे स्टैक (स्पिल) पर धकेल देगा और बाद में स्टैक से पढ़कर तर्क के रूप में पास करेगा। लगता है चमक? यह वास्तव में बहुत प्रभावी विलंबता मुआवजा और तेजी से क्रम में परिणाम हो सकता है।

// अनुकूलित संस्करण call_function (डेटा [i], ...); // इतना सब के बाद अनुकूलित नहीं ..

अनुकूलित संस्करण के साथ विचार यह था, कि हमने रजिस्टर दबाव कम कर दिया है और स्पिलिंग से बचें। लेकिन सच में, "shitty" संस्करण तेज था!

असेंबली कोड को देखते हुए, बस निर्देशों को देखना और निष्कर्ष निकालना: अधिक निर्देश, धीमा, एक गलत अनुमान होगा।

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

  • याददाश्त धीमी है
  • कैश तेज है
  • कैश्ड का बेहतर उपयोग करने का प्रयास करें
  • आप कितनी बार चूकने वाले हैं? क्या आपके पास विलंबता मुआवजा रणनीति है?
  • आप एक एकल कैश मिस के लिए 10-100 ALU / FPU / SSE निर्देशों को निष्पादित कर सकते हैं
  • आवेदन वास्तुकला महत्वपूर्ण है ..
  • .. लेकिन यह मदद नहीं करता है जब समस्या वास्तुकला में नहीं है

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

C / C ++ में कंपाइलर आमतौर पर सब-एक्सप्रेशन को री-ऑर्डर करने में बहुत अच्छे नहीं होते क्योंकि फंक्शन का साइड इफेक्ट होता है, शुरुआत के लिए। कार्यात्मक भाषाएं इस चेतावनी से ग्रस्त नहीं हैं, लेकिन मौजूदा पारिस्थितिकी तंत्र को अच्छी तरह से फिट नहीं करती हैं। आराम से सटीक नियमों की अनुमति देने के लिए संकलक विकल्प हैं जो संकलक / लिंकर / कोड जनरेटर द्वारा संचालन के आदेश को बदलने की अनुमति देते हैं।

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

यह सब इस पर उबलता है: "यह समझने के लिए कि आप क्या कर रहे हैं", यह जानने से थोड़ा अलग है कि आप क्या कर रहे हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.