एक क्षेत्र प्रकार आदिम या उपयोगकर्ता-परिभाषित है या नहीं, इस पर संरचना संरेखण क्यों निर्भर करता है?


121

में Noda समय वी 2, हम nanosecond समाधान करने के लिए जा रहे हैं। इसका मतलब है कि हम अब उस समय की पूरी श्रृंखला का प्रतिनिधित्व करने के लिए एक 8-बाइट पूर्णांक का उपयोग नहीं कर सकते हैं जिसमें हम रुचि रखते हैं। इससे मुझे नोदा समय के (कई) संरूपों के स्मृति उपयोग की जांच करने के लिए प्रेरित किया गया है, जिसने मुझे प्रेरित किया है। सीएलआर के संरेखण निर्णय में थोड़ी विषमता को उजागर करने के लिए।

सबसे पहले, मुझे लगता है यह है कि है एक कार्यान्वयन निर्णय, और कहा कि डिफ़ॉल्ट व्यवहार को किसी भी समय बदल सकता है। मुझे लगता है कि मैं कर सकते हैं का उपयोग कर इसे संशोधित [StructLayout]और [FieldOffset], लेकिन मेरे लिए एक समाधान है जो कि आवश्यकता नहीं किया था यदि संभव हो तो साथ आने चाहते हैं।

मेरा मुख्य परिदृश्य यह है कि मेरे पास एक structसंदर्भ-प्रकार फ़ील्ड और दो अन्य मान-प्रकार फ़ील्ड हैं, जहाँ वे फ़ील्ड उनके लिए सरल आवरण हैं int। मुझे उम्मीद थी कि 64-बिट सीएलआर (संदर्भ के लिए 8 और अन्य में से प्रत्येक के लिए 4) पर 16 बाइट्स के रूप में प्रतिनिधित्व किया जाएगा, लेकिन किसी कारण से यह 24 बाइट्स का उपयोग कर रहा है। मैं सरणियों का उपयोग करके अंतरिक्ष को माप रहा हूं, वैसे - मैं समझता हूं कि विभिन्न स्थितियों में लेआउट अलग-अलग हो सकता है, लेकिन यह एक उचित शुरुआती बिंदु की तरह लगा।

यहाँ एक नमूना कार्यक्रम इस मुद्दे को प्रदर्शित करता है:

using System;
using System.Runtime.InteropServices;

#pragma warning disable 0169

struct Int32Wrapper
{
    int x;
}

struct TwoInt32s
{
    int x, y;
}

struct TwoInt32Wrappers
{
    Int32Wrapper x, y;
}

struct RefAndTwoInt32s
{
    string text;
    int x, y;
}

struct RefAndTwoInt32Wrappers
{
    string text;
    Int32Wrapper x, y;
}    

class Test
{
    static void Main()
    {
        Console.WriteLine("Environment: CLR {0} on {1} ({2})",
            Environment.Version,
            Environment.OSVersion,
            Environment.Is64BitProcess ? "64 bit" : "32 bit");
        ShowSize<Int32Wrapper>();
        ShowSize<TwoInt32s>();
        ShowSize<TwoInt32Wrappers>();
        ShowSize<RefAndTwoInt32s>();
        ShowSize<RefAndTwoInt32Wrappers>();
    }

    static void ShowSize<T>()
    {
        long before = GC.GetTotalMemory(true);
        T[] array = new T[100000];
        long after  = GC.GetTotalMemory(true);        
        Console.WriteLine("{0}: {1}", typeof(T),
                          (after - before) / array.Length);
    }
}

और मेरे लैपटॉप पर संकलन और आउटपुट:

c:\Users\Jon\Test>csc /debug- /o+ ShowMemory.cs
Microsoft (R) Visual C# Compiler version 12.0.30501.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.


c:\Users\Jon\Test>ShowMemory.exe
Environment: CLR 4.0.30319.34014 on Microsoft Windows NT 6.2.9200.0 (64 bit)
Int32Wrapper: 4
TwoInt32s: 8
TwoInt32Wrappers: 8
RefAndTwoInt32s: 16
RefAndTwoInt32Wrappers: 24

इसलिए:

  • यदि आपके पास एक संदर्भ प्रकार फ़ील्ड नहीं है, तो CLR Int32Wrapperफ़ील्ड को एक साथ पैक करने में प्रसन्न है ( TwoInt32Wrappersजिसका आकार 8 है)
  • यहां तक ​​कि एक संदर्भ प्रकार के क्षेत्र के साथ, सीएलआर अभी भी intखेतों को एक साथ पैक करने में प्रसन्न है ( RefAndTwoInt32sजिसका आकार 16 है)
  • दोनों को मिलाकर, प्रत्येक Int32Wrapperक्षेत्र 8 बाइट्स के लिए गद्देदार / संरेखित प्रतीत होता है। ( RefAndTwoInt32Wrappers24 का आकार है।)
  • डिबगर में समान कोड चलाना (लेकिन अभी भी रिलीज़ बिल्ड) 12 का आकार दिखाता है।

कुछ अन्य प्रयोगों के समान परिणाम मिले हैं:

  • मान प्रकार फ़ील्ड के बाद संदर्भ प्रकार फ़ील्ड को रखने से मदद नहीं मिलती है
  • मदद objectकरने के बजाय उपयोग करने से string(मुझे उम्मीद है कि यह "किसी भी संदर्भ प्रकार")
  • संदर्भ के चारों ओर "रैपर" के रूप में एक और संरचना का उपयोग करने से मदद नहीं मिलती है
  • संदर्भ के चारों ओर एक आवरण के रूप में एक सामान्य संरचना का उपयोग करना मदद नहीं करता है
  • अगर मैं फ़ील्ड (सादगी के लिए जोड़े में) जोड़ता रहता हूं, तो intफ़ील्ड अभी भी 4 बाइट्स के लिए और Int32Wrapper8 बाइट्स के लिए फ़ील्ड्स की गणना करता है
  • [StructLayout(LayoutKind.Sequential, Pack = 4)]दृष्टि में हर संरचना को जोड़ने से परिणाम नहीं बदलते हैं

क्या किसी के पास इसके लिए कोई स्पष्टीकरण है (आदर्श रूप से संदर्भ प्रलेखन के साथ) या मैं सीएलआर को संकेत कैसे दे सकता हूं इसका एक सुझाव है कि मैं चाहूंगा कि खेतों को एक निरंतर क्षेत्र ऑफसेट निर्दिष्ट किए बिना पैक किया जाए ?


1
आप वास्तव में उपयोग Ref<T>नहीं कर रहे हैं लेकिन stringइसके बजाय उपयोग कर रहे हैं , ऐसा नहीं है कि यह एक फर्क करना चाहिए।
तवानफोसन

2
यदि आप दो डालते हैं TwoInt32Wrappers, तो दो या एक Int64ए के साथ एक संरचना बनाते हैं क्या होता है TwoInt32Wrappers? कैसे के बारे में अगर आप एक सामान्य Pair<T1,T2> {public T1 f1; public T2 f2;}बनाने के लिए Pair<string,Pair<int,int>>और फिर बनाने और Pair<string,Pair<Int32Wrapper,Int32Wrapper>>? कौन सा संयोजन चीजों को पैड करने के लिए जेटर को मजबूर करता है?
सुपरकैट

7
@ सुपरकैट: शायद आपके लिए सबसे अच्छा है कि आप कोड को कॉपी करें और अपने लिए प्रयोग करें - लेकिन Pair<string, TwoInt32Wrappers> यह सिर्फ 16 बाइट्स देता है, ताकि समस्या का समाधान हो जाए। चित्त आकर्षण करनेवाला।
जॉन स्कीट

9
@ एसएलएस: कभी-कभी जब कोई संरचना मूल कोड में पारित हो जाती है, तो रनटाइम सभी डेटा को एक अलग लेआउट के साथ एक संरचना में कॉपी कर देगा। Marshal.SizeOfमूल कोड को पारित किए जाने वाले ढांचे के आकार को वापस करेगा, जो .NET कोड में संरचना के आकार से कोई संबंध नहीं है।
सुपरकाट

5
दिलचस्प अवलोकन: मोनो सही परिणाम देता है। पर्यावरण: CLR 4.0.30319.17020 यूनिक्स पर 3.13.0.24 (64 बिट) Int32Wrapper: 4 TwoInt32s: 8 TwoInt32Wrappers: 8 RefAndTwoInt32s: 16 RefAndTwoInt32Wrappers: 16
एंड्रीअकेशिन

जवाबों:


85

मुझे लगता है कि यह एक बग है। आप स्वचालित लेआउट के साइड-इफ़ेक्ट को देख रहे हैं, यह गैर-तुच्छ क्षेत्रों को एक पते पर संरेखित करना पसंद करता है जो 64-बिट मोड में 8 बाइट्स से अधिक है। यह तब भी होता है जब आप स्पष्ट रूप से [StructLayout(LayoutKind.Sequential)]विशेषता को लागू करते हैं। ऐसा होने वाला नहीं है।

आप इसे इस तरह से संरचित सदस्यों को जोड़कर और परीक्षण कोड को जोड़कर देख सकते हैं:

    var test = new RefAndTwoInt32Wrappers();
    test.text = "adsf";
    test.x.x = 0x11111111;
    test.y.x = 0x22222222;
    Console.ReadLine();      // <=== Breakpoint here

जब ब्रेकपॉइंट हिट होता है, तो डीबग + विंडोज + मेमोरी + मेमोरी 1 का उपयोग करें। 4-बाइट पूर्णांक पर स्विच करें और &testपता फ़ील्ड में डालें:

 0x000000E928B5DE98  0ed750e0 000000e9 11111111 00000000 22222222 00000000 

0xe90ed750e0मेरी मशीन पर स्ट्रिंग सूचक है (तुम्हारा नहीं)। आप आसानी से देख सकते हैं Int32Wrappers, अतिरिक्त 4 बाइट्स पैडिंग के साथ, जिसने आकार को 24 बाइट्स में बदल दिया। वापस संरचना पर जाएं और स्ट्रिंग को अंतिम रखें। दोहराएँ और आप देखेंगे स्ट्रिंग सूचक अभी भी पहले है। उल्लंघन करते हुए LayoutKind.Sequential, आप मिल गए LayoutKind.Auto

इसे ठीक करने के लिए Microsoft को समझाना मुश्किल हो रहा है, इसने बहुत लंबे समय तक इस तरह से काम किया है इसलिए किसी भी परिवर्तन से कुछ टूटने वाला है । सीएलआर केवल [StructLayout]एक संरचना के प्रबंधित संस्करण के लिए सम्मान करने का प्रयास करता है और इसे प्रफुल्लित करता है, यह सामान्य रूप से जल्दी देता है। किसी भी संरचना के लिए कुख्यात है जिसमें डेटाइम है। आपको केवल सच्चा लेआउटकाइंड की गारंटी मिलती है जब किसी संरचना को मार्शलिंग किया जाता है। मार्शल्ड संस्करण निश्चित रूप से 16 बाइट्स है, जैसा Marshal.SizeOf()कि आपको बताएगा।

LayoutKind.Explicitइसे ठीक करने का उपयोग करना , न कि आप जो सुनना चाहते थे।


7
"इसे ठीक करने के लिए Microsoft को समझाना मुश्किल हो रहा है, इसने बहुत लंबे समय तक काम किया है इसलिए कोई भी बदलाव कुछ होने वाला है।" तथ्य यह है कि यह स्पष्ट रूप से 32 बिट या मोनो में प्रकट नहीं होता है (अन्य टिप्पणियों के अनुसार) मदद कर सकता है।
एनपीएसएफ ३०००

StructLayoutAttribute का प्रलेखन काफी दिलचस्प है। मूल रूप से, प्रबंधित मेमोरी में स्ट्रक्चुएलआउट के माध्यम से केवल ब्लिटेबल प्रकारों को नियंत्रित किया जाता है। दिलचस्प, यह कभी नहीं पता था।
माइकल स्टम

@ कोई भी इसे ठीक नहीं करता है। क्या आपने 8 को ऑफसेट करने के लिए दोनों क्षेत्रों पर लेआउट डाला था? यदि ऐसा है तो x और y समान हैं और एक को बदलने से दूसरे में परिवर्तन होता है। स्पष्ट रूप से नहीं जॉन के बाद क्या है।
बार्टोज़अडैमकेवस्की

stringएक और नए संदर्भ प्रकार ( class) के साथ प्रतिस्थापित करना, जिस पर किसी ने आवेदन किया [StructLayout(LayoutKind.Sequential)]है, वह कुछ भी बदलने के लिए प्रकट नहीं होता है। विपरीत दिशा में, स्मृति उपयोग में परिवर्तन के [StructLayout(LayoutKind.Auto)]लिए आवेदन करना । struct Int32WrapperTwoInt32Wrappers
जेपी स्टिग नीलसन

1
"इसे ठीक करने के लिए Microsoft को समझाना मुश्किल हो रहा है, इसने बहुत लंबे समय तक काम किया है इसलिए कोई भी बदलाव कुछ होने वाला है।" xkcd.com/1172
iCodeSometime

19

EDIT2

struct RefAndTwoInt32Wrappers
{
    public int x;
    public string s;
}

यह कोड 8 बाइट से संरेखित होगा, इसलिए संरचना में 16 बाइट होंगे। इसकी तुलना करके:

struct RefAndTwoInt32Wrappers
{
    public int x,y;
    public string s;
}

4 बाइट गठबंधन होगा इसलिए इस संरचना में 16 बाइट्स भी होंगे। इसलिए यहाँ तर्क यह है कि सीएलआर में संरचना परिवर्तन सबसे अधिक संरेखित क्षेत्रों की संख्या से निर्धारित होता है, क्लैस स्पष्ट रूप से ऐसा नहीं कर सकते हैं ताकि वे 8 बाइट संरेखित रहें।

अब अगर हम उस सभी को जोड़ते हैं और संरचना बनाते हैं:

struct RefAndTwoInt32Wrappers
{
    public int x,y;
    public Int32Wrapper z;
    public string s;
}

इसमें 24 बाइट्स होंगे {x, y} में 4 बाइट्स होंगे और {z, s} में 8 बाइट्स होंगे। एक बार जब हम संरचना में एक रेफरी प्रकार का परिचय देते हैं तो सीएलआर हमेशा क्लास संरेखण से मेल खाने के लिए हमारी कस्टम संरचना को संरेखित करेगा।

struct RefAndTwoInt32Wrappers
{
    public Int32Wrapper z;
    public long l;
    public int x,y;  
}

इस कोड में 24 बाइट्स होंगे क्योंकि Int32Wrapper को लंबे समय तक एक जैसे रखा जाएगा। इसलिए कस्टम संरचना आवरण हमेशा संरचना में उच्चतम / सर्वोत्तम संरेखित क्षेत्र के लिए संरेखित होगा या यह आंतरिक आंतरिक महत्वपूर्ण क्षेत्र होगा। तो एक रेफ स्ट्रिंग के मामले में जो कि 8 बाइट से जुड़ा होता है, स्ट्रक्चर रैपर को अलाइन करेगा।

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


संपादित करें

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

मैं cli कोड का निरीक्षण करूंगा और आगे के अपडेट पोस्ट करूंगा अगर कुछ उपयोगी मिलेगा।


यह एक एलाइनमेंट स्ट्रैटेजी है जिसका उपयोग .NET मेम एलोकेशनर द्वारा किया जाता है।

public static RefAndTwoInt32s[] test = new RefAndTwoInt32s[1];

static void Main()
{
    test[0].text = "a";
    test[0].x = 1;
    test[0].x = 1;

    Console.ReadKey();
}

यह कोड x64 के तहत .net40 के साथ संकलित है, विनडबग में निम्नलिखित कार्य करने देता है:

पहले Heap पर टाइप करने दें:

    0:004> !dumpheap -type Ref
       Address               MT     Size
0000000003e72c78 000007fe61e8fb58       56    
0000000003e72d08 000007fe039d3b78       40    

Statistics:
              MT    Count    TotalSize Class Name
000007fe039d3b78        1           40 RefAndTwoInt32s[]
000007fe61e8fb58        1           56 System.Reflection.RuntimeAssembly
Total 2 objects

एक बार हमारे पास यह देखने की सुविधा है कि उस पते के नीचे क्या है:

    0:004> !do 0000000003e72d08
Name:        RefAndTwoInt32s[]
MethodTable: 000007fe039d3b78
EEClass:     000007fe039d3ad0
Size:        40(0x28) bytes
Array:       Rank 1, Number of elements 1, Type VALUETYPE
Fields:
None

हम देखते हैं कि यह एक ValueType है और इसका निर्माण हमने किया है। चूंकि यह एक ऐसा सरणी है, जिसके लिए हमें सरणी में किसी एकल तत्व का ValueType डीएफ़ प्राप्त करना होगा:

    0:004> !dumparray -details 0000000003e72d08
Name:        RefAndTwoInt32s[]
MethodTable: 000007fe039d3b78
EEClass:     000007fe039d3ad0
Size:        40(0x28) bytes
Array:       Rank 1, Number of elements 1, Type VALUETYPE
Element Methodtable: 000007fe039d3a58
[0] 0000000003e72d18
    Name:        RefAndTwoInt32s
    MethodTable: 000007fe039d3a58
    EEClass:     000007fe03ae2338
    Size:        32(0x20) bytes
    File:        C:\ConsoleApplication8\bin\Release\ConsoleApplication8.exe
    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        000007fe61e8c358  4000006        0            System.String      0     instance     0000000003e72d30     text
        000007fe61e8f108  4000007        8             System.Int32      1     instance                    1     x
        000007fe61e8f108  4000008        c             System.Int32      1     instance                    0     y

संरचना वास्तव में 32 बाइट्स है क्योंकि यह 16 बाइट्स पैडिंग के लिए आरक्षित है इसलिए प्रत्येक संरचना गेट से आकार में कम से कम 16 बाइट्स होती है।

यदि आप ints से 16 बाइट्स और एक स्ट्रिंग रेफरी जोड़ते हैं: 0000000003e72d18 + 8 बाइट्स EE / पैडिंग आप 0000000003e72d30 पर समाप्त हो जाएंगे और यह स्ट्रिंग संदर्भ के लिए घूर बिंदु है, और सभी संदर्भों में उनके पहले वास्तविक डेटा क्षेत्र से 8 बाइट गद्देदार हैं। यह इस संरचना के लिए हमारी 32 बाइट्स के लिए बनाता है।

आइए देखें कि क्या स्ट्रिंग वास्तव में इस तरह से गद्देदार है:

0:004> !do 0000000003e72d30    
Name:        System.String
MethodTable: 000007fe61e8c358
EEClass:     000007fe617f3720
Size:        28(0x1c) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      a
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fe61e8f108  40000aa        8         System.Int32  1 instance                1 m_stringLength
000007fe61e8d640  40000ab        c          System.Char  1 instance               61 m_firstChar
000007fe61e8c358  40000ac       18        System.String  0   shared           static Empty
                                 >> Domain:Value  0000000001577e90:NotInit  <<

अब उपर्युक्त कार्यक्रम का उसी तरह विश्लेषण करते हैं:

public static RefAndTwoInt32Wrappers[] test = new RefAndTwoInt32Wrappers[1];

static void Main()
{
    test[0].text = "a";
    test[0].x.x = 1;
    test[0].y.x = 1;

    Console.ReadKey();
}

0:004> !dumpheap -type Ref
     Address               MT     Size
0000000003c22c78 000007fe61e8fb58       56    
0000000003c22d08 000007fe039d3c00       48    

Statistics:
              MT    Count    TotalSize Class Name
000007fe039d3c00        1           48 RefAndTwoInt32Wrappers[]
000007fe61e8fb58        1           56 System.Reflection.RuntimeAssembly
Total 2 objects

हमारी संरचना अब 48 बाइट्स है।

0:004> !dumparray -details 0000000003c22d08
Name:        RefAndTwoInt32Wrappers[]
MethodTable: 000007fe039d3c00
EEClass:     000007fe039d3b58
Size:        48(0x30) bytes
Array:       Rank 1, Number of elements 1, Type VALUETYPE
Element Methodtable: 000007fe039d3ae0
[0] 0000000003c22d18
    Name:        RefAndTwoInt32Wrappers
    MethodTable: 000007fe039d3ae0
    EEClass:     000007fe03ae2338
    Size:        40(0x28) bytes
    File:        C:\ConsoleApplication8\bin\Release\ConsoleApplication8.exe
    Fields:
                      MT    Field   Offset                 Type VT     Attr            Value Name
        000007fe61e8c358  4000009        0            System.String      0     instance     0000000003c22d38     text
        000007fe039d3a20  400000a        8             Int32Wrapper      1     instance     0000000003c22d20     x
        000007fe039d3a20  400000b       10             Int32Wrapper      1     instance     0000000003c22d28     y

यहां स्थिति समान है, अगर हम 0000000003c22d18 + 8 बाइट्स को स्ट्रिंग रेफरी में जोड़ते हैं, तो हम पहले इंट रैपर की शुरुआत में समाप्त हो जाएंगे, जहां मूल्य वास्तव में उस पते पर इंगित होता है, जिस पर हम हैं।

अब हम देख सकते हैं कि प्रत्येक मान एक वस्तु संदर्भ है जो फिर से पुष्टि करता है कि 0000000003c22d20 को देख कर।

0:004> !do 0000000003c22d20
<Note: this object has an invalid CLASS field>
Invalid object

वास्तव में यह सही है क्योंकि इसकी संरचना का पता हमें कुछ भी नहीं बताता है यदि यह ओबीजी या वीटी है।

0:004> !dumpvc 000007fe039d3a20   0000000003c22d20    
Name:        Int32Wrapper
MethodTable: 000007fe039d3a20
EEClass:     000007fe03ae23c8
Size:        24(0x18) bytes
File:        C:\ConsoleApplication8\bin\Release\ConsoleApplication8.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fe61e8f108  4000001        0         System.Int32  1 instance                1 x

तो वास्तविकता में यह एक यूनियन प्रकार की तरह है जो इस बार के आसपास 8 बाइट संरेखित करेगा (पैडिंग के सभी मूल संरचना के साथ गठबंधन किया जाएगा)। यदि ऐसा नहीं होता, तो हम 20 बाइट्स के साथ समाप्त हो जाते हैं और यह इष्टतम नहीं है इसलिए मेम आवंटनकर्ता इसे कभी भी नहीं होने देंगे। यदि आप गणित को फिर से करते हैं तो यह पता चलेगा कि संरचना वास्तव में आकार के 40 बाइट्स है।

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

यहाँ अंतिम प्रश्न यह है कि अचानक हमें ऐसा क्यों लेआउट मिल सकता है। यदि आप एक काउंटर क्षेत्र वृद्धि के साथ एक [[] संरचना के साथ int []] के jited कोड और प्रदर्शन की तुलना करते हैं, तो दूसरा एक 8 बाइट संरेखित पता एक संघ उत्पन्न करेगा, लेकिन जब यह jited अधिक अनुकूलित विधानसभा कोड (singe) में अनुवाद करता है LEA बनाम कई MOV)। हालाँकि यहाँ वर्णित मामले में प्रदर्शन वास्तव में बदतर होगा, इसलिए मेरा कहना है कि यह अंतर्निहित सीएलआर कार्यान्वयन के अनुरूप है क्योंकि यह एक कस्टम प्रकार है जिसमें कई फ़ील्ड हो सकते हैं इसलिए इसके बजाय शुरुआती पता लगाना आसान / बेहतर हो सकता है मूल्य (चूंकि यह असंभव होगा) और वहाँ पर स्ट्रक्चर पैडिंग करते हैं, जिसके परिणामस्वरूप बड़े बाइट का आकार होता है।


1
यह खुद को देखते हुए, का आकार RefAndTwoInt32Wrappers 32 बाइट्स नहीं है - यह 24 है, जो मेरे कोड के साथ रिपोर्ट किया गया है। यदि आप उपयोग करने के बजाय स्मृति दृश्य में देखते हैं dumparray, और अलग-अलग मानों वाले (कहे) 3 तत्वों के साथ एक सरणी के लिए मेमोरी देखें, तो आप स्पष्ट रूप से देख सकते हैं कि प्रत्येक तत्व में 8-बाइट स्ट्रिंग संदर्भ और दो 8-बाइट पूर्णांक होते हैं । मुझे संदेह है कि dumparrayमूल्यों को केवल संदर्भ के रूप में दिखाया जा रहा है क्योंकि यह नहीं जानता कि Int32Wrapperमूल्यों को कैसे प्रदर्शित किया जाए । वे "संदर्भ" खुद को इंगित करते हैं; वे अलग-अलग मूल्य नहीं हैं।
जॉन स्कीट

1
मुझे यकीन नहीं है कि आपको "16 बाइट पैडिंग" कहां से मिलेगी, लेकिन मुझे संदेह है कि यह हो सकता है क्योंकि आप ऐरे ऑब्जेक्ट के आकार को देख रहे हैं, जो "16 बाइट्स + काउंट * एलिमेंट साइज" होगा। तो गिनती 2 के साथ एक सरणी का आकार 72 (16 + 2 * 24) है, जो कि dumparrayदिखाता है।
जॉन स्कीट

@ जोंन ने आपकी संरचना को धूमिल किया और यह जांचा कि ढेर पर कितनी जगह है? आम तौर पर सरणी का आकार सरणी की शुरुआत में रखा जाता है, यह भी सत्यापित किया जा सकता है।
बार्टोज़अडैमकेवस्की

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

1
नहीं, थ्रीइंट 3232 क्रैपर 12 बाइट्स के रूप में, 16 के रूप में फोरइंट 32 व्रैपर्स, फाइवइंट 32 व्रैपर्स 20 के रूप में समाप्त होते हैं। मुझे रेफरेंस टाइप फील्ड के अलावा लेआउट को इतनी तेजी से बदलने के बारे में कुछ भी तार्किक नहीं दिखता है। और ध्यान दें कि खेतों के प्रकार होने पर 8-बाइट संरेखण को अनदेखा करना काफी खुश है Int32। मैं इस बारे में बहुत परेशान नहीं हूं कि ईमानदार होने के लिए यह क्या करता है - लेकिन मैंने इसकी जांच नहीं की है।
जॉन स्कीट

9

सारांश देखें कि @ हंस पासंत का उत्तर शायद ऊपर है। लेआउट अनुक्रम काम नहीं करता है


कुछ परीक्षण:

यह निश्चित रूप से केवल 64 बिट पर है और वस्तु संदर्भ "जहर" संरचना है। 32 बिट आप क्या उम्मीद कर रहे हैं:

Environment: CLR 4.0.30319.34209 on Microsoft Windows NT 6.2.9200.0 (32 bit)
ConsoleApplication1.Int32Wrapper: 4
ConsoleApplication1.TwoInt32s: 8
ConsoleApplication1.TwoInt32Wrappers: 8
ConsoleApplication1.ThreeInt32Wrappers: 12
ConsoleApplication1.Ref: 4
ConsoleApplication1.RefAndTwoInt32s: 12
ConsoleApplication1.RefAndTwoInt32Wrappers: 12
ConsoleApplication1.RefAndThreeInt32s: 16
ConsoleApplication1.RefAndThreeInt32Wrappers: 16

जैसे ही ऑब्जेक्ट संदर्भ जोड़ा जाता है सभी संरचनाएं 8 बाइट्स के बजाय 4 बाइट के आकार का विस्तार करती हैं। परीक्षण का विस्तार:

Environment: CLR 4.0.30319.34209 on Microsoft Windows NT 6.2.9200.0 (64 bit)
ConsoleApplication1.Int32Wrapper: 4
ConsoleApplication1.TwoInt32s: 8
ConsoleApplication1.TwoInt32Wrappers: 8
ConsoleApplication1.ThreeInt32Wrappers: 12
ConsoleApplication1.Ref: 8
ConsoleApplication1.RefAndTwoInt32s: 16
ConsoleApplication1.RefAndTwoInt32sSequential: 16
ConsoleApplication1.RefAndTwoInt32Wrappers: 24
ConsoleApplication1.RefAndThreeInt32s: 24
ConsoleApplication1.RefAndThreeInt32Wrappers: 32
ConsoleApplication1.RefAndFourInt32s: 24
ConsoleApplication1.RefAndFourInt32Wrappers: 40

जैसा कि आप संदर्भ के रूप में जल्द ही देख सकते हैं हर Int32Wrapper 8 बाइट्स बन जाता है तो सरल संरेखण नहीं है। मैं सरणी आवंटन को कम कर देता हूं, यह LoH आवंटन था जो अलग-अलग संरेखित है।


4

मिश्रण में कुछ डेटा जोड़ने के लिए - मैंने आपके पास से एक और प्रकार बनाया है:

struct RefAndTwoInt32Wrappers2
{
    string text;
    TwoInt32Wrappers z;
}

कार्यक्रम लिखते हैं:

RefAndTwoInt32Wrappers2: 16

तो ऐसा लगता TwoInt32Wrappersहै कि नई RefAndTwoInt32Wrappers2संरचना में संरचना ठीक से संरेखित है ।


क्या आप 64 बिट चला रहे हैं? 32 बिट में संरेखण ठीक है
बेन एडम्स

मेरे निष्कर्ष सभी विभिन्न वातावरणों के लिए समान हैं।
जेसी सी। स्लाइसर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.