ボックス化とボックス化解除とそのコスト

基本用法

ボックス化とボックス化解除

C#言語では、すべての型はSystem.Objectクラスから派生しており、継承関係のある型は代入が可能です。

これは、値型である構造体などを派生型であるObject型へ変換可能なことも意味しています。

このように、値型を参照型であるObject型またはインタフェースに変換することをボックス化といい、
ボックス化されているのを値型へ戻すことをボックス化解除またはアンボックス化といいます。
// ボックス化
int n = 10;
object obj = n;
 
// アンボックス化
int m = (int)obj;

ボックス化・アンボックス化のコスト

このようなボックス化やアンボックス化はコストが重いらしい(パフォーマンスが悪い)らしいです。DOSのこのページから引用します。
簡単な代入と比べて、ボックス化およびボックス化解除は負荷の大きいプロセスです。 値型をボックス化するときは、新しいオブジェクトを割り当てて構築する必要があります。 ボックス化ほどではありませんが、ボックス化解除に必要なキャストも大きな負荷がかかります。

なるほど、それではどの程度の影響があるのでしょうか、測ってみます。
ついでにDynamicも合わせて計測します。こんなコードです。


var list = Enumerable.Range(1, 1000000).ToList();
 
var sw = new Stopwatch();
int sum = 0;
sw.Start();
 
foreach (int val in list)
    sum += val;
 
sw.Stop();
var em1 = sw.ElapsedMilliseconds;
sum = 0;
 
sw.Restart();
 
foreach (object val in list) // ここでボックス化
    sum += (int)val;         // ここでボックス化解除
 
sw.Stop();
var em2 = sw.ElapsedMilliseconds;
sum = 0;
 
sw.Restart();
 
foreach (dynamic val in list)
    sum += val;
 
sw.Stop();
var em3 = sw.ElapsedMilliseconds;

Console.WriteLine($"nomal {em1} , box unbox {em2}, dynamic {em3}");
結果は以下のような感じです。

初回計測

normal 3 , box unbox 25 , dynamic 72 (ms)

二回目以降

normal 3 , box unbox 12 , dynamic 15 (ms)


ボックス化無しに対し、ボックス化&ボックス化解除が介在すると初回は8倍ほど、二回目以降で4倍ほどの差でした。

Dynamic型への変換は更に遅く初回24倍、2回目以降は5倍ほど遅い。

なお試行回数を100万回から1万回に減らすと、いずれも0msでした

これを「パフォーマンスに大きな影響がでる」というのかは使用箇所によるでしょう。数千回ぐらいならほぼ計測誤差ぐらいでしょうか。