SORU
14 Temmuz 2014, PAZARTESİ


Neden yapı hizalama alan bir tür ilkel veya kullanıcı tanımlı olup olmadığını bağlıdır?

Noda Time v2, çözünürlük nanosaniye için hareket ediyoruz. Artık 8 baytlık bir tamsayı ilgilendiğimiz zaman aralığını temsil etmek için kullanabiliriz demektir. O beni ele beni CLR uyumu karar hafif bir gariplik ortaya çıkarmak için açmıştır Noda Zaman (çok) yapılar, bellek kullanımını araştırmak üzere harekete geçirdi.

Öncelikle, bunun farkındayımvarsayılan davranış, herhangi bir zamanda değiştirebilecek bir uygulama kararı almış. Bunu ben de fark ettimolabilir[StructLayout] [FieldOffset] kullanarak değiştirmek daha ziyade mümkünse o durmamış olan bir çözüm buldum.

Benim temel senaryo bu alanlar basit ambalaj kağıtları nerede başvuru tipi alanı ve değer türü Diğer iki alanları içerir struct int için var. Vardıumut16 bayt olarak temsil 64-bit CLR (başvuru ve diğerleri her biri için 4 8), ama nedense 24 byte kullanıyor. Uzay dizileri kullanarak ölçüyorum, bu arada düzen farklı durumlarda farklı olabilir anlıyorum, ama bu mantıklı bir başlangıç noktası gibi hissettim.

İşte örnek bir program sorunu gösteren:

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);
    }
}

Ve benim laptop derleme ve çıktı:

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

Yani:

  • Başvuru türü alanı yok ise, CLR Int32Wrapper alanlar birlikte pack mutlu (TwoInt32Wrappers 8 büyüklüğünde)
  • Başvuru türü alanı bile, CLR hala int alanlar birlikte pack mutlu (RefAndTwoInt32s 16 büyüklüğünde)
  • İkisini birleştiren Int32Wrapper Her alan/8 bayt için sıfır hizada görünüyor. (RefAndTwoInt32Wrappers boyutu vardır 24.)
  • Hata ayıklayıcı, aynı kod (ama hala bir yayın oluşturma) çalışan 12 Boyutunu gösterir.

Birkaç Deney benzer sonuçlar vermiştir

  • Değer tipi alanları sonra başvuru tipi alanı koyarak bir faydası yok
  • string yerine object kullanarak yardımcı olmuyor (bekliyorum "herhangi bir başvuru türü")
  • Kullanarak bir "referans yok ortalıkta" yardım . kapsayıcı bir yapı
  • Başvuru etrafında sarıcı olarak genel bir yapı kullanarak yardımcı olmuyor
  • Eğer alanları (sadelik için çiftler halinde), int eklemeye devam edersem alanlar hala 4 bayt sayısı için Int32Wrapper 8 bayt sayısını alanları
  • Görünürde her yapı için [StructLayout(LayoutKind.Sequential, Pack = 4)] ekleme sonuçları değiştirmez

Alanları dolu olmak istiyorum herkes bu (ideal olarak başvuru belgeleri ile birlikte) için herhangi bir açıklama veya CLR ipucu nasıl alabilirim bir öneriniz var mıolmadansabit bir alan belirleme ofset?

CEVAP
14 Temmuz 2014, PAZARTESİ


Bu bir hata olduğunu düşünüyorum. 64-bit modunda 8 bayt katları olan bir adres için önemsiz olmayan alanları hizalamak için seviyor yan etkisi otomatik düzen, görüyorsun. Açıkça [StructLayout(LayoutKind.Sequential)] öznitelik uygulamak bile oluşur. İşte bunun olmaması lazım.

Yapı üyeleri ortak yapımı ve bu gibi test kodu ekleyerek de görebilirsiniz:

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

Kesme isabetlerinin sayısı, hata Ayıklama Windows Bellek 1 kullanın. 4-byte tamsayılar ve anahtarı koymak &test Adres alanı:

 0x000000E928B5DE98  0ed750e0 000000e9 11111111 00000000 22222222 00000000 

0xe90ed750e0 makinem (senin değil) dize işaretçisi. Kolayca görebilirsiniz 24 bayt boyutuna döndü dolgu fazladan 4 bayt ile Int32Wrappers,. Yapı dönün ve son dize koyun. Tekrar ve dize işaretçisi olduğunu göreceksinizhalailk. LayoutKind.Sequential, ihlal var LayoutKind.Auto.

Zor Microsoft bunu düzeltmek için ikna etmek için olacak, herhangi bir değişiklik kopma olacak bu yüzden çok uzun süre bu şekilde çalıştıbir şey. CLR tek bir yapı yönetilen sürümü için [StructLayout] onur ve blittable yapmak için bir girişim yapar, genel olarak çabuk vazgeçmez. Herkesin bildiği gibi, herhangi bir yapı için bir DateTime içerir. Sadece bir yapı, sıralama yaparken doğru LayoutKind garanti olsun. Sıralanmış sürümü kesinlikle Marshal.SizeOf() size söyleyecektir olarak 16 bayt.

LayoutKind.Explicit kullanarak duymak istediğin bu, değil giderir.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Ben Schoon

    Ben Schoon

    23 Kasım 2012
  • chickenby

    chickenby

    2 HAZİRAN 2008
  • Mark Hyder

    Mark Hyder

    6 EKİM 2011