2016-03-21 10 views
5

पहले उत्तर here, निम्नलिखित C++ ढेर स्मृति के बारे में उल्लेख किया गया था:सी ++: संकलक कैसे जानता है कि प्रत्येक स्टैक फ्रेम के लिए कितनी मेमोरी आवंटित की जाती है?

जब एक समारोह में कहा जाता है, एक ब्लॉक स्थानीय चर और कुछ बहीखाता डेटा के लिए ढेर के शीर्ष पर आरक्षित है।

इस शीर्ष स्तर पर सही समझ में आता है और मुझे उन स्मार्ट compilers कर रहे हैं जब में और स्वयं का इस स्मृति आवंटन, this question के संदर्भ को देखते हुए के बारे में उत्सुक बनाता है: ब्रेसिज़ के बाद से खुद को सी में एक ढेर फ्रेम नहीं हैं (मुझे लगता है कि यह सी ++ के लिए भी सच है), मैं यह जांचना चाहता हूं कि संकलक एक ही फ़ंक्शन के भीतर परिवर्तनीय स्कॉप्स के आधार पर आरक्षित मेमोरी अनुकूलित करते हैं या नहीं।

निम्नलिखित में मैं यह सोचते हैं रहा है कि ढेर एक समारोह कॉल करने से पहले इस तरह दिखता है:,

-------- 
|main()| 
-------- <- old stack pointer (osp) 
| f() | 
-------- <- stack pointer, variables will now be placed between here and osp upon reaching their declarations 
|  | 
|  | 
|  | 
|  | 
-------- 

उदाहरण के लिए दिए गए:

-------- 
|main()| 
-------- <- stack pointer: space above it is used for current scope 
|  | 
|  | 
|  | 
|  | 
-------- 

और फिर एक समारोह f() लागू करने के बाद निम्नलिखित यह फ़ंक्शन

void f() { 
    int x = 0; 
    int y = 5; 
    int z = x + y; 
} 

संभवतः, यह केवल 012 आवंटित करेगाबहीखाता के लिए कुछ अतिरिक्त ओवरहेड।

हालांकि, इस समारोह के बारे में:

void g() { 
    for (int i = 0; i < 100000; i++) { 
    int x = 0; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
} 

की उपेक्षा संकलक अनुकूलन जो वास्तव में वे कुछ नहीं कर के बाद से ऊपर में सामान का एक बहुत छिपाना सकता है, मैं उत्सुक हूँ कि दूसरे उदाहरण में के बारे में निम्नलिखित:

  • for लूप के लिए: क्या स्टैक स्पेस सभी 100000 इनट्स फिट करने के लिए पर्याप्त होगा?
  • उस पर, क्या स्टैक स्पेस में 1000*sizeof(MyObject) या 2000*sizeof(MyObject) होगा?

सामान्य रूप से: क्या संकलक एक निश्चित कार्य को आमंत्रित करने से पहले नए स्टैक फ्रेम के लिए कितनी मेमोरी की आवश्यकता होगी यह निर्धारित करते समय संकलक को चरम दायरा लेता है? यदि यह कंपाइलर-विशिष्ट है, तो कुछ प्रसिद्ध कंपेलर इसे कैसे करते हैं?

+3

'{}' की एक जोड़ी एक गुंजाइश है। लूप 'x' के लिए एक ही मेमोरी का उपयोग करता है, और दो' myObject' arrays एक ही समय में मौजूद नहीं होते हैं। – LogicStuff

+1

'100000' इनट्स के लिए जगह आवंटित करने की आवश्यकता क्यों होगी, जब यह उसी स्थान का पुन: उपयोग कर सके? सरणी के लिए भी चला जाता है। –

+1

कंपाइलर फ़ंक्शन के प्रत्येक दायरे की जांच करता है और आरक्षित स्थान सभी क्षेत्रों के अधिकतम स्थान है जो एक ही समय में मौजूद हो सकते हैं। –

उत्तर

4

कंपाइलर आवश्यकतानुसार स्थान आवंटित करेगा (आमतौर पर फ़ंक्शन की शुरुआत में सभी वस्तुओं के लिए), लेकिन लूप में प्रत्येक पुनरावृत्ति के लिए नहीं।

उदाहरण के लिए, क्या बजना पैदा करता है, LLVM-आईआर के रूप में

define void @_Z1gv() #0 { 
    %i = alloca i32, align 4 
    %x = alloca i32, align 4 
    %myObject = alloca [1000 x %class.MyObject], align 16 
    %myObject1 = alloca [1000 x %class.MyObject], align 16 
    store i32 0, i32* %i, align 4 
    br label %1 

; <label>:1:          ; preds = %5, %0 
    %2 = load i32, i32* %i, align 4 
    %3 = icmp slt i32 %2, 100000 
    br i1 %3, label %4, label %8 

; <label>:4:          ; preds = %1 
    store i32 0, i32* %x, align 4 
    br label %5 

; <label>:5:          ; preds = %4 
    %6 = load i32, i32* %i, align 4 
    %7 = add nsw i32 %6, 1 
    store i32 %7, i32* %i, align 4 
    br label %1 

; <label>:8:          ; preds = %1 
    ret void 
} 

इस का परिणाम है:

class MyObject 
{ 
public: 
    int x, y; 
}; 

void g() { 
    for (int i = 0; i < 100000; i++) 
    { 
    int x = 0; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
} 

तो, जैसा कि आप देख सकते हैं, x केवल एक बार आवंटित किया जाता है, नहीं 100000 बार। क्योंकि किसी भी समय उनमें से केवल एक चर मौजूद होगा।

(संकलक x के लिए myObject[1000] के लिए जगह और दूसरे myObject[1000] का पुन: उपयोग कर सकते हैं -, और शायद एक अनुकूलित निर्माण के लिए ऐसा करना होगा, लेकिन इस मामले में यह भी पूरी तरह से इन चरों हटा के रूप में वे उपयोग नहीं किया जाता तो यह wouldn बहुत अच्छी तरह से नहीं दिखाएं)

+0

और स्टैक पॉइंटर के संदर्भ में: क्या यह 'g()' तक पहुंचने पर 'अधिकतम (2 * आकार (int), 1000 * sizeof (MyObject)) द्वारा बढ़ाया जाएगा? चूंकि केवल वे चर एक ही समय में मौजूद हो सकते हैं। मुझे नहीं लगता कि यह असेंबली से स्पष्ट है। – Jimmy

+0

सबसे अधिक संभावना है, हां, लेकिन यह सभी स्थानीय चरों का योग हो सकता है - लगभग निश्चित रूप से यह हो सकता है कि एक गैर-अनुकूलित निर्माण [जो मेरा कोड दिखाता है] –

+0

बेशक, अनुकूलित अनुकूलित 'i' और 'x में 'स्टैक के बजाए रजिस्टरों में सबसे ज्यादा रहने की संभावना है। –

2

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

चीजें थोड़ा अधिक जटिल हो जाती हैं क्योंकि ऑप्टिमाइज़र आगे बढ़ता है, क्योंकि यह स्टैक वैरिएबल को चारों ओर स्थानांतरित नहीं करना पसंद कर सकता है। यह मुफ़्त नहीं है।

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

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