0~255と無効値を保存したい §
0~255と無効値、つまり257種類の値を保存するにはどうすれば良いでしょうか。
第1のアイデアはbyte?の利用です。この型なら0~255とnull値を表現できます。無駄がありません。
第2のアイデアは、byte型は256種類の値しか識別できない以上、もっと幅の大きいshort型の利用です。-1が無効値と決めておけば問題ありませんが、65536種類の値を識別できて無駄が大きいと言えます。
さて、どちらのメモリ効率が良いでしょう?
実はshortが勝つ §
以下のサンプルソースは1000000個の配列を作って、その前後のメモリ使用量の大きさを比較しています。
実行結果を見ると分かる通り、byte?とshortの使用量は同じです。
なぜ無駄がないように見えたbyte?は勝てないのか? §
byte型は8bitです。byte?型はそれに加えて値を持っているかどうかを示すbool値を持ちます。bool値は8bitのメモリを占有します。合計16bitです。
一方でshort型は16bitです。
どちらも16bitなので必要メモリ量は同じです。
罠の数々 §
- shortをbyte?に書き換えてもメモリの節約にはならない
- short?はshort 16bit+bool 8bitで24bitで済むように思えるが実際は32bitのintと消費量は同じである。アドレスを整列するためにパディングのバイトが挿入されるからだ。ビギナーは整列の意味を理解しなくても良いが、消費量が狂うことがあることは知っておくと良い
- 同様にint?はint 32bit+bool 8bitで40bitで済むように思えるが実際は64bitのlongと消費量は同じである。
参考リンク §
整数数値型 (C# リファレンス)
null 許容値型 (C# リファレンス)
個々の型は何bitか。それをnull許容型にすると何bit増えるかを確認しておこう。これを知っておくとメモリ効率の高いプログラムを書けるようになり、メモリ効率が良いと実行速度も速くなる可能性がある。(メモリ効率が良いとCPUのキャッシュに入りやすくなるので)
リポジトリ §
https://github.com/autumn009/cshowto
NullableByteVSShort §
using System;
class Program
{
private static T[] sub<T>(string label)
{
long start = GC.GetTotalMemory(true);
T[] array = new T[1000000];
Console.WriteLine($"{label} {GC.GetTotalMemory(true) - start}");
return array;
}
static void Main()
{
sub<byte?>("byte?");
sub<short>("short");
sub<short?>("short?");
sub<int>("int");
sub<int?>("int?");
sub<long>("long");
}
}
実行結果
byte? 2000024
short 2000024
short? 4000024
int 4000024
int? 8000024
long 8000024