Dictionary<struct,int>
の場合を1回の呼び出しで追加/設定できますか?C#辞書パフォーマンス上の理由から1つのコールで追加/設定する
エントリごとに1つの参照で以下のコードを実行することはできますか?
これは膨大なデータを処理する非常にタイトなループで行われるため、すべてのパフォーマンスが重要です。
さらに適切なデータ構造がありますか?
Dictionary<struct,int>
の場合を1回の呼び出しで追加/設定できますか?C#辞書パフォーマンス上の理由から1つのコールで追加/設定する
エントリごとに1つの参照で以下のコードを実行することはできますか?
これは膨大なデータを処理する非常にタイトなループで行われるため、すべてのパフォーマンスが重要です。
さらに適切なデータ構造がありますか?
はい、これを行う方法はありますが、欠点があります。このクラスを考えてみましょう:
class Ref<T>
{
public T Value;
}
あなたはDictionary<K, Ref<int>> dict
を使用して、これを行うことができます。
Ref<int> count;
if (!dict.TryGetValue(key, out count))
{
count = new Ref<int> { Value = 0 };
dict[key] = count;
}
count.Value += points;
欠点は今、あなたの辞書のエントリごとに追加のヒープオブジェクトを持っているということです。あなたの状況によっては、これは受け入れられるかもしれません。
これは本当に速いのですか?あなたはかなりの場所で同じ操作をしているし、最初のヒット(辞書に項目を追加するとき)はオブジェクトを作成することによってさらに重くなります。 –
@QualityCatalyst 2つのアプローチをプロファイルする必要があります。私が解釈した質問は、単なる辞書検索でこれを行うことが可能かどうかということでした。私はちょうどそれが可能であり、定性的に欠点を特徴付けていると説明しました。プロファイリングではこれがパフォーマンスにとって恐ろしいアプローチであることが明らかになっているかもしれませんが、私はその答えを無効にするとは思わないでしょう。 –
ありがとう、ティモシーしかし、この場合実際にはるかに大きな問題を作成:)私は私の質問を十分に指定していないと思う。これが本当に唯一の方法なら、私はとにかくそれを受け入れます –
非常に厄介な解決策ですが、多くの場合、辞書にある既存の項目のほとんどがAddPoints()
メソッドと呼ばれるという前提のもとでは高速になります。
void AddPoints(T key, int points)
{
try
{
_KeyToPoints[key] = _KeyToPoints[key] + points;
}
catch (ArgumentException)
{
_KeyToPoints[key] = points;
}
}
例外を投げて処理するのは非常に高価(時間がかかる)であり、パフォーマンスに悪影響を及ぼします。
void PreFillKeys(params T[] keys) // use an IEnumerable<T> if handier
{
foreach (T key in keys)
{
// EITHER THIS:
if (!_KeyToPoints.ContainsKey(key))
_KeyToPoints[key] = 0;
/* OR THIS (faster if you know none of keys was added before)
try
{
_KeyToPoints[key] = 0;
}
catch (ArgumentException)
{
// ignore >> you shouldn't have called
}
*/
}
}
ロール独自の辞書:これを回避するには、このような辞書を事前に埋めることができます。あなたが本当に必要とするのは、特定のタイプのための最適化されたセッター/ゲッター関数であれば、その目的に特化した独自の辞書を作ることができます。思考を得るための参照コードが必要な場合は、組み込み辞書のソースコードはhttp://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,d3599058f8d79be0にあります。
あなたは古いHashtableコンテナを使用することができます
Hashtable map = new Hashtable();
map[key] = value;
キーが存在しない場合は、それが作成し、マップに自動的に追加されますが。しかし、あなたは値の型であるキーを使用する場合、ボクシング/アンボクシングに苦しむでしょう...
私はあなたが辞書に2回インデックスを付けることを避けることができるとは思わない(一度は既存の値を取得し、再度値を設定するには)、元の値を0に設定するのをスキップすることができます。これは、キーが見つからない場合、TryGetValueがそれを行います。また、TryGetValueがfalseを返す場合は、チェックを行う必要があります。 – SlimsGhost
プロセス全体に関連して_actual_パフォーマンスの問題がありますか?それとも、あると思っていますか?辞書ルックアップはO(1)なので、ルックアップでのパフォーマンスオーバーヘッドはほとんどありません。(その場合はそのセット) –
@SlimsGhostこの場合、結果を確認する必要はありません。キーが存在しない場合'_KeyToPoints [key]'はそのキー位置に '0 + points'を格納します。キーが存在するかどうかに関係なく、コードは同じになります。 –