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