2011-11-25 23 views
5

डिफ़ॉल्ट रूप से सी # में सभी struct एस [StructLayout(LayoutKind.Sequential)] -मार्क मूल्य प्रकार के रूप में माना जाता है।सीएलआर क्रमिक structs लेआउट: संरेखण और आकार

using System; 
using System.Reflection; 
using System.Linq; 
using System.Runtime.InteropServices; 

class Foo 
{ 
    struct E { } 
    struct S0 { byte a; } 
    struct S1 { byte a; byte b; } 
    struct S2 { byte a; byte b; byte c; } 
    struct S3 { byte a; int b; } 
    struct S4 { int a; byte b; } 
    struct S5 { byte a; byte b; int c; } 
    struct S6 { byte a; int b; byte c; } 
    struct S7 { int a; byte b; int c; } 
    struct S8 { byte a; short b; int c; } 
    struct S9 { short a; byte b; int c; } 
    struct S10 { long a; byte b; } 
    struct S11 { byte a; long b; } 
    struct S12 { byte a; byte b; short c; short d; long e; } 
    struct S13 { E a; E b; } 
    struct S14 { E a; E b; int c; } 
    struct S15 { byte a; byte b; byte c; byte d; byte e; } 
    struct S16 { S15 b; byte c; } 
    struct S17 { long a; S15 b; } 
    struct S18 { long a; S15 b; S15 c; } 
    struct S19 { long a; S15 b; S15 c; E d; short e; } 
    struct S20 { long a; S15 b; S15 c; short d; E e; } 

    static void Main() 
    { 
    Console.WriteLine("name: contents => size\n"); 
    foreach (var type in typeof(Foo).GetNestedTypes(BindingFlags.NonPublic)) 
    { 
     var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); 
     Console.WriteLine("{0}: {2} => {1}", type.Name, Marshal.SizeOf(type), 
     string.Join("+", fields.Select(_ => Marshal.SizeOf(_.FieldType)))); 
    } 
    } 
} 

आउटपुट है (x86/x64 पर एक ही):

name: contents => size 

E: => 1 
S0: 1 => 1 
S1: 1+1 => 2 
S2: 1+1+1 => 3 
S3: 1+4 => 8 
S4: 4+1 => 8 
S5: 1+1+4 => 8 
S6: 1+4+1 => 12 
S7: 4+1+4 => 12 
S8: 1+2+4 => 8 
S9: 2+1+4 => 8 
S10: 8+1 => 16 
S11: 1+8 => 16 
S12: 1+1+2+2+8 => 16 
S13: 1+1 => 2 
S14: 1+1+4 => 8 
S15: 1+1+1+1+1 => 5 
S16: 5+1 => 6 
S17: 8+5 => 16 
S18: 8+5+5 => 24 
S19: 8+5+5+1+2 => 24 
S20: 8+5+5+2+1 => 24 

इस परिणाम पर मैं नहीं समझ सकता देख रहे हैं तो struct रों की कुछ संख्या ले और इस struct एस के आकार का निरीक्षण करने देता है लेआउट (फ़ील्ड संरेखण और कुल आकार) नियमसेट सीएलआर अनुक्रमिक structs के लिए उपयोग किया जाता है। क्या कोई मुझे इस व्यवहार की व्याख्या कर सकता है?

+3

क्या आप प्रबंधित अंतरिक्ष में structs के लिए सीएलआर द्वारा उपयोग किए जाने वाले नियमों के लिए पूछ रहे हैं (जो एक कार्यान्वयन विस्तार है) या मार्शलर द्वारा उपयोग किए जाने वाले नियमों को प्रबंधित करते हैं जब प्रबंधित एक अप्रबंधित स्थान के बीच structs को मार्शल करते हैं (मार्शल.SizeOf एक संरचना का आकार देता है marshalling के बाद, प्रबंधित अंतरिक्ष में संरचना की नहीं)? – dtb

+0

इसे थोड़ा सा संक्षिप्त करें, कौन से विशेष परिणाम अप्रत्याशित हैं? अनुक्रमिक लेआउट के साथ –

+0

@ डीटीबी यह दो चीजें पूरी तरह से समान हैं। 'मार्शल .इज़ऑफ() 'पूरी तरह से वही आकार देता है, क्योंकि सी #' आकार का ऑपरेटर रिटर्न देता है। – ControlFlow

उत्तर

11

सभी फ़ील्ड उनके प्रकार के आधार पर गठबंधन हैं। देशी प्रकार (int, byte, आदि) सभी अपने आकार से गठबंधन हैं। उदाहरण के लिए, int हमेशा 4 बाइट्स में से एक पर होगा, जबकि बाइट कहीं भी हो सकता है।

यदि छोटे फ़ील्ड int से पहले आते हैं, तो int को 4 बाइट्स के साथ ठीक से गठबंधन करने के लिए आवश्यक होने पर पैडिंग जोड़ा जाएगा। के संरेखण

[1][1][ ][ ][4] // S5 
[1][ ][ 2 ][4] // S8 

साथ ही, struct ही विरासत में: यही कारण है कि S5 (1 + 1 + 4 = 8) और S8 (1 + 2 + 4 = 8) गद्दी है और एक ही आकार खत्म हो जाएगा है इसका सबसे गठबंधन क्षेत्र (यानी S5 और S8, int सबसे गठबंधन क्षेत्र है, इसलिए उनमें से दोनों का संरेखण 4 है)। संरेखण इस तरह विरासत में है ताकि जब आपके पास structs की एक सरणी हो, तो सभी structs के सभी फ़ील्ड ठीक से गठबंधन किए जाएंगे। तो, 4 + 2 = 8.

[4][2][ ][ ] // starts at 0 
[4][2][ ][ ] // starts at 8 
[4][2][ ][ ] // starts at 16 

सूचना 4 हमेशा सबसे गठबंधन क्षेत्र से इनहेरिट बिना 4. द्वारा गठबंधन है, एक सरणी में हर दूसरे तत्व होता है अपने int 4 के बजाय 6 बाइट्स से गठबंधन :

[4][2] // starts at 0 
[4][2] // starts at 6 -- the [4] is not properly aligned! 
[4][2] // starts at 12 

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

मूल प्रदर्शन से परे, संरेखण भी समरूपता के साथ खेल में आता है। सी # मेमोरी मॉडल की गारंटी 4 बाइट चौड़े तक देशी प्रकारों के पढ़ने/लिखने परमाणु होती है, और .NET पर Interlocked वर्ग जैसी परमाणु विशेषताएं होती हैं। इन तरह परमाणु संचालन सीपीयू निर्देशों के लिए उबालते हैं कि स्वयं को काम करने के लिए गठबंधन स्मृति पहुंच की आवश्यकता होती है।

उचित संरेखण बहुत महत्वपूर्ण है!

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

+0

आप बहुत से! लेकिन गैर देशी प्रकार के क्षेत्रों के बारे में क्या? – ControlFlow

+0

यदि संरचना को सबसे गठबंधन क्षेत्र से संरेखण प्राप्त होता है, तो क्यों 'संरचना S22 {S16 a; एस 15 बी; } '6 + 5 => 11' आउटपुट का उत्पादन करता है? सबसे गठबंधन क्षेत्र आकार '6' का' 'है, है ना? – ControlFlow

+0

गैर देशी प्रकार उनके सबसे गठबंधन क्षेत्र के संरेखण का उत्तराधिकारी है। तो 'S15' को' 1' से गठबंधन किया गया है, क्योंकि इसका सबसे गठबंधन क्षेत्र प्रकार 'बाइट' है। 'S16' को' 1' से भी गठबंधन किया गया है, क्योंकि इसमें केवल 'S15' और' बाइट' है। –