10

パフォーマンスの観点から、2 つのソリューションのうちどちらが優先されるかを理解しようとしています。たとえば、次の 2 つのコードがあります。

1) 箱詰め/箱詰め解除

int val = 5;
Session["key"] = val; 
int val2 = (int)Session["key"];

2) キャスト (IntObj には int を格納する int Value プロパティがあります)

IntObj val = new IntObj(5);  
Session["key"] = val;
int val2 = ((IntObj )Session["key"]).Value;

これらの例のメモリ管理の違いは何ですか? そのような操作を実行するより高速な方法はありますか?

注: Sessionは単なる例であり、任意の値にすることができますDictionary<string, object>

4

4 に答える 4

9

ここで実際に行っていることは、手動のボクシングと組み込みのボクシングを比較しているようです。組み込みのボクシングは高度に最適化されているため、ここで大きな違いが見られるとは思いませんが、確認できます. 重要なのは、どちらもメモリへの影響が同じであることに注意してください。ボックス化/ラップintごとに、1 つのフィールドを含む 1 つのヒープ オブジェクトです。int

以下は、接近した 2 つのほぼ同じ時間を示しています。したがって、直接/組み込みの方法でボックス化するだけです。

注: デバッガーなしで (理想的にはコマンド ラインで) リリース モードで実行します。最初の呼び出しは、すべてを pre-JIT するためにあることに注意してください。

using System;
using System.Diagnostics;
public sealed class IntObj
{
    public readonly int Value;
    public IntObj(int value)
    {
        Value = value;
    }
}
static class Program
{
    static void Main()
    {
        Run(1, 0, false);
        Run(100000, 500, true);
        Console.ReadKey();
    }
    static void Run(int length, int repeat, bool report)
    {
        var data = new object[length];

        int chk = 0;
        var watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = i;
                chk += i;
            }
        }
        watch.Stop();
        if(report) Console.WriteLine("Box: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                chk += (int) data[i];
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Unbox: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = new IntObj(i);
                chk += i;
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Wrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                chk += ((IntObj)data[i]).Value;
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Unwrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
    }


}
于 2012-09-06T07:30:55.937 に答える
4

IntObjでは、組み込みのボクシングを使用した、より高速な DIY ボクシングとは何でしょう?

私の推測では、組み込みの品種です。両方のコンパイラが最適化されている可能性があります。

そのような操作を実行するためのより「高速な」方法はありますか?

推奨されるアプローチは、大規模なデータセットでは常に回避することです。小さなセットの場合、それは問題ではありません。

于 2012-09-06T07:08:14.840 に答える
2

何かをするための最も速い方法は、まったくやらないことです。データの再構築を試みて大量のボックス化を回避し、型の安全性、可読性、潜在的なパフォーマンスを同時に向上させます。

型指定されていない辞書に、​​無関係な整数 (またはその他の値型) 要素を大量に格納する必要があるとは考えにくいと思います。通常、値は一緒に意味をなすいくつかのオブジェクトに編成されます。この場合、最上位のオブジェクトを型指定されていないディクショナリに格納し、必要なキャストは 1 つだけです。より深い要素についてDictionary<string,int>は、ボクシングが必要ないため、この問題がすでに解決されている厳密に型指定されたクラス ( など) を使用します。

あなたのケースで本当に多数の int (または他の値型要素) を string=>opbject マップに格納する必要があると感じた場合は、データセットと目標を使用して自分で測定を実行し、バージョンのいずれかを確認するのは非常に簡単です。大きなメリットがあります。両方があなたの目標を満たしている場合 (可能性が高い) - 最も読みやすいコードを生成するものを選択してください (つまり、私にとっては最初のバリアントになります)。

于 2012-09-06T07:28:50.733 に答える
0

C# キャスト演算子によって生成されるさまざまなタイプの IL 命令を分類しました。

ボクシング (box IL 命令) と unboxing (unbox IL 命令) 継承階層を介したキャスト (C++ の dynamic_cast のように、検証には castclass IL 命令を使用)プリミティブ型間のキャスト) ユーザー定義の変換演算子の呼び出し (IL レベルでは、適切な op_XXX メソッドへのメソッド呼び出しにすぎません)。

違いは、新しい参照型が作成されると、キャストによって追加のメモリが割り当てられることです。

于 2012-09-06T07:15:43.480 に答える