web-dev-qa-db-ja.com

Dictionary <TKey、TValue>へのより高速な置き換え

System.Collections.Generic.Dictionary<TKey, TValue>の迅速な交換が必要です。私のアプリケーションは本当に高速である必要があります。したがって、交換は以下をサポートする必要があります。

  • ジェネリック
  • 追加
  • Get
  • 含まれています

... 以上です。 LINQなどでのサポートは必要ありません。そしてそれは速いでなければなりません。

次のような単純なコード:

Stopwatch stopWatch = Stopwatch.StartNew();

Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("fieldName", "fieldValue");
dictionary.Add("Title", "fieldVaaaaaaaaaaaaaaaaalue");

Console.WriteLine(stopWatch.Elapsed);

... 00:00:00.0001274を出力します。これは、私のアプリケーションが他の多くのことを実行しているため、alotの時間です。私が使用しなければならず、私に依存していない遅いライブラリ。

より高速なものを実装する方法についてのアイデアはありますか?

ありがとうございました。

21
Alon Gubkin

JITコンパイルが表示されている可能性があります。私の箱には、次のように表示されます。

00:00:00.0000360
00:00:00.0000060

デバッガーではなく、同じプロセス内で2回続けてすばやく実行したとき。 (デバッガーで実行していないことを確認してください。実行していないと、意味のないテストになります。)

さて、いつでも測定するthat小さなことは、一般的に悪い考えです。どれくらいの時間がかかるかをよりよく理解するには、何百万回も繰り返す必要があります。

それが実際にはコードの速度を低下させていると信じる十分な理由がありますか?それとも、すべてを元のタイミングに基づいていますか?

Dictionary<TKey, TValue>よりもはるかに高速なものが見つかるとは思えませんが、それがボトルネックであることに非常に驚いています。

編集:すべてのキーが既存のオブジェクト(配列内の文字列)であるDictionary<TKey, TValue>に100万個の要素を追加し、同じ値を再利用して(無関係であるため)、構築時に100万個の容量を指定することをベンチマークしました。 -そして、私の2歳のラップトップでは約0.15秒かかりました。

アプリの他の場所で「古い遅いライブラリ」を使用していると既に言っていることを考えると、それは本当にボトルネックになる可能性がありますか?これらの他のライブラリの速度が遅いほど、コレクションクラスの改善による影響が少なくなることに注意してください。辞書の変更がアプリケーション全体の時間の1%しか占めていない場合、瞬時辞書を提供できたとしても、アプリの速度は1%しか向上しません。

相変わらず、プロファイラーを入手してください-それはあなたの時間がどこに向かっているのかについてあなたにはるかに良い考えを与えるでしょう。

65
Jon Skeet

Jon Skeet これはおそらくJITコンパイルであるという仮定に同意します。

そうは言っても、ここに他の情報を追加したいと思います。

_Dictionary<T,U>_の使用に関連する速度の問題のほとんどは、辞書の実装とは関係ありません。 _Dictionary<T,U>_は、箱から出して非常に高速です。それを打ち負かすのは難しいでしょう。

辞書インスタンスに関連する速度の問題は、ほとんどの場合、実際にはハッシュコードの実装の問題です。 _Dictionary<MyCustomClass,MyValue>_の使用時に速度の問題が発生した場合は、MyCustomClassで定義したGetHashCode()実装に再度アクセスしてください。カスタム構造体をキーとして使用している場合、これはさらに重要です。

辞書から優れたパフォーマンスを引き出すには、GetHashCode()は次のようになります。

  1. 速い
  2. 競合がほとんど発生しないハッシュコードを提供できます。一意のインスタンスは、可能であれば、一意のハッシュ値を生成する必要があります。

それが正しければ、デフォルトの辞書の実装に非常に満足すると思います。

37
Reed Copsey

そのコードでもDictionaryコンストラクターのタイミングを調整していることを忘れないでください。私はテストを行い、コンストラクターへの呼び出しを測定から外し、10回ループしました。これが私のテストコードです:

for (int i = 0; i < 10; i++)
{
    Dictionary<string, string> test = new Dictionary<string, string>();

    System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();

    test.Add("fieldName", "fieldValue");
    test.Add("Title", "fieldavlkajlkdjflkjalkjslkdjfiajwelkrjelrkjavoijl");

    Console.WriteLine(watch.Elapsed);
}

Console.ReadKey();

結果は次のとおりです。

00:00:00.0000607
00:00:00.0000025
00:00:00.0000015
00:00:00.0000015
00:00:00.0000016
00:00:00.0000017
00:00:00.0000016
00:00:00.0000016
00:00:00.0000016
00:00:00.0000015

それよりどれだけ速くなるかわかりません...

更新

これはジョンスキートの結果も反映しているようです... JIT。

7
Justin Niessner

本当に優れたパフォーマンスが必要な場合は、ジェネリック、動的メモリ割り当てなどの主要なものをあきらめる必要があります。これらの機能はすべて、パフォーマンスをいくらか犠牲にします。

可能な限りContainsの使用を避け、 TryGetValue などを確認します。

5
Cade Roux

最大のパフォーマンスのためのキーとしてINTSを使用する:

グーグルからここに来た人にとって、辞書からパフォーマンスの最後のビットをすべて絞り出したいのであれば、キーとしてIntを使用してください。 Intキーと文字列キーを比較するベンチマークは次のとおりです。 https://jacksondunstan.com/articles/2527

この記事の著者は、そのような必要がある場合は、文字列をintに変換する価値があるとさえ述べています。

また、これと同じ動作がPHPなどの他の言語でも発生することに注意してください。 PHPの連想配列は実際には辞書であり、PHP7でIntを昇順で使用すると、文字列キーよりもはるかに優れたパフォーマンスを発揮します。

3
JamesHoux

辞書にいくつのアイテムを追加する予定ですか?通常、Dictionary/Hashtableが最速ですが、実行している内容によっては、Hashtable(Dictionaryの基礎となる構造)よりも高速なもの(より適している)がある場合があります。使用法に基づいて、SortedListは、ある種のスキップリスト、または自己平衡ツリーまたは試行と組み合わせると、より高速になる可能性があります。特に、単一の値ではなく範囲の値を返したい場合。

ハッシュテーブルは、次の場合に適しています。

  1. テーブルの作成が始まる前に、保存するアイテムの数がわかります。動的なサイズ変更は非常に苦痛になります!
  2. .NETが行う、均等な分散を備えた優れたハッシュアルゴリズムがあります。
  3. .NETが行う、衝突解決のための適切なメカニズムがあります。
  4. あなたは単一の価値を探しています
  5. すべての値が一意になることを保証できます

たとえば、圧縮を行う場合、RBツリーはハッシュテーブルよりも優れています。

出典: http://en.wikipedia.org/wiki/Hashtable#Dynamic_resizing

2
Nate Zaugg

ディクショナリは、指定されたIEqualityComparer比較子を許可します。文字列、または他のタイプの一般的な比較は、最高のパフォーマンスではない場合があります。小さなILSpyは、デフォルトの==コンパレータを使用する場合、実装のパフォーマンスが低下する場合は、独自のIEqualityComparerコンパレータを挿入できることを示します。最終的に、辞書は、キーとして提供したもののハッシュコードを、エントリのリストにある既存のハッシュコードと比較します。

したがって、特定のニーズ辞書がある場合は、FastDictionaryクラスに特化して、より効率的な方法でhascodeにアクセスします。

実装では、次のようになります。

var dictionary = new Dictionary<string, string>(StringComparer.Ordinal); 
2
Walter Vehoeven

リストを使用して、たとえばfieldName = 0、Title = 1となるように列挙型を定義し、各プロパティの一意のインデックスをリストへのルックアップインデックスとして使用できますか?これが最速のソリューションですが、列挙型に縛られるため柔軟性が最も低くなります。

1
Paul Sasik

辞書よりもはるかに速く何かを見つけることはないでしょう。辞書を使うだけです。次に、パフォーマンスの目標を達成していないことがわかり、プロファイラーがディクショナリへの追加/削除がボトルネックであることを示した場合、よりターゲットを絞ったクラスに置き換えることを検討できます。

LINQなどの機能を使用しない場合、パフォーマンスが低下することはありません。

1
Michael