1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C#のメモリアレンジメント

Posted at

C++のとき構造体のデータのアレンジメントをよく注意しなればないですが、C#はほとんどは自動管理でやります。でも本当に何もしなくていいですか?これについて確認します。

最初は簡単な構造体を作ります。

struct MyStruct
{
	public byte a;	//1byte
	public int b;   //4byte
	public short c; //2byte
	public byte d;  //1byte
}

Marshal.SizeOfを利用してメモリサイズを確認したら12 bytesを返します。つまりC#は自動的に4 byteのブロックを分けています。
現在のレイアウトは下のようにアレンジメントしています。こうする見ると、この中にはすでに4 bytesの無駄スペースを発生しました。
[byte][..][..][..]
[int][int][int][int]
[short][short][byte][..]

今ブロックの長さは一番長いのintのサイズと一致ですから、intをdoubleに変わると24 Bytesになるはず。
早速確認すると、やっぱり24 bytesになりました。

struct MyStruct
{
	public byte a;	//1byte
	public double b;   //8byte
	public short c; //2byte
	public byte d;  //1byte
}

メモリレイアウトはこうになりました。無駄なメモリも12 bytesになりました。50%のスペースが使わないからなんかもったいないです。では、手動的にデータの順序でレイアウトを変わってみます。
[byte][..][..][..][..][..][..][..]
[double][double][double][double][double][double][double][double]
[short][short][byte][..][..][..][..][..]

はじめてのメモリレイアウトを見ると、変数a、変数cと変数dのサイズの和は4 bytesです。一緒に並びましょう。

struct MyStruct
{
	public byte a;	//1byte
	public byte d;  //1byte
	public short c; //2byte
	public int b;   //4byte
}

今回SizeOfで確認すると8 bytesを返します。レイアウトをコンパクトしました。無駄にさようなら!
[byte][byte][short][short]
[int][int][int][int]

そして2つのレイアウトは効率にも差があります。1000000の構造体を生成するテストプログラムを作ります。

		public struct MyStruct
		{
			public byte a;	//1byte
			public byte d;  //1byte
			public short c; //2byte
			public int b;   //8byte

			public void modify(byte a)
			{
				this.a = a;
			}
		}
		


		public class Struct2
		{
			public byte a;	//1byte
			public int b; // 4byte
			public short c; //2byte
			public byte d; //1byte
		}

		private const int Num = 1000000;
		public static void Main(string[] args)
		{
			Stopwatch sw = new Stopwatch();
			sw.Start();
			List<MyStruct> list = new List<MyStruct>();
			for (int i = 0; i < Num; i++)
			{
				list.Add(new MyStruct());
			}
			
			sw.Stop();
			Console.WriteLine("Struct1 Elapsed: " + sw.Elapsed);
			GC.Collect();
			Thread.Sleep(1000);
			sw.Restart();
			var list2 = new List<Struct2>();
			for (int i = 0; i < Num; i++)
			{
				list2.Add(new Struct2());
			}
			sw.Stop();
			Console.WriteLine("Struct2 Elapsed: " + sw.Elapsed);
		}

リザルト

Struct1 Elapsed: 00:00:00.0208816
Struct2 Elapsed: 00:00:00.0616822

構造体だけではなく、クラスにも同じ原理です。日常のプログラミングには特にメモリレイアウトを書きながら変数を移動しなくてもいいです。ただ、同じタイプの変数を一緒に並ぶと無駄を結構減っています。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?