2012-09-06 6 views
8

私はパフォーマンスの観点からどちらのソリューションが望ましいかを理解しようとしています。 たとえば、私は2つのコードがあります.NETボクシング/アンボクシングとキャスティングパフォーマンス

1)ボクシング/アンボクシング

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

2)は何

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

(IntObjがストアintにint型のValueプロパティを持っている)キャスティングこれらの例のメモリ管理の違いは? このような操作をより速く実行する方法はありますか?

注:Sessionはちょうど例えば、それはどのDictionary<string, object>

+2

私はそれを測定していませんが、プリミティブが速いと言います。オブジェクトにオーバーヘッドがあります。しかし、この場合、私は可読性がはるかに重要な手段だと思います。 Cpuのサイクルは脳のサイクルよりもずっと安価です –

答えて

7

あなたが実際にここでやっているように見えるのは、手動ボクシングとインバイトボクシングの比較です。 inbuiltボクシングは高度に最適化されています - だから私はここで大きな違いを見込むとは思っていないが、我々は確認することができます。重要なことに、両方とも同じメモリへの影響があることに注意してください。intフィールドを含む1つのヒープオブジェクト、intの囲み/折り返し。

次の例は、かなり近似しています。私は、したがって、それを直接/組み込みの方法で囲むと言うでしょう。

注:デバッガなしで(理想的にはコマンドラインで)リリースモードで実行してください。最初の呼び出しはJITをすべてプレ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); 
    } 


} 
+0

ありがとう!今それを見るのは簡単です:) –

4

できるようにIntObjまたは内蔵ボクシングとDIYボクシング、より高速なものですか?

私の推測では、組み込みのバラエティです。両方のコンパイラがそれに対処するために最適化されている可能性があります。

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

大規模なデータセットでは、常に回避することをお勧めします。小さなセットの場合、それは単に重要ではありません。

+0

私はセッションを与えました。それは単純なDictionary / –

+0

のようなものでした。コンテナ(セッション)はそれほど関連性がありません。 –

0

私はC#のキャスト演算子によって生成されたIL命令の種類を分類:、C++でのdynamic_castのように(inhertiance階層をキャスティング

ボクシング(ボックスIL命令)とアンボクシング(UnboxのIL命令) castclass IL命令を使用しています プリミティブ型間のキャスト(C++のstatic_castのように、プリミティブ型間のさまざまな型のキャストに対してたくさんのIL命令があります) IL定義レベルでは、適切なop_XXXメソッド)。

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

+3

それでは、その質問にどう答えますか? (ボクシングもヒープ上に新しいインスタンスを作成します) –

2

何かをする最速の方法は全くありません。大量のボクシングを避けるためにデータを再構成して、型の安全性、可読性、潜在的なパフォーマンスを同時に向上させてください。

大量の無関係な整数(または他の値型)要素を型なしの辞書に格納する必要はないでしょう。通常、値はいくつかのオブジェクトにまとめられています。この場合、型なしの辞書に最上位のオブジェクトを格納し、1つのキャストのみが必要です。より深い要素については、ボクシングが必要ないので、この問題が既に解決されている強く型付けされたクラス(Dictionary<string,int>など)を使用します。

あなたが実際にstring => opbjectマップに多数のint(または他の値型要素)を格納する必要があると感じる場合は、データセットと目標を使用して測定を非常に簡単に行う必要がありますいずれかのバージョンに大きなメリットがあるかどうかを確認してください。両方があなたの目標を満たしているなら(最も好きな) - 最も読みやすいコードを作るものを選んでください(私にとっては、それは最初の変種です)。