最後に、私はそれを理解したと思います... これが私が見つけた答えです。エラーが発生した場合は、お知らせください。
ランタイムは、ボックス化/ボックス化解除操作ごとにヒープ上のデータを本当にコピーしますか?それとも、参照カウントなどのトリックを使用しますか?
public void Test6()
{
GC.Collect(GC.MaxGeneration);
GC.WaitForFullGCComplete();
object[] myarr = new object[1024 * 1024];
long mem1 = GC.GetTotalMemory(true);
int a = 1;
for (int i = 0; i < myarr.Length; ++i)
{
myarr[i] = a;
}
long mem2 = GC.GetTotalMemory(true);
Console.WriteLine("Total memory usage is {0} bytes", mem2 - mem1);
// Make sure we use it so that the JIT doesn't optimize our code
int sum = 0;
for (int i = 0; i < myarr.Length; ++i)
{
sum += (int)myarr[i];
}
Console.WriteLine("Sum = {0}", sum);
}
この結果は x86 では 12582912 です。これは本格的なオブジェクトの動作です: 4x1M の int、4x1M の型参照、配列に格納された 4x1M のポインター。回答: ヒープにコピーするだけです。
そのため、ランタイムが異なるルール IMO を使用する可能性はほとんどありません。
- ランタイム IL は、コードをインライン化するときにボックス化/ボックス化解除操作を最適化しますか?それとも不可能ですか? 可能であれば、JIT コンパイルを少し「手伝って」もらえますか?
そうではないようです。試す:
private object IntBox1()
{
return 1;
}
private int IntNotBox1()
{
return 1;
}
public int Total1()
{
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += (int)IntBox1();
}
return sum;
}
public int Total2()
{
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += IntNotBox1();
}
return sum;
}
時間的にはかなりの差があるので、そうではありません。ランタイムがボックス化/ボックス化解除を最適化するのを助ける方法が見つかりませんでした。実行時にボックス/ボックス解除操作を最適化する方法を誰かが見つけた場合は、それを共有してください。
- 値型とクラス型の両方がオブジェクトから派生し、ボックス化された値はクラス型であると想定されるため、ボックス化された値型の vtable ルックアップは値型の vtable ルックアップと異なるのでしょうか?
これは事実のようです: 値型の vtable ルックアップははるかに高速です。
public void Test4()
{
int a = 1;
object oa = a;
Stopwatch sw = new Stopwatch();
sw.Start();
int sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += a.GetHashCode();
}
Console.WriteLine("Calc {0} took {1:0.000}s", sum, new TimeSpan(sw.ElapsedTicks).TotalSeconds);
sw = new Stopwatch();
sw.Start();
sum = 0;
for (int i = 0; i < 100000000; ++i)
{
sum += oa.GetHashCode();
}
Console.WriteLine("Calc {0} took {1:0.000}s", sum, new TimeSpan(sw.ElapsedTicks).TotalSeconds);
}
- なぜ「int」なのですか?ボックスではなく値型として実装されていますか?
これには2つの理由が関係していると今では考えています。
- パフォーマンスとメモリ サイズ。intのオーバーヘッド?は 4 バイトですが、ボックス化された値のオーバーヘッドは x86 では 4 バイト、x64 では 8 バイトです。つまりint?オブジェクトとしてコピーする方が高速または同等に高速です。メソッドを介してコピーするときのオーバーヘッドは同じです。また、値型の vtable ルックアップを使用すると、はるかに高速になります。
- 互換性。ボックス化されたオブジェクトのタイプ = ボックス化されていないオブジェクトのタイプ。イント用?古いバージョンのコードとの互換性を損なうことなく、別の型が必要です。intを変更しますか?to オブジェクトには言語サポートが必要であり、これらの型が同じであることに依存する古いバージョンが壊れます。
結論:ボクシングは実際には常に値の型をヒープにコピーし、それは単なる通常のオブジェクトです。唯一奇妙な点は、オブジェクト内の型参照が、ボックス化されていない値の元の (値) 型への型参照であることです。ボックス化された値がヒープ上に存在する「通常のクラス型」オブジェクトではないことを示唆する証拠を見つけることができません。