2009-08-17 7 views
1

id2key_valueというDictionary<string, Dictionary<string, string>>にハッシュ(ID)でマッピングされたキーと値のペアがあります。これを、データベースのような表を行で表現する方法と考えることができます。キャスティングの費用は?

public int GetInt(string id, string key) 
{ 
    int value = 0; 
    bool success = int.TryParse(map[id][key], out value); 

    if (success) 
     return value; 
    else 
     throw new InvalidOperationException(
      "Trying to obtain a non-integer value with GetInt()."); 
} 

は、まあ、私は「cast-のアイデアを思い付いたとき、私は賢いされていたと思ったように私は、キャストを実行して、いくつかの基本的なデータ型の使用を容易にするために、いくつかのヘルパー関数を追加しましたキャッシュ "、これは基本的に既に解析されたオブジェクトを保持しているので、int、bool、DateTimeなどの文字列の解析をスキップして、キャッシュから適切なデータ型にキャストするだけです。同様に、

public int GetInt(string id, string key) 
{ 
    if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key)) 
     return (int) cast_cache[id][key]; 

    int value = 0; 
    bool success = int.TryParse(map[id][key], out value); 

    if (success) 
    { 
     this.AddToCache(id, key, value); 

     return value; 
    } 
    else 
     throw new InvalidOperationException(
      "Trying to obtain a non-integer value with GetInt()."); 
} 

「キャストキャッシュ」は、単にDictionary<string, Dictionary<string, object>>です。

私はマップに10000の整数を追加してパフォーマンステストを行いました。それから私は "キャストキャッシング"の有無にかかわらず、100万回のランダム検索を行いました。

キャッシングなしで495(ms)、キャッシングで490(ms)かかりました。私はDateTimeを使ってテストを行いました。違いはより重要でしたが、私が期待していたよりも少ない(約750(ms)キャッシュされないキャッシュと約500(ms)キャッシュされた)。

キャストの原則を理解していない、この操作がどれほどコストがかかり、パフォーマンスが文字列から「逆シリアル化」されたものに近いのはなぜですか?

+0

あなたはジェネリックを使用することはできませんどのようになる参照してください? – Dykam

+0

@Dykam、 どうすればよいですか? id2key_valueには、実装されたプロバイダ(ファイル、データベースなど)に永続的に格納できる文字列として逆シリアル化された任意の型を含めることができます。ジェネリック医薬品を使用するビジョンはどこですか? –

+1

構文解析がキャストしていません... –

答えて

11

キャスティングは、オブジェクト自体に触れていないため(ほとんどの場合、そのオブジェクトを指している参照を変更しているため)、大部分の人が思考しているように高速です。

キャストを避けるべき主な理由の1つは、キャストするときに型安全性を避けてアプリケーションに潜在的な実行時エラーを導入することです。私はめったにパフォーマンスの問題であると考えていません。

補足として、キャッシュ内の参照型と値型の両方をテストして、値の種類のボクシングとアンボックス化のためにパフォーマンス上のペナルティが発生していないことを確認します。

ボクシングのパフォーマンスの低下は、値の型をオブジェクトにキャストすると、値の型がヒープにコピーされる必要があるため、に変更されます。また、ボックス化された値型を使用すると、参照型がunboxされ、その後、これらの値がヒープからスタックに再度コピーされます。

+1

)この場合、実際には他のものよりもアンボックスされています...しかし、私は一般的に同意します。 –

+1

ニース - あなたがコメントした直前に編集しました:) –

+0

質問の問題は、ボクシング/アンボクシングではなく、その文字列からデータを再解析することです。ボクシング/アンボクシングはstring.TryParse()と比較して非常に高速です。 –

-2

あなたの例は、時間と空間の関係のためにより速く動作します。すべてのハッシュタイプをキャッシュし続ける場合、プログラムにどのくらいのメモリが必要ですか?

2

あなたのコードで「キャスト」と呼んでいるものは、キャストされていません。

あなたは、このやっている場合:

bool success = int.TryParse(map[id][key], out value); 

変換、ないキャストです。キャストは次のようになります。

value = (int) map[id][key]; 

またはオブジェクトが実際に文字列ではなく、int型であるため、この場合には失敗していました

value = map[id][key] as int; 

Dictionary<string, string>がありますが、実際には任意のオブジェクトを格納することに興味がある場合は、Dictionary<string, object>とします。結果として、変換の代わりにキャストを使用できるようになります。Andrew Hare氏が指摘するように、より高速になると指摘しています。

+0

"まあ、私はキャストキャッシュのアイデアを思いついたときに賢いと思ったが、これは基本的にはすでに解析済みのオブジェクトを保持していたので、int、bool、DateTimeなどの文字列の解析をスキップできる。キャッシュから適切なデータ型にキャストするだけです」。 私は明らかに、キャッシュの不幸な名前を理解しています。この解決策は、オブジェクトの直列化をToString()またはリフレクションに制限しますが、これは適切ではありません。これには、オブジェクトの種類も記憶域に格納する必要があり、より多くのスペースと、避けようとしているものが必要です。 –

0

辞書検索の回数を減らした場合、コードがキャッシュで高速に実行される場合があります。さらに、Dictionary<string, object>の代わりにキャッシュDictionary<string, int>を作成すると、ボクシングとアンボックスも避けられますが、これもコストがかかります。

public int GetInt(string id, string key) 
{ 
    int value; 
    Dictionary<string, int> cache; 

    if (cast_cache.TryGetValue(id, out cache) 
      && cache.TryGetValue(key, out value)) 
    { 
     return value; 
    } 

    if (int.TryParse(map[id][key], out value)) 
    { 
     this.AddToCache(id, key, value); 
     return value; 
    } 

    throw new InvalidOperationException("Trying to obtain a non-integer value with GetInt()."); 
} 
+0

私はあらゆる種類の辞書に辞書を使うことができますが、これは私がここでやっていることの目的を破っています。 'Dictionary <文字列、オブジェクト> 'に置き換えて、我々は50%のスピードアップを得るので、ありがとう!私はHounShellの提案がさらに高速化すると信じていますので、答えは彼が確認したら彼に行くでしょう。 –

0

そこにはいくつか考慮すべき事項があります。まず、正しい用語を知っているだけで、これは実際にはアンボクシングです(値型を参照型として格納したか、またはボックス化しています)。値型に戻すことはアンボックス化です。

第二に、私の代わりにそれがキャッシュ辞書に複数の呼び出しにあります、あなたのコードの大半はアンボクシングされていないことを賭けたい:

if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key))    
    return (int)cast_cache[id][key] 

私は5辞書はそこに横断回数:cast_cache (id)、cast_cache [id]、.ContainstKey(key)、cast_cache [id]、および[key]を含む。

これはかなり厳しいです。集計されたキーを使用することで、これらの多くを減らすことができます。 [id] [key]を探すのではなく、それらを単一のオブジェクトに結合します。これは、あなたの辞書の数を指数関数的に減らし、ContainsKey()をtry/catch(その速度を調べる)でスキップすると、そのルックアップを2,1に減らします。

ここでは、それらを組み合わせることができるようになるクラスです:

public class Vector 
{ 
    private object[] _Data; 

    public object this[int index] 
    { 
     get 
     { 
      return _Data[index]; 
     } 
    } 

    public Vector(params object[] data) 
    { 
     _Data = (object[])data.Clone(); 
    } 

    public override bool Equals(object obj) 
    { 
     Vector OtherVector = obj as Vector; 

     if (OtherVector == null) 
      return false; 

     if (OtherVector._Data.Length != _Data.Length) 
      return false; 

     for (int I = 0; I < _Data.Length; I++) 
      if (!_Data[I].Equals(OtherVector._Data[I])) 
       return false; 

     return true; 
    } 

    public override int GetHashCode() 
    { 
     int Result = 0; 
     for (int I = 0; I < _Data.Length; I++) 
      Result = Result^(_Data[I].GetHashCode() * I); 

     return Result; 
    } 
} 

はそれを試してみて、あなたの速度は

+0

これは私の直感が私に言っていたものでした。私は試してみて戻ってきます、ありがとう! –

+0

私はこれを使用する方法を理解しようとしています。キャッシュが「辞書」に変更された場合、ベクトルによって生成される衝突はどのように処理されますか? int hash =(新しいベクトル(id、key))。GetHashCode(); –

+0

ディクショナリオブジェクトである必要があります。ディクショナリは、キーのGetHashCode()とEquals()を使用して物事を調べます。 Vectorクラスは、これら2つの呼び出しの代理として動作します。これは、ハッシュコードが比較的ユニークで、ハッシュコードがEqual()(GetHashCode()のオーバーロードの2つの要件)であり、それぞれの構成要素でEquals()を呼び出す2つのクラスで同じであることを確認します。基本的にn値キーのプロキシですが、直接使用することができます – Hounshell

関連する問題