#pragma पैक प्रभाव


233

मैं सोच रहा था कि क्या कोई मुझे समझा सकता है कि #pragma packप्रीप्रोसेसर बयान क्या करता है, और इससे भी महत्वपूर्ण बात यह है कि कोई इसका उपयोग क्यों करना चाहेगा।

मैंने MSDN पृष्ठ की जाँच की , जिसमें कुछ जानकारी दी गई थी, लेकिन मैं अनुभव वाले लोगों से अधिक सुनने की उम्मीद कर रहा था। मैंने इसे पहले भी कोड में देखा है, हालांकि मुझे अब और नहीं मिल रहा है।


1
यह एक संरचना के एक विशेष संरेखण / पैकिंग को मजबूर करता है, लेकिन सभी #pragmaनिर्देशों की तरह वे कार्यान्वयन को परिभाषित करते हैं।
ड्रीमलैक्स

A mod s = 0जहां A पता है और s डेटाटाइप का आकार है; यह जाँचता है कि क्या कोई डेटा गलत नहीं है।
लीजेंड्स 2k

जवाबों:


422

#pragma packसंकलक को विशेष संरेखण के साथ संरचना के सदस्यों को पैक करने का निर्देश देता है। अधिकांश कंपाइलर, जब आप एक संरचना की घोषणा करते हैं, तो यह सुनिश्चित करने के लिए सदस्यों के बीच पैडिंग डालेंगे कि वे मेमोरी में उपयुक्त पते (आमतौर पर कई प्रकार के आकार) से जुड़े हैं। यह पहुंच मार्ग से जुड़े कुछ आर्किटेक्चर पर प्रदर्शन जुर्माना (या एकमुश्त त्रुटि) से बचा जाता है जो ठीक से संरेखित नहीं हैं। उदाहरण के लिए, दिए गए 4-बाइट पूर्णांकों और निम्नलिखित संरचना:

struct Test
{
   char AA;
   int BB;
   char CC;
};

संकलक इस तरह से स्मृति में संरचना को बाहर करना चुन सकता है:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

और sizeof(Test)4 × 3 = 12 होगा, भले ही इसमें केवल 6 बाइट डेटा हो। #pragma(मेरी जानकारी में) के लिए सबसे आम उपयोग मामला हार्डवेयर उपकरणों के साथ काम करते समय होता है जहां आपको यह सुनिश्चित करने की आवश्यकता होती है कि कंपाइलर डेटा में पैडिंग नहीं डालता है और प्रत्येक सदस्य पिछले एक का अनुसरण करता है। इसके साथ #pragma pack(1), ऊपर की संरचना इस तरह रखी जाएगी:

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

और sizeof(Test)1 × 6 = 6 होगा।

इसके साथ #pragma pack(2), ऊपर की संरचना इस तरह रखी जाएगी:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

और sizeof(Test)2 × 4 = 8 होगा।

संरचना में चर का आदेश भी महत्वपूर्ण है। निम्नलिखित के साथ आदेश दिए गए चर के साथ:

struct Test
{
   char AA;
   char CC;
   int BB;
};

और #pragma pack(2), संरचना इस तरह रखी जाएगी:

|   1   |   2   | 

| AA(1) | CC(1) |
| BB(1) | BB(2) |
| BB(3) | BB(4) |

और sizeOf(Test)3 × 2 = 6 होगा।


76
यह पैकिंग के डाउनसाइड को जोड़ने के लायक हो सकता है। (अनलग्ड ऑब्जेक्ट एक्सेस सबसे अच्छे मामले में धीमी हैं , लेकिन कुछ प्लेटफ़ॉर्म पर त्रुटियां पैदा करेंगी।)
jalf

11
लगता है संरेखण "प्रदर्शन दंड" वास्तव में कुछ सिस्टम danluu.com/3c-conflict पर लाभ हो सकता है ।

6
@ स्पेसर वास्तव में नहीं। यह पोस्ट कुछ काफी चरम संरेखण (4KB सीमाओं पर संरेखित) के बारे में बात करती है। सीपीयू विभिन्न डेटा प्रकारों के लिए कुछ न्यूनतम संरेखण की उम्मीद करता है, लेकिन उन लोगों की आवश्यकता होती है, सबसे खराब स्थिति में, 8-बाइट संरेखण (वेक्टर प्रकारों की गिनती नहीं होती है जिसमें 16 या 32 बाइट संरेखण की आवश्यकता हो सकती है)। उन सीमाओं पर संरेखित नहीं करना आम तौर पर आपको एक ध्यान देने योग्य प्रदर्शन हिट देता है (क्योंकि एक लोड को एक के बजाय दो ऑपरेशन के रूप में करना पड़ सकता है), लेकिन प्रकार या तो अच्छी तरह से संरेखित है या यह नहीं है। कि आप खरीदता कुछ नहीं से सख्त संरेखण (और खंडहर कैश उपयोग
jalf

6
दूसरे शब्दों में, एक 8 बाइट सीमा पर एक डबल होने की उम्मीद है। इसे 7 बाइट की बाउंड्री पर रखने से प्रदर्शन को नुकसान होगा। लेकिन इसे १६, ३२, ६४ या ४० ९ ६ बाइट की बाउंड्री पर रखने से आपको कुछ भी नहीं मिलता है, जो 16 बाइट की बाउंड्री आपको पहले ही दे चुकी है। उस पोस्ट में उल्लिखित कारणों के लिए बहुत अधिक कैश उपयोग प्राप्त करते हुए, आपको CPU से समान प्रदर्शन मिलेगा।
जालफ

4
तो सबक "पैकिंग फायदेमंद नहीं है" (पैकिंग प्रकार के प्राकृतिक संरेखण का उल्लंघन करता है, ताकि प्रदर्शन को नुकसान पहुंचाता है), लेकिन बस "अति आवश्यक से अधिक संरेखित न करें"
jalf

27

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

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


17
इसके बाद इसे करने के लिए: #pragma pack (पुश, 1) और #pragma पैक (पॉप)
malhal

16

यह संकलक को एक संरचना में वस्तुओं को संरेखित करने के लिए सीमा बताता है। उदाहरण के लिए, अगर मेरे पास कुछ है:

struct foo { 
    char a;
    int b;
};

एक विशिष्ट 32-बिट मशीन के साथ, आप सामान्य रूप से "चाहते हैं" कि 3 बाइट्स के बीच में पैडिंग हो aऔर bइसलिए यह bअपनी पहुंच की गति को अधिकतम करने के लिए 4-बाइट सीमा पर उतरेगा (और आमतौर पर डिफ़ॉल्ट रूप से ऐसा होगा)।

यदि, हालांकि, आपको बाहरी रूप से परिभाषित संरचना से मेल खाना है, तो आप उस बाहरी परिभाषा के अनुसार कंपाइलर को अपनी संरचना सुनिश्चित करना चाहते हैं। इस मामले में, आप संकलक एक दे सकता है #pragma pack(1)यह बताने के लिए नहीं के सदस्यों के बीच किसी भी गद्दी डालने के लिए - अगर संरचना की परिभाषा के सदस्यों के बीच गद्दी भी शामिल है, आप इसे स्पष्ट रूप से सम्मिलित (जैसे, नामित सदस्यों के साथ आम तौर पर unusedNया ignoreN, या उस पर कुछ गण)।


"आप सामान्य रूप से" चाहते हैं "कि ए और बी के बीच 3 बाइट्स की पैडिंग हो ताकि बी अपनी पहुंच की गति को अधिकतम करने के लिए 4-बाइट की सीमा पर उतरे" - कैसे पैडिंग की 3 बाइट अधिकतम एक्सेस स्पीड होगी?
अश्विन

8
@Ashwin: b4-बाइट सीमा पर रखने का मतलब है कि प्रोसेसर इसे सिंगल-बाइट लोड जारी करके लोड कर सकता है। यद्यपि यह प्रोसेसर पर कुछ हद तक निर्भर करता है, अगर यह एक विषम सीमा पर है तो एक अच्छा मौका है कि इसे लोड करने के लिए प्रोसेसर को दो अलग-अलग लोड निर्देश जारी करने की आवश्यकता होगी, फिर उन टुकड़ों को एक साथ रखने के लिए एक मज़दूर का उपयोग करें। उस आइटम के 3x धीमे लोड के आदेश पर विशिष्ट जुर्माना है।
जेरी कॉफिन

... यदि आप असेंबली और अनलाइन असाइनमेंट को पढ़ने के लिए असेंबली कोड को देखते हैं, तो एलायंस रीड आम तौर पर एक ही एमनोमोनिक है। असंबद्ध रीड असेंबली की 10 पंक्तियां आसानी से हो सकती हैं क्योंकि यह इंट को एक साथ जोड़ती है, इसे बाइट से उठाती है और रजिस्टर के सही स्थानों पर रखती है।
एसएफ।

2
@ एसएफ: यह हो सकता है - लेकिन तब भी जब यह गलत नहीं है - एक x86 सीपीयू (एक स्पष्ट उदाहरण के लिए) पर ऑपरेशन हार्डवेयर में किए जाते हैं, लेकिन आप अभी भी लगभग एक ही ऑपरेशन का सेट प्राप्त करते हैं। और मंदी।
जेरी कॉफिन

8

डेटा तत्वों (उदाहरण के लिए वर्गों और संरचनाओं के सदस्य) आमतौर पर वर्तमान समय के प्रोसेसर के लिए WORD या DWORD सीमाओं पर संरेखित किए जाते हैं ताकि पहुंच समय में सुधार हो सके। एक पते पर एक DWORD पुनर्प्राप्त करना जो 4 से विभाज्य नहीं है, 32 बिट प्रोसेसर पर कम से कम एक अतिरिक्त सीपीयू चक्र की आवश्यकता होती है। इसलिए, यदि आपके पास उदाहरण के लिए तीन चार सदस्य हैं char a, b, c;, तो वे वास्तव में भंडारण के 6 या 12 बाइट लेते हैं।

#pragmaएक्सेस गति की कीमत पर या विभिन्न संकलक लक्ष्यों के बीच संग्रहीत डेटा की संगति के लिए, आपको अधिक कुशल स्थान उपयोग प्राप्त करने के लिए इसे ओवरराइड करने की अनुमति देता है। मैंने 16 बिट से 32 बिट कोड में इस परिवर्तन के साथ बहुत मज़ा किया; मुझे उम्मीद है कि 64 बिट कोड को पोर्ट करने से कुछ कोड के लिए समान प्रकार के सिरदर्द होंगे।


असल में, char a,b,c;आमतौर पर या तो 3 या 4 बाइट्स स्टोरेज (x86 कम से कम) पर ले जाएगा - ऐसा इसलिए है क्योंकि उनकी संरेखण आवश्यकता 1 बाइट है। अगर ऐसा नहीं होता, तो आप कैसे निपटते char str[] = "foo";? एक के लिए प्रवेश charहमेशा एक साधारण लाने-बदलाव-मुखौटा है, जबकि एक करने के लिए पहुँच प्राप्त किया intजा सकता है कि यह गठबंधन है या नहीं, इस पर निर्भर करता है। intहै (x86 पर) एक 32-बिट (4 बाइट) संरेखण क्योंकि अन्यथा आप (कहते हैं) एक intमें DWORDआधा और दूसरे में आधा मिलेगा, और वह दो लुकअप लेगा।
टिम'स

3

कंपाइलर निश्चित मंच पर अधिकतम प्रदर्शन प्राप्त करने के लिए संरचनाओं में सदस्यों को संरेखित कर सकता है। #pragma packनिर्देश आपको उस संरेखण को नियंत्रित करने की अनुमति देता है। आमतौर पर आपको इसे इष्टतम प्रदर्शन के लिए डिफ़ॉल्ट रूप से छोड़ देना चाहिए। यदि आपको दूरस्थ मशीन के लिए एक संरचना पारित करने की आवश्यकता है तो आप आमतौर पर #pragma pack 1किसी भी अवांछित संरेखण को बाहर करने के लिए उपयोग करेंगे ।


2

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

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

यह कुछ I / O डिवाइस के आंतरिक रजिस्टर संरचना को बिल्कुल ओवरले करने के लिए भी हो सकता है, जैसे कि UART या USB नियंत्रक उदाहरण के लिए, ताकि रजिस्टर का उपयोग सीधे पते के बजाय एक संरचना के माध्यम से हो।


1

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

हालांकि, यह उस छोर को प्राप्त करने के लिए एक सुंदर कुंद उपकरण की तरह दिखता है। एक बेहतर तरीका यह होगा कि एक मिनी-ड्राइवर को कोडांतरक में कोडित किया जाए और इस प्राग्मा के साथ घूमने के बजाय इसे C कॉलिंग इंटरफ़ेस दिया जाए।


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

1

मैंने इसे पहले कोड में उपयोग किया है, हालांकि केवल विरासत कोड के साथ इंटरफ़ेस करने के लिए। यह एक मैक ओएस एक्स कोको एप्लिकेशन था जिसे पहले, कार्बन संस्करण (जो मूल M68k सिस्टम 6.5 संस्करण के साथ पीछे की ओर संगत था ... आपको विचार मिलता है) से वरीयता फ़ाइलों को लोड करने की आवश्यकता है। मूल संस्करण में वरीयता फाइलें एक कॉन्फ़िगरेशन संरचना का एक द्विआधारी डंप था, जो #pragma pack(1)अतिरिक्त जगह लेने और जंक (यानी पैडिंग बाइट्स जो अन्यथा संरचना में होगा) को बचाने से बचने के लिए उपयोग किया जाता है ।

कोड के मूल लेखकों ने #pragma pack(1)संरचनाओं को संग्रहीत करने के लिए भी उपयोग किया था जो अंतर-प्रक्रिया संचार में संदेशों के रूप में उपयोग किए गए थे। मुझे लगता है कि यहां कारण अज्ञात या परिवर्तित पैडिंग आकारों की संभावना से बचने के लिए था, क्योंकि कोड कभी-कभी संदेश संरचना के एक विशिष्ट हिस्से को प्रारंभ (ewww) से कई बाइट्स गिनकर देखता था।


1

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


0

ध्यान दें कि डेटा स्थिरता प्राप्त करने के अन्य तरीके हैं जो #pragma पैक ऑफ़र (उदाहरण के लिए कुछ लोग संरचनाओं के लिए #pragma पैक (1) का उपयोग करते हैं जिन्हें नेटवर्क में भेजा जाना चाहिए)। उदाहरण के लिए, निम्न कोड और उसके बाद के आउटपुट देखें:

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

आउटपुट निम्नानुसार है: sizeof (संरचना a): 15, sizeof (संरचना b): 24 आकार (दोa): 30, आकार (twob): 48

सूचना कैसे struct एक का आकार ठीक है क्या बाइट गिनती है, लेकिन struct ख गद्दी जोड़ा गया है (देखें इस गद्दी पर जानकारी के लिए)। #Pragma पैक के विपरीत ऐसा करने से आप "तार प्रारूप" को उचित प्रकारों में परिवर्तित करने का नियंत्रण कर सकते हैं। उदाहरण के लिए, "चार दो [2]" एक "लघु int" एट cetera में।


नहीं यह गलत है। यदि आप b.two की स्मृति में स्थिति को देखते हैं, तो यह b.one (संकलक (और अक्सर) संरेखित करेगा b) b के बाद एक बाइट नहीं है, इसलिए यह शब्द अभिगम से जुड़ा हुआ है। A.two के लिए, यह a.one के बाद बिल्कुल एक बाइट है। यदि आपको एक शॉर्ट इंट के रूप में a.two का उपयोग करने की आवश्यकता है, तो आपके पास 2 विकल्प होने चाहिए, या तो एक यूनियन का उपयोग करें (लेकिन यह आमतौर पर विफल होता है यदि आपके पास धीरज का मुद्दा है), या कोड द्वारा अनपैक / कन्वर्ट (उपयुक्त ntohX फ़ंक्शन का उपयोग करके)
xll669

1
sizeofरिटर्न का उपयोग करके मुद्रितsize_t किया जाना चाहिए%zu । गलत प्रारूप निर्दिष्ट का उपयोग करते हुए अपरिभाषित व्यवहार को आमंत्रित करता है
phuclv

0

कोई इसका उपयोग क्यों करना चाहता है?

संरचना की स्मृति को कम करने के लिए

किसी को इसका उपयोग क्यों नहीं करना चाहिए?

  1. इससे प्रदर्शन जुर्माना हो सकता है, क्योंकि कुछ सिस्टम संरेखित डेटा पर बेहतर काम करते हैं
  2. कुछ मशीन बिना डेटा के पढ़ने में विफल रहेंगी
  3. कोड पोर्टेबल नहीं है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.