ক্ষেত্রের প্রকারটি আদিম বা ব্যবহারকারী-সংজ্ঞায়িত কিনা তার উপর কেন স্ট্রাক্ট অ্যালাইনমেন্ট নির্ভর করে?


121

ইন নোদা সময় v2, আমরা ন্যানোসেকেন্ড রেজল্যুশন থেকে সরানোর করছি। এর অর্থ আমরা আমাদের আগ্রহী এমন পুরো সময়ের প্রতিনিধিত্ব করতে আর 8-বাইট পূর্ণসংখ্যার ব্যবহার করতে পারি না That এটি আমাকে নোদা সময়ের (অনেক) স্ট্রাক্টের স্মৃতি ব্যবহারের তদন্ত করতে উত্সাহিত করেছিল, যার ফলে আমাকে নেতৃত্ব দিয়েছে has সিএলআর এর প্রান্তিককরণ সিদ্ধান্তে একটি সামান্য বিজোড়তা উদঘাটনের জন্য।

প্রথমত, আমি বুঝতে পারছি এই যে হয় একটি বাস্তবায়ন সিদ্ধান্ত, এবং যে ডিফল্ট আচরণ যে কোন সময়ে পরিবর্তন হতে পারে। আমি বুঝতে পারছি যে আমি করতে পারেন ব্যবহার করে এটি পরিবর্তন [StructLayout]এবং [FieldOffset], কিন্তু আমি বরং একটি সমাধান যা প্রয়োজন হয়নি সম্ভব হলে সঙ্গে আসা পর্যন্ত চাই।

আমার মূল দৃশ্যটি হ'ল আমার কাছে structএমন একটি রয়েছে যা একটি রেফারেন্স-টাইপ ফিল্ড এবং অন্য দুটি মান-ধরণের ক্ষেত্র রয়েছে, যেখানে those ক্ষেত্রগুলির জন্য সহজ মোড়ক int। আমি আশা করেছিলাম যে that৪-বিট সিএলআরতে (রেফারেন্সের জন্য ৮ এবং অন্য প্রত্যেকের জন্য 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

তাই:

  • আপনার যদি কোনও রেফারেন্স টাইপ ক্ষেত্র না থাকে, সিএলআর Int32Wrapperএকসাথে ক্ষেত্রগুলি প্যাক করতে খুশি ( TwoInt32Wrappers8 এর আকার রয়েছে)
  • এমনকি একটি রেফারেন্স ধরণের ক্ষেত্র সহ, সিএলআর এখনও intক্ষেত্রগুলি একসাথে প্যাক করতে খুশি ( RefAndTwoInt32s16 এর আকার রয়েছে)
  • দুটির সংমিশ্রণে প্রতিটি Int32Wrapperক্ষেত্র প্যাডড / 8 বাইটে সারিবদ্ধ বলে মনে হচ্ছে। ( RefAndTwoInt32Wrappersআকার 24 আছে।)
  • ডিবাগারে একই কোডটি চালানো (তবে এখনও একটি রিলিজ বিল্ড) 12 এর আকার দেখায়।

আরও কয়েকটি পরীক্ষা-নিরীক্ষার অনুরূপ ফলাফল পাওয়া গেছে:

  • মান ধরণের ক্ষেত্রগুলির পরে রেফারেন্স প্রকারের ক্ষেত্র স্থাপন করা কোনও লাভ করে না
  • objectপরিবর্তে ব্যবহার করা stringসাহায্য করে না (আমি এটি "কোনও রেফারেন্স টাইপ" আশা করি)
  • রেফারেন্সের চারপাশে "র‌্যাপার" হিসাবে অন্য কাঠামো ব্যবহার করা কোনও লাভ করে না
  • জেনেরিক স্ট্রাক্টটি রেফারেন্সের চারপাশে মোড়ক হিসাবে ব্যবহার করা সাহায্য করে না
  • যদি আমি ক্ষেত্রগুলি যোগ করতে থাকি (সরলতার জন্য জোড়ায়), intক্ষেত্রগুলি এখনও 4 বাইট হিসাবে Int32Wrapperগণনা করে এবং ক্ষেত্রগুলি 8 বাইট হিসাবে গণনা করে
  • [StructLayout(LayoutKind.Sequential, Pack = 4)]প্রতিটি কাঠামোর দৃষ্টিতে যুক্ত করা ফলাফলগুলি পরিবর্তন করে না

এর জন্য (আদর্শভাবে রেফারেন্স ডকুমেন্টেশনের সাহায্যে) কারও কাছে কোনও ব্যাখ্যা আছে বা আমি কীভাবে সিএলআরকে ইঙ্গিত পেতে পারি যে আমি ধ্রুবক ফিল্ড অফসেট উল্লেখ না করে ক্ষেত্রগুলি প্যাক করতে চাই ?


1
আপনি আসলে ব্যবহার করছেন বলে মনে হয় না Ref<T>তবে stringপরিবর্তে ব্যবহার করছেন, এটির কোনও পার্থক্য হওয়া উচিত নয়।
tvanfosson

2
আপনি যদি দুটি দিয়ে দুটি TwoInt32Wrappersবা একটি Int64এবং একটি দিয়ে একটি কাঠামো তৈরি করেন তবে কী হবে TwoInt32Wrappers? আপনি যদি জেনেরিক Pair<T1,T2> {public T1 f1; public T2 f2;}তৈরি করেন Pair<string,Pair<int,int>>এবং তারপরে তৈরি করেন এবং Pair<string,Pair<Int32Wrapper,Int32Wrapper>>কীভাবে? কোন সংমিশ্রণগুলি JITter কে জিনিসগুলি প্যাড করতে বাধ্য করে?
সুপারক্যাট

7
@supercat: এটা সম্ভবত শ্রেষ্ঠ আপনি নিজেকে কোড এবং পরীক্ষা কপি করতে জন্য - কিন্তু Pair<string, TwoInt32Wrappers> নেই যাতে সমস্যার সমাধান হবে মাত্র 16 বাইট দিতে। আকর্ষনীয়।
জন স্কিটি

9
@ এসএলাক্স: কখনও কখনও যখন কোনও কাঠামো স্থানীয় কোডে পৌঁছে দেওয়া হয়, রানটাইম সমস্ত ডেটা একটি ভিন্ন বিন্যাস সহ একটি কাঠামোতে অনুলিপি করে। Marshal.SizeOfনেটিক কোডে কাঠামোর আকারের সাথে কোনও সম্পর্ক থাকার দরকার নেই এমন কাঠামোর আকারটি দেশীয় কোডে পাঠানো হবে।
সুপারক্যাট

5
আকর্ষণীয় পর্যবেক্ষণ: মনো সঠিক ফলাফল দেয়। পরিবেশ: সিএনআর 4.0.30319.17020 ইউনিক্সে 3.13.0.24 (bit৪ বিট) ইন্টার 32 র্যাপার: 4 টু আইন্ট 32 এস: 8 টু আইন্ট 32 র‍্যাপারস: 8 রেফঅ্যান্ডটুইআইন্ট 32স: 16 রেফঅ্যান্ডটুই আইন্ট 32 র‌্যাপার্স: 16
AndreyAkinshin

উত্তর:


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 বাইটে পরিণত করেছে। স্ট্রাক্টটিতে ফিরে যান এবং স্ট্রিংটি শেষ রাখুন। পুনরাবৃত্তি করুন এবং আপনি স্ট্রিং পয়েন্টারটি এখনও প্রথম দেখতে পাবেন is লঙ্ঘন LayoutKind.Sequential, আপনি পেয়েছেন LayoutKind.Auto

এটি ঠিক করার জন্য মাইক্রোসফ্টকে বোঝানো মুশকিল হতে চলেছে, এটি দীর্ঘ সময় ধরে এইভাবে কাজ করেছে তাই কোনও পরিবর্তন কিছু ভেঙে যাচ্ছে । সিএলআর কেবল [StructLayout]একটি স্ট্রাক্টের পরিচালিত সংস্করণকে সম্মান জানাতে এবং এটিকে দৃষ্টিনন্দন করার চেষ্টা করে, এটি সাধারণভাবে দ্রুত ছেড়ে দেয়। ডেটটাইমযুক্ত যে কোনও কাঠামোর জন্য কুখ্যাত। কোনও কাঠামো মার্শাল করার সময় আপনি কেবল সত্য লেআউটকাইন্ড গ্যারান্টি পাবেন। মার্শাল্ড সংস্করণটি অবশ্যই 16 বাইট হিসাবে Marshal.SizeOf()আপনাকে জানাবে।

LayoutKind.Explicitএটি ঠিক করতে ব্যবহার করে, আপনি যা শুনতে চেয়েছিলেন তা নয়।


7
"এটি নির্ধারণের জন্য মাইক্রোসফ্টকে বোঝানো কঠিন হতে চলেছে, এটি দীর্ঘদিন ধরে এইভাবে কাজ করেছে তাই কোনও পরিবর্তন কোনও কিছু ভেঙে ফেলতে চলেছে।" এটি 32 বিট বা মনোতে দৃশ্যত প্রকাশিত হয় না তা (অন্যান্য মতামত অনুসারে) সহায়তা করতে পারে।
এনপিএসএফ 3000

স্ট্রাকলআউটআউট্রিবিউটের ডকুমেন্টেশনটি বেশ আকর্ষণীয়। মূলত, পরিচালিত মেমরিতে স্ট্রাক্ট লেআউটের মাধ্যমে শুধুমাত্র ব্লিটযোগ্য প্রকারগুলি নিয়ন্ত্রণ করা হয়। আকর্ষণীয়, এটি কখনই জানতেন না।
মাইকেল স্টাম

@ সোনার না এটি এটি ঠিক করে না। আপনি অফসেট হতে উভয় ক্ষেত্রে লেআউটটি রেখেছিলেন? যদি তাই হয় তবে এক্স এবং y একই এবং এক পরিবর্তন অন্যকে পরিবর্তন করে। স্পষ্টতই জনের পরে কী তা নয়।
বার্তোসএডামজেজেস্কি

stringআর একটি নতুন রেফারেন্স টাইপের ( class) প্রতিস্থাপন করা হয়েছে যার প্রতি একজন প্রয়োগ করেছে [StructLayout(LayoutKind.Sequential)]তাতে কিছু পরিবর্তন হবে না বলে মনে হয়। বিপরীত দিকে, প্রয়োগ [StructLayout(LayoutKind.Auto)]করতে struct Int32Wrapperমেমরি ব্যবহার পরিবর্তন TwoInt32Wrappers
জেপ্প স্টিগ নীলসেন

1
"এটি নির্ধারণের জন্য মাইক্রোসফ্টকে বোঝানো কঠিন হতে চলেছে, এটি দীর্ঘদিন ধরে এইভাবে কাজ করেছে তাই কোনও পরিবর্তন কোনও কিছু ভেঙে ফেলতে চলেছে।" xkcd.com/1172
আইকোডসোমটাইম

19

EDIT2

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

এই কোডটি 8 বাইট সারিবদ্ধ হবে সুতরাং স্ট্রাক্টটিতে 16 বাইট থাকবে। এটি তুলনা করে:

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

4 বাইট সারিবদ্ধ হবে তাই এই স্ট্রাক্টটিতে 16 বাইট থাকবে। সুতরাং এখানে যুক্তিটি হ'ল সিএলআর-তে স্ট্রাক্ট অ্যালিগমেন্ট সর্বাধিক প্রান্তিকৃত ক্ষেত্রগুলির সংখ্যা দ্বারা নির্ধারিত হয়, ক্লজগুলি স্পষ্টতই এটি করতে পারে না তাই তারা আট বাইট প্রান্তিকিত থাকবে।

এখন আমরা যদি সমস্ত কিছু একত্রিত করে কাঠামো তৈরি করি:

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

এটিতে 24 বাইট থাকবে {x, y} এর 4 টি বাইট থাকবে এবং {z, s 8 এর 8 টি বাইট থাকবে। একবার আমরা স্ট্রাক্ট সিএলআরএলে একটি রেফ টাইপ প্রবর্তন করি বরাবরই শ্রেণিবদ্ধকরণের সাথে মেলে আমাদের কাস্টম কাঠামোটিকে সারিবদ্ধ করে তুলবে।

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

এই কোডটিতে 24 বাইট থাকবে যেহেতু ইন্টার 32 র্যাপারটি দীর্ঘ হিসাবে একইভাবে সারিবদ্ধ থাকবে। সুতরাং কাস্টম স্ট্রাকের মোড়কটি সর্বদা কাঠামোর সর্বোচ্চ / সেরা সারিবদ্ধ ক্ষেত্র বা তার নিজস্ব অভ্যন্তরীণ সর্বাধিক উল্লেখযোগ্য ক্ষেত্রগুলিতে সারিবদ্ধ থাকে will সুতরাং একটি রেফ স্ট্রিংয়ের ক্ষেত্রে 8 বাইট সারিবদ্ধভাবে স্ট্রাকের মোড়কটি এতে প্রান্তিক হবে।

কাঠামোর অভ্যন্তরে কাস্টম স্ট্রাক্ট ক্ষেত্র সমাপ্তি সর্বদা কাঠামোর সর্বোচ্চ সারিবদ্ধ উদাহরণ ক্ষেত্রের সাথে সংযুক্ত করা হবে। এখন যদি আমি নিশ্চিত না যে এটি একটি বাগ কিনা তবে কিছু প্রমাণ ছাড়াই আমি আমার মতামত ধরে যাচ্ছি যে এটি সচেতন সিদ্ধান্ত হতে পারে।


সম্পাদনা

মাপগুলি কেবল তখনই সঠিক হয় যখন একটি গাদাতে বরাদ্দ করা হয় তবে স্ট্রাক্টগুলিতে নিজের ছোট আকার থাকে (এর ক্ষেত্রগুলির সঠিক আকার)। আরও বিশ্লেষণের শিখার পরামর্শ দেওয়ার জন্য যে এটি সিএলআর কোডে একটি বাগ হতে পারে, তবে তার প্রমাণ দিয়ে ব্যাক আপ নেওয়া দরকার।

আমি ক্লাইপ কোডটি পরীক্ষা করব এবং দরকারী কিছু পাওয়া গেলে আরও আপডেটগুলি পোস্ট করব।


এটি .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();
}

এক্স কোড 64 এর অধীনে .N40 40 সহ এই কোডটি সংকলিত, উইনডিবিজিতে নিম্নলিখিতগুলি করতে দেয়:

প্রথমে হিপগুলিতে ধরণটি আবিষ্কার করুন:

    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

আমরা দেখতে পাচ্ছি যে এটি একটি মান টাইপ এবং এটি আমরা তৈরি করেছি। যেহেতু এটি একটি অ্যারে তাই আমাদের অ্যারেতে একটি একক উপাদানের ভ্যালুটাইপ ডিফ পেতে হবে:

    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 বাইট আকারের হয়।

যদি আপনি কালি থেকে 16 বাইট যোগ করেন এবং একটি স্ট্রিং রেফার করে: 0000000003e72d18 + 8 বাইট ইই / প্যাডিং আপনি 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

প্রকৃতপক্ষে এটি সঠিক কারণ এটির কোনও গঠন বা ঠিকানা না থাকলে ঠিকানাটি আমাদের কাছে কিছুই বলে না if

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 মাপের আকারের।

সুতরাং আপনি যদি মেমরির সাথে আরও রক্ষণশীল হতে চান তবে আপনার এটিকে কখনও স্ট্রাক্ট কাস্টম স্ট্রাক টাইপে প্যাক করা উচিত নয় বরং এর পরিবর্তে সহজ অ্যারে ব্যবহার করা উচিত। আর একটি উপায় হিপ অফ অফ মেমরি (উদাহরণস্বরূপ ভার্চুয়ালআলোকেক্স) এইভাবে আপনাকে নিজের মেমরি ব্লক দেওয়া হয় এবং আপনি এটি যেভাবে চান সেটি পরিচালনা করেন।

এখানে চূড়ান্ত প্রশ্ন হঠাৎ কেন আমরা এরকম লেআউট পেতে পারি। আচ্ছা আপনি যদি কাউন্টার ফিল্ড ইনক্রিমেন্টেশনের সাথে ইন্ট্রি [] ইনক্রিমেন্টেশন এর জেট কোড এবং পারফরম্যান্সকে কাউন্টার ফিল্ড ইনক্রিমেন্টেশনের সাথে তুলনা করেন তবে দ্বিতীয়টি ইউনিয়ন হওয়ার সাথে সাথে একটি 8 বাইট প্রান্তিক ঠিকানা তৈরি করবে, তবে জিট করলে এটি আরও অনুকূলিত সংসদীয় কোডে (সিজ) অনুবাদ করে এলইএ বনাম একাধিক এমওভি)। তবে এখানে বর্ণিত ক্ষেত্রে কার্যক্ষমতাটি আরও খারাপ হবে তাই আমার গ্রহণযোগ্যতাটি হ'ল এটি অন্তর্নিহিত সিএলআর বাস্তবায়নের সাথে সামঞ্জস্যপূর্ণ কারণ এটি একটি কাস্টম ধরণের যেখানে একাধিক ক্ষেত্র থাকতে পারে তাই এটির পরিবর্তে প্রারম্ভিক ঠিকানাটি রাখা আরও সহজ / ভাল হতে পারে মান (যেহেতু এটি অসম্ভব হবে) এবং সেখানে স্ট্রাক্ট প্যাডিং করুন, ফলে বড় বাইট আকারের ফলস্বরূপ।


1
নিজেকে এই সময়ে খুঁজছি, আকার RefAndTwoInt32Wrappers নয় 32 বাইট - এটা 24, যা একই হিসাবে আমার কোড রিপোর্ট হয়। আপনি যদি ব্যবহারের পরিবর্তে মেমরি ভিউটিতে dumparrayসন্ধান করেন এবং স্বতন্ত্র মানগুলির সাথে (বলুন) 3 টি উপাদান সহ একটি অ্যারের জন্য মেমরিটি দেখেন, আপনি পরিষ্কারভাবে দেখতে পারবেন যে প্রতিটি উপাদান একটি 8-বাইট স্ট্রিং রেফারেন্স এবং দুটি 8-বাইট পূর্ণসংখ্যা নিয়ে গঠিত । আমার সন্দেহ হয় যে dumparrayমানগুলি কেবল রেফারেন্স হিসাবে দেখায় কারণ এটি কীভাবে Int32Wrapperমানগুলি প্রদর্শন করতে পারে তা জানে না । সেই "রেফারেন্স" নিজের দিকে ইঙ্গিত করে; তারা পৃথক মান হয় না।
জন স্কিটি

1
আপনি কোথা থেকে "16 বাইট প্যাডিং" পেয়েছেন তা সম্পর্কে আমি নিশ্চিত নই, তবে আমার সন্দেহ হয় যে এটি অ্যারে অবজেক্টের আকারের দিকে তাকিয়ে আছেন যা "16 বাইট + গণনা * উপাদান আকার" হবে। সুতরাং 2 টি গণনা সহ একটি অ্যারের আকার 72 (16 + 2 * 24) রয়েছে যা এটি dumparrayদেখায়।
জন স্কিটি

@ জোন আপনি কি আপনার স্ট্রাকটি ডাম্পেপ করেছেন এবং এটি পরীক্ষা করেছেন যে এটি স্তূপে কতটি স্থান দখল করে? সাধারণত অ্যারের শুরুতে অ্যারের আকার রাখা হয়, এটিও যাচাই করা যেতে পারে।
বার্তোসএডামজেজেস্কি

@ জোন রিপোর্ট করা আকারে স্ট্রিংয়ের অফসেট রয়েছে যা 8 থেকে শুরু হয় I আমি মনে করি না যে উল্লিখিত অতিরিক্ত 8 টি বাইট অ্যারে থেকে এসেছে কারণ বেশিরভাগ অ্যারে স্টাফগুলি প্রথম উপাদান ঠিকানার আগে থাকে তবে আমি ডাবল চেক করব এবং যে মন্তব্য।
বার্তোসএডামজেজেস্কি

1
না, থ্রিআইন্ট 32আরেপ্পারগুলি 12 বাইট হিসাবে শেষ হয়, 16 হিসাবে ফোরআইন্ট 32 র্যাপার্স, 20 হিসাবে ফাইভ ইন্ট 32 র্যাপারগুলি the লেআউটটি এত তাড়াতাড়ি পরিবর্তন করে কোনও রেফারেন্স টাইপ ফিল্ড যুক্ত করার বিষয়ে আমি যৌক্তিক কিছু দেখতে পাচ্ছি না। এবং মনে রাখবেন যে ক্ষেত্রগুলি টাইপ করার সময় 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

অবজেক্ট রেফারেন্স যুক্ত হওয়ার সাথে সাথে সমস্ত স্ট্রাকটি তাদের 4 বাইট আকারের পরিবর্তে 8 বাইটে প্রসারিত হবে। পরীক্ষার সম্প্রসারণ:

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

আপনি দেখতে পাবেন যেহেতু রেফারেন্স যুক্ত হওয়ার সাথে সাথে প্রতিটি ইন্ট 32 র্যাপার 8 বাইট হয়ে যায় তাই সাধারণ প্রান্তিককরণ নয়। আমি অ্যারে বরাদ্দকে সংকুচিত করেছিলাম যদি এটি এলওএইচ বরাদ্দ যা আলাদাভাবে সংযুক্ত থাকে।


4

কেবল মিশ্রণটিতে কিছু ডেটা যুক্ত করতে - আপনার যেগুলি রয়েছে তার থেকে আমি আরও একটি টাইপ তৈরি করেছি:

struct RefAndTwoInt32Wrappers2
{
    string text;
    TwoInt32Wrappers z;
}

প্রোগ্রামটি লিখেছেন:

RefAndTwoInt32Wrappers2: 16

সুতরাং দেখে মনে হচ্ছে TwoInt32Wrappersস্ট্রাক্টটি নতুন RefAndTwoInt32Wrappers2কাঠামোর মধ্যে সঠিকভাবে প্রান্তিক হয়েছে ।


আপনি কি 64 বিট চালাচ্ছেন? প্রান্তিককরণটি 32 বিটে ঠিক আছে
বেন অ্যাডামস

আমার অনুসন্ধানগুলি বিভিন্ন পরিবেশের জন্য সবার মতো একই।
জেসি সি স্লিকার 21
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.