2011-01-07 5 views
13

सी ++ में, अधिकांश अनुकूलन एएस-नियम से प्राप्त किए गए हैं। यही है, जब तक कार्यक्रम व्यवहार नहीं करता है-यदि कोई अनुकूलन नहीं हुआ है, तो वे मान्य हैं।खाली डेटा सदस्य अनुकूलन: क्या यह संभव होगा?

खाली बेस अनुकूलन एक ऐसी चाल है: कुछ स्थितियों में, यदि बेस क्लास खाली है (कोई गैर-स्थैतिक डेटा सदस्य नहीं है), तो संकलक इसकी स्मृति प्रस्तुति को बढ़ा सकता है।

जाहिर है ऐसा लगता है कि मानक डेटा के सदस्यों पर इस अनुकूलन की मनाही है, कि भले ही एक डेटा सदस्य खाली है, यह अभी भी जगह कम से कम एक बाइट लायक ले लेना चाहिए: n3225 से, [वर्ग]

4 - कक्षा के प्रकार के पूर्ण ऑब्जेक्ट्स और सदस्य उप-प्रोजेक्ट में nonzero आकार होगा।

नोट: इस EBO में लात जब उचित

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


संपादित: जवाब और टिप्पणियों के एक नंबर के बाद, और यह स्पष्ट है कि मैं क्या कर रहा हूँ के बारे में सोच सकते हैं।

सबसे पहले, मुझे एक उदाहरण देता है:

struct Empty {}; 

struct Foo { Empty e; int i; }; 

मेरा प्रश्न है, क्यों sizeof(Foo) != sizeof(int) है? विशेष रूप से, जब तक कि आप कुछ पैकिंग निर्दिष्ट नहीं करते हैं, संभावनाएं संरेखण के मुद्दों के कारण होती हैं कि फू int के आकार से दोगुना होगा, जो हास्यास्पद रूप से फुलाया जाता है।

नोट: मेरे सवाल क्यों sizeof(Foo) != 0 है, ऐसा इसलिए है क्योंकि कोई उप-वस्तु एक शून्य आकार हो सकता है नहीं है यह वास्तव में सी ++ के अनुसार EBO या तो

वजह से नहीं है।

struct Bar: Empty { int i; }; 

की संभावना है (EBO करने के लिए धन्यवाद) sizeof(Bar) == sizeof(int) पालन करने के लिए: हालांकि एक आधार एक शून्य आकार (EBO) इसलिए है करने के लिए अधिकृत है।

स्टीव जेसॉप एक राय का मानना ​​है कि ऐसा नहीं है कि कोई भी दो उप-वस्तुओं का एक ही पता नहीं होगा। मैं इसके बारे में सोचा, लेकिन यह वास्तव में ज्यादातर मामलों में अनुकूलन नहीं रोकता:

यदि आपके पास "अप्रयुक्त" स्मृति, तो यह मामूली बात है:

struct UnusedPadding { Empty e; Empty f; double d; int i; }; 
// chances are that the layout will leave some memory after int 

लेकिन वास्तव में, यह और भी "बदतर है "इसके मुकाबले, क्योंकि Empty अंतरिक्ष कभी नहीं लिखा गया है (यदि बेहतर होगा कि ईबीओ इसमें शामिल न हो तो बेहतर होगा ...) और इसलिए आप वास्तव में है कि किसी अन्य वस्तु का पता नहीं है एक कब्जे वाले जगह पर यह जगह सकता है:

struct Virtual { virtual ~Virtual() {} Empty e; Empty f; int i; }; 
// most compilers will reserve some space for a virtual pointer! 

या, यहां तक ​​कि हमारे मूल मामले में:

struct Foo { Empty e; int i; }; // deja vu! 

एक अगर सब हम (char*)foo.e == (char*)foo.i + 1 हो सकता था चाहता था अलग पता था।

+1

बूस्ट के [संपीडित जोड़ी] (http://www.boost.org/doc/libs/1_45_0/libs/utility/compressed_pair.htm) पुस्तकालय पर एक नजर डालें, कैसे इस अनुकूलन प्राप्त करने के लिए देखने के लिए। – GManNickG

+0

@GMan: वे चालाकी से ईबीओ का उपयोग करते हैं। लेकिन असल में ईबीओ का यह उपयोग ठीक है जो मेरे प्रश्न को शुरू करने के लिए प्रेरित करता है। –

+0

इसे देखें: [प्रोग्रामर खाली बेस ऑप्टिमाइज़ेशन (ईबीओ) का उपयोग कब करते हैं] (http://stackoverflow.com/questions/4325144/scenario-when-do-programmers-use-empty-base-optimization-ebo) – Nawaz

उत्तर

6

के रूप में करता है, तो नियम के तहत:

struct A { 
    EmptyThing x; 
    int y; 
}; 

A a; 
assert((void*)&(a.x) != (void*)&(a.y)); 

ज़ोर ट्रिगर नहीं किया जाना चाहिए। इसलिए मुझे x को गुप्त रूप से बनाने में कोई लाभ नहीं दिखता है, जब आपको संरचना में पैडिंग जोड़ने की ज़रूरत होती है।

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

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

+0

हां, यह पता पहचान की बात यह है कि वास्तव में मुझे यहां परेशान करता है। जबकि मैं अनुमान लगा सकता हूं कि इसका उपयोग हो सकता है, एक ऑब्जेक्ट जिसका पता नहीं लिया जाता है उस ऑब्जेक्ट की मात्रा जो कि उपयोग नहीं की जाती है (क्योंकि किसी भी विधि को आमंत्रित करने के लिए इसके पते की आवश्यकता होती है) और मुझे नहीं लगता कि सी ++ कंपाइलर किसी ऑब्जेक्ट को कैसे महसूस कर सकता है सभी इनलाइन लाइनों के अलावा अप्रयुक्त। क्या आप जानते हैं कि इस गैर-शून्य का उपयोग क्यों किया जाता है? (यानी यदि यह सी के अवशेष है या फिर भी आधुनिक शैली में उपयोग किया गया है) –

+0

@Matthieu: ठीक है, मुझे लगता है कि पूर्ण वस्तुओं का शून्य-शून्य आकार इतना है कि आप 'sizeof (array)/sizeof (* array) ' और शून्य से विभाजित नहीं है। मैं सदस्य सबोबजेक्ट्स के गैर-शून्य आकार के लिए एक मजबूत तर्क के बारे में नहीं सोच सकता - यदि आपको अलग-अलग पते वाले कुछ सदस्य ऑब्जेक्ट्स की आवश्यकता है, तो आप (प्रोग्रामर) यह सुनिश्चित कर सकते हैं कि वे 'एम्प्टी टिंग' के बजाय 'char' टाइप करें। । मुझे लगता है कि ऑब्जेक्ट/मेमोरी मॉडल में कहीं भी है जिसे विशेष केस हैंडलिंग की आवश्यकता होगी, और यह मानना ​​आसान है कि सदस्य सबोबजेक्ट अलग और गैर-ओवरलैपिंग हैं। –

+0

मैंने कुछ हद तक अपना प्रश्न अपडेट कर लिया है। मुझे पूर्ण ऑब्जेक्ट्स का गैर-शून्य आकार की आवश्यकता नहीं है (हालांकि यह भी अच्छा हो सकता है, अन्य अनुकूलन के ढेर की अनुमति देता है, और 'sizeof' संकलित-समय कोड भी है, इसलिए किसी भी तरह रनटाइम पर शून्य अपवाद द्वारा विभाजन की कोई चिंता नहीं है) । मुझे समझ में नहीं आता है कि अगर हमारे पास ईबीओ हो (जो शून्य आकार का बेस क्लास है) तो हमारे पास ईडीएमओ नहीं हो सकता है (यह शून्य आकार का डेटा सदस्य है)। –

2

तकनीकी कारणों से सी ++ अनिवार्य है कि खाली कक्षाओं में शून्य-शून्य आकार होना चाहिए।
यह लागू करना है कि विशिष्ट वस्तुओं में अलग-अलग स्मृति पते हों। तो compilers चुपचाप एक बाइट "खाली" वस्तुओं में डालें।
यह बाधा व्युत्पन्न कक्षाओं के बेस क्लास भागों पर लागू नहीं होती है क्योंकि वे मुक्त नहीं हैं।

-1

struct Empty { }; देखते हुए विचार करें कि sizeof(Empty) == 0 क्या होता है। जेनेरिक कोड जो रिक्त ऑब्जेक्ट्स के लिए ढेर आवंटित करता है, आसानी से अलग-अलग व्यवहार कर सकता है - उदाहरण के लिए - realloc(p, n * sizeof(T)), जहां TEmpty है, तो free(p) के बराबर है। यदि sizeof(Empty) != 0 तो मेमसेट/memcpy इत्यादि जैसी चीजें स्मृति क्षेत्रों पर काम करने का प्रयास करेंगी जो Empty ऑब्जेक्ट्स द्वारा उपयोग में नहीं थीं। इसलिए, संकलक को मूल्य के अंतिम उपयोग के आधार पर sizeof(Empty) जैसी चीजों को सिलाई करने की आवश्यकता होगी - जो मेरे लिए असंभव लगता है।

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

+0

"मेमसेट/मेम्प्पी इत्यादि जैसी चीजें मेमोरी क्षेत्रों पर काम करने की कोशिश करती हैं जो रिक्त ऑब्जेक्ट्स द्वारा उपयोग में नहीं थीं" - केवल तभी जब आप गलत (i.e non-0) लंबाई में memset/memcpy में गुजरती हैं। –

+0

@ टोनी: मैं यह सुझाव नहीं दे रहा था कि 'आकार (खाली) '''' हो, बल्कि' संरचना फू {खाली ई; int i; }; 'एक का आकार था (Foo) == sizeof (int)' जिसका अर्थ है स्मृति के बारे में 4/8 बाइट्स (संरेखण के कारण) –

+0

@Steve: बिल्कुल :-) - वह वाक्य "sizeof (खाली)! = 0 ", जिसका अर्थ है कि जब आप 'sizeof (टी)' का उपयोग करते हुए मेमसेट/memcpy कॉल के आकार के लिए गणना करते हैं, जहां टी खाली है, तो आपको एक गैर-शून्य मान मिलेगा ... –

1

क्योंकि Empty एक पॉड प्रकार है, तो आप memcpy का उपयोग अपने "प्रतिनिधित्व" अधिलेखित करने के लिए कर सकते हैं, तो यह बेहतर एक और सी ++ वस्तु या उपयोगी डेटा के साथ साझा नहीं।

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