In Noda Zaman v2, biz nanosaniye çözünürlüğü taşınıyoruz. Bu, ilgilendiğimiz tüm zaman aralığını temsil etmek için artık 8 baytlık bir tamsayı kullanamayacağımız anlamına geliyor. Bu, beni Noda Time'ın (birçok) yapısının bellek kullanımını araştırmaya sevk etti ve bu da beni yönlendirdi. CLR'nin uyum kararındaki küçük bir tuhaflığı ortaya çıkarmak için.
İlk olarak, bunun bir uygulama kararı olduğunu ve varsayılan davranışın herhangi bir zamanda değişebileceğini anlıyorum . Ben fark edebilirsiniz kullanarak bu maddede değişiklikler [StructLayout]
ve [FieldOffset]
, ama yerine mümkünse o gerektirmeyen bir çözüm gelirdi.
Benim temel senaryom, struct
bir başvuru türü alanı ve bu alanların basit sarmalayıcılar olduğu diğer iki değer türü alanını içeren bir alanımın olmasıdır int
. Bunun 64-bit CLR'de 16 bayt olarak gösterileceğini ummuştum (referans için 8 ve diğerlerinin her biri için 4), ancak nedense 24 bayt kullanıyor. Bu arada, dizileri kullanarak alanı ölçüyorum - farklı durumlarda düzenin farklı olabileceğini anlıyorum, ancak bu mantıklı bir başlangıç noktası gibi geldi.
İşte sorunu gösteren örnek bir program:
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 dizüstü bilgisayarımdaki 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:
- Bir referans türü alanınız yoksa, CLR
Int32Wrapper
alanları bir arada paketlemekten memnuniyet duyar (TwoInt32Wrappers
boyutu 8'dir) - Bir referans türü alanı olsa bile, CLR
int
alanları birlikte paketlemekten mutludur (RefAndTwoInt32s
boyutu 16'dır) - İkisini birleştirdiğinde, her
Int32Wrapper
alan 8 bayta doldurulmuş / hizalanmış gibi görünür. (RefAndTwoInt32Wrappers
24 bedene sahiptir.) - Hata ayıklayıcıda aynı kodu çalıştırmak (ancak yine de bir sürüm derlemesi) 12 boyutunu gösterir.
Diğer birkaç deney de benzer sonuçlar verdi:
- Referans türü alanını değer türü alanlarının sonrasına koymak yardımcı olmuyor
object
Bunun yerine kullanmakstring
yardımcı olmuyor ("herhangi bir referans türü" olduğunu umuyorum)- Referans etrafında "sarmalayıcı" olarak başka bir yapıyı kullanmak yardımcı olmaz
- Referans etrafında bir sarmalayıcı olarak genel bir yapı kullanmak yardımcı olmaz
- Alan eklemeye devam edersem (basitlik için çiftler halinde)
int
alanlar hala 4 bayt,Int32Wrapper
alanlar 8 bayt olarak sayılır [StructLayout(LayoutKind.Sequential, Pack = 4)]
Görünürdeki her yapıya eklemek sonuçları değiştirmez
Herkes paketlenecek olan (ideal referans belgelerle) Bunun için herhangi bir açıklama veya ben alanlarını istediğinizi CLR'ye ipucu elde nasıl bir öneri var mı olmadan ofset sabit alanını belirterek?
TwoInt32Wrappers
veya bir Int64
ve bir ile bir yapı oluşturursanız ne olur TwoInt32Wrappers
? Bir jenerik Pair<T1,T2> {public T1 f1; public T2 f2;}
oluşturup ardından Pair<string,Pair<int,int>>
ve oluştursanız nasıl olur Pair<string,Pair<Int32Wrapper,Int32Wrapper>>
? Hangi kombinasyonlar JITter'i bir şeyleri doldurmaya zorlar?
Pair<string, TwoInt32Wrappers>
yok sadece 16 bayt vermek konusu ele alınacak, böylece. Büyüleyici.
Marshal.SizeOf
.NET kodundaki yapının boyutuyla herhangi bir ilişkisi olması gerekmeyen yerel koda geçirilecek yapının boyutunu döndürür.
Ref<T>
kullanıyorsunuz,string
bunun bir fark yaratması gerektiği anlamına gelmiyor.