2013-04-09 3 views
7

निम्नलिखित कार्यक्रम पर विचार करें:क्या कंपाइलर स्थानीय चरों को पुन: व्यवस्थित करके स्टैक मेमोरी उपयोग को अनुकूलित करने की अनुमति देता है?

#include <stdio.h> 

void some_func(char*, int*, char*); 

void stack_alignment(void) { 
    char a = '-'; 
    int i = 1337; 
    char b = '+'; 
    some_func(&a, &i, &b); // to prevent the compiler from removing the local variables 
    printf("%c|%i|%c", a, i, b); 
} 

यह निम्न विधानसभा उत्पन्न करता है (टिप्पणी अपने आप से कहा, मैं विधानसभा के लिए एक पूरा नौसिखिया हूँ):

$ vim stack-alignment.c 
$ gcc -c -S -O3 stack-alignment.c 
$ cat stack-alignment.s 
     .file "stack-alignment.c" 
     .section .rdata,"dr" 
LC0: 
     .ascii "%c|%i|%c\0" 
     .text 
     .p2align 2,,3 
     .globl _stack_alignment 
     .def _stack_alignment;  .scl 2;  .type 32;  .endef 
_stack_alignment: 
LFB7: 
     .cfi_startproc 
     subl $44, %esp 
     .cfi_def_cfa_offset 48 
     movb $45, 26(%esp) // local variable 'a' 
     movl $1337, 28(%esp) // local variable 'i' 
     movb $43, 27(%esp) // local variable 'b' 
     leal 27(%esp), %eax 
     movl %eax, 8(%esp) 
     leal 28(%esp), %eax 
     movl %eax, 4(%esp) 
     leal 26(%esp), %eax 
     movl %eax, (%esp) 
     call _some_func 
     movsbl 27(%esp), %eax 
     movl %eax, 12(%esp) 
     movl 28(%esp), %eax 
     movl %eax, 8(%esp) 
     movsbl 26(%esp), %eax 
     movl %eax, 4(%esp) 
     movl $LC0, (%esp) 
     call _printf 
     addl $44, %esp 
     .cfi_def_cfa_offset 4 
     ret 
     .cfi_endproc 
LFE7: 
     .def _some_func;  .scl 2;  .type 32;  .endef 
     .def _printf;  .scl 2;  .type 32;  .endef 

आप देख सकते हैं वहाँ 3 स्थानीय हैं चर (a, i और b) 1 बाइट, 4 बाइट और 1 बाइट के आकार के साथ। पैडिंग सहित यह 12 बाइट होगा (संकलक को 4 बाइट्स पर संरेखित करना)।

क्या यह अधिक मेमोरी कुशल नहीं होगा यदि कंपाइलर चर के क्रम को बदल देगा (a, b, i)? फिर केवल 8 बाइट आवश्यक होंगे।

यहाँ एक "ग्राफिक" प्रतिनिधित्व:

3 bytes unused     3 bytes unused 
    vvvvvvvvvvv      vvvvvvvvvvv 
+---+---+---+---+---+---+---+---+---+---+---+---+ 
| a | | | | i    | b | | | | 
+---+---+---+---+---+---+---+---+---+---+---+---+ 

       | 
       v 

+---+---+---+---+---+---+---+---+ 
| a | b | | | i    | 
+---+---+---+---+---+---+---+---+ 
     ^^^^^^^ 
     2 bytes unused 

संकलक (सी मानक आदि से) इस अनुकूलन करने की अनुमति दी है?

  • यदि नहीं (जैसा कि मुझे लगता है कि असेंबली आउटपुट दिखाता है), क्यों?
  • यदि हां, तो यह ऊपर क्यों नहीं होता है?
+0

यह मानते हैं कि इसे मानकों आदि द्वारा अनुमत किया जाता है, तो यह व्यक्तिगत संकलक कार्यान्वयन पर पूरी तरह से होगा चाहे वे इसे करते हैं या नहीं। मुझे लगता है कि यह संकलन समय पर अनुकूलन स्तरों द्वारा नियंत्रित किया जाएगा। – John3136

+0

कंपाइलर/ऑप्टिमाइज़र स्थानीय लोगों को जहां भी चाहें रखने के लिए स्वतंत्र है, जब तक कि यह प्रोग्राम को तोड़ नहीं देता है। यह एक ही स्थान पर दो चर रखने के लिए स्वतंत्र है यदि यह निश्चित है कि वे कभी भी एक ही समय में उपयोग नहीं किए जाते हैं। – mah

+0

क्या आपने विभिन्न अनुकूलन विकल्पों के साथ संकलन करने का प्रयास किया है? शायद आपने अनुकूलन के साथ संकलित किया है। –

उत्तर

4

क्या संकलक इस अनुकूलन (सी मानक आदि द्वारा) करने की अनुमति देता है?

हां।

यदि हां, तो यह ऊपर क्यों नहीं होता है?

ऐसा हुआ।

असेंबलर आउटपुट सावधानी से पढ़ें।

movb $45, 26(%esp) // local variable 'a' 
    movl $1337, 28(%esp) // local variable 'i' 
    movb $43, 27(%esp) // local variable 'b' 

चर a पर ऑफसेट 26. चर b है पर ऑफसेट 27. चर i है पर ऑफसेट 28.

आप लेआउट बनाया छवियों का उपयोग अब है:

+---+---+---+---+---+---+---+---+ 
| | | a | b | i    | 
+---+---+---+---+---+---+---+---+ 
^^^^^^^ 
2 bytes unused 
+0

मैंने वास्तव में mov (l | b) कमांड के दूसरे तर्क में संख्या को नहीं देखा><, इसके लिए धन्यवाद। खैर, अभी भी मजाकिया क्यों जीसीसी इस आदेश में इन आदेशों की व्यवस्था करता है, यह ध्यान देना आसान होगा कि यह दूसरी तरफ है या नहीं। – MarcDefiant

+1

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

7

कंपाइलर स्थानीय चर के लेआउट के लिए स्वतंत्र है जैसा वह चाहता है। इसे ढेर का उपयोग करने की भी आवश्यकता नहीं है।

यह स्टैक पर घोषणा के आदेश से संबंधित किसी भी क्रम में स्थानीय चर को स्टोर कर सकता है यदि यह स्टैक का उपयोग करता है।

क्या संकलक इस अनुकूलन (सी मानक आदि द्वारा) करने की अनुमति देता है?

  • यदि हां, तो यह ऊपर क्यों नहीं होता है?

अच्छा, क्या यह बिल्कुल अनुकूलन है?

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

+1

के साथ विंडोज 64 बिट है यह प्रश्न का पूरी तरह उत्तर नहीं देता है। कॉल स्टैक पर कुछ बाइट्स एक रिकर्सिव एल्गोरिदम लागू करते समय मायने रख सकते हैं। सी सिस्टम प्रोग्रामिंग की भाषा है, इसलिए जहां संभव हो स्मृति को सहेजना * एक चिंता है। सवाल यह है कि जीसीसी एक अनुकूलन अवसर पर गायब है या ट्रेडऑफ के एक तरफ का चयन कर रहा है। यह लगभग निश्चित रूप से सच है कि * कुछ * आर्किटेक्चर पर एक गठबंधन स्थान से 'char' पढ़ने के लिए तेज़ है; लेकिन असेंबली एक ठोस वास्तुकला के लिए उत्पन्न होता है। यदि यह आर्किटेक्चर अनचाहे पढ़ने के लिए जुर्माना नहीं लगाता है, तो अनुकूलन गुम है। – user4815162342

0

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

2

के लिए मैनुअल में देखो इसे और अधिक स्मृति नहीं चाहेंगे कुशल अगर संकलक चर

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

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

क्या संकलक इस अनुकूलन (सी मानक आदि द्वारा) करने की अनुमति देता है?

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

0

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

चेतावनी: ऐसी विशेषताएं समझौता को रोकती नहीं हैं, वे सिर्फ हमलावर के लिए बाधाओं को बढ़ाते हैं, लेकिन एक कुशल हमलावर आमतौर पर अपना रास्ता खोजता है।

संबंधित मुद्दे

 संबंधित मुद्दे