0

3 つの異なるソースからの要素を調整する方法を探しています。キー (文字列) とバージョン (長い) だけを持つように要素を単純化しました。

リストは同時に取得されます (別のデータベース クエリから 2 つ、別のシステムのメモリ キャッシュから 1 つ)。

私の最終結果では、3 つのソースすべてで同一のバージョンではない要素のみを気にします。したがって、私が気にする結果は、各システムの対応するバージョンを含むキーのリストになります。

Element1 | system1:v100    | system2:v100 | system3:v101 |
Element2 | system1:missing | system2:v200 | system3:v200 |

同じバージョンの要素は破棄できます。

私が考えたこれを達成する2つの方法は

  1. すべてのデータソースが取得を完了するのを待ってから、各リストをループして、キーの結合 + 3 つのバージョンすべてでマスター リストを集約します (同一のアイテムはすべて破棄します)。

  2. 最初のリストの取得が完了するとすぐに、それをディクショナリ (.net 4.0 で提供) などの並行コレクションに入れ、残りのリストが利用可能になり次第 (並行コレクションに) 集約を開始します。

私の考えでは、2 番目のアプローチの方が少し速くなりますが、おそらくそれほど速くはありません。3 つのソースすべてが存在するまで、私は実際には多くのことを行うことができないため、2 番目のアプローチから得られるものは多くなく、競合が発生します。

たぶん、これについてはまったく別の方法がありますか?また、バージョンは long を使用して保存され、数十万 (場合によっては数百万) の要素が存在するため、メモリ割り当てが問題になる可能性があります (ただし、これらのオブジェクトは存続期間が短いため、おそらく大きな問題ではありません)。

4

2 に答える 2

2

HashSet は、Union メソッドと Intersect メソッドがあるため、オプションです

HashSet.UnionWith メソッド

これを使用するには、Equals と GetHashCode をオーバーライドする必要があります。
優れた (一意の) ハッシュは、パフォーマンスの鍵です。

バージョンがすべて v の場合、数値を使用して、欠落しているハッシュを 0 として構築
できます。Int32 を使用して、バージョンが Int10 以下の場合、完全なハッシュを作成できます。

もう 1 つのオプションは ConcurrentDictionary (同時 HashSet はありません) で、3 つすべてをフィードします。
Equals と GetHashCode をオーバーライドする必要があります。
私の直感は 3 つの HashSet であり、Union の方が高速です。

すべてのバージョンが数値であり、欠落に 0 を使用できる場合は、UInt32 または UInt64 にパックして、それを直接 HashSet に入れることができます。ユニオンの後、開梱します。アンパックをパックするには、数学ではなくビットプッシュ << を使用します。

これはたった 2 つの UInt16 ですが、2 秒で実行されます。
これは、Hashing クラスよりも高速になります。

3 つのバージョンがすべて長い場合、HashSet<integral type>はオプションではありません。
long1 ^ long2 ^ long3; 良いハッシュかもしれませんが、それは私の専門知識ではありません。
タプルの GetHashCode が悪いことは知っています。

class Program
{
    static void Main(string[] args)
    {
        HashSetComposite hsc1 = new HashSetComposite();
        HashSetComposite hsc2 = new HashSetComposite();
        for (UInt16 i = 0; i < 100; i++)
        {
            for (UInt16 j = 0; j < 40000; j++)
            {
                hsc1.Add(i, j);
            }
            for (UInt16 j = 20000; j < 60000; j++)
            {
                hsc2.Add(i, j);
            }
        }
        Console.WriteLine(hsc1.Intersect(hsc2).Count().ToString());
        Console.WriteLine(hsc1.Union(hsc2).Count().ToString());
    }
}

public class HashSetComposite : HashSet<UInt32>
{
    public void Add(UInt16 u1, UInt16 u2)
    {      
        UInt32 unsignedKey = (((UInt32)u1) << 16) | u2;
        Add(unsignedKey);           
    }
    //left over notes from long
    //ulong unsignedKey = (long) key;
    //uint lowBits = (uint) (unsignedKey & 0xffffffffUL);
    //uint highBits = (uint) (unsignedKey >> 32);
    //int i1 = (int) highBits;
    //int i2 = (int) lowBits;
}

ConcurrentDictionary を使用してテストしたところ、上記は 2 倍以上高速でした。
挿入物をロックするのはコストがかかります。

于 2012-10-18T13:26:33.800 に答える
0

あなたの問題は、イベントベースのソリューションに適しているようです。基本的に、ソースごとにデータの完了にイベントを割り当てます。type でグローバル同時ハッシュを保持します。イベント ハンドラーでは、完成したデータ ソースを調べて、同時ハッシュに現在の要素のキーが含まれている場合は、指定された要素で新しいリストを挿入するだけでなく、それをリストに追加します。

ただし、パフォーマンス要件によっては、アプリケーションが過度に複雑になる場合があります。最初の方法は、使用する最も簡単な方法です。

于 2012-10-18T14:05:20.563 に答える