new KeyValuePair <UInt32, UInt32> (i, j) .GetHashCode (); Hohe Rate von Duplikaten
Auf der Suche nach einem schnellen zusammengesetzten Schlüssel für Dictionary bin ich auf eine Anomalie gestoßen, die ich weder verstehen noch rechtfertigen kann.
In begrenzten Tests
Dictionary<KeyValuePair<UInt32, UInt32>, string>
ist deutlich langsamer (200: 1) als
Dictionary<KeyValuePair<UInt16, UInt16>, string>
Test auf zwei Schleifen von 0 bis 1000 Bestücken und dann ContainsKey
Poplulate ContainsKey
UInt32 92085 86578
UInt16 2201 431
Das Problem ist, dass
new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
ergibt VIELE Duplikate.
In der Schleife von i und j 1024 werden nur 1024 eindeutige Hash-Werte erstellt.
Basierend auf dem Lawinenkommentar von CasperOne haben wir es mit i * 31 und j * 97 (zwei Primzahlen) versucht, und das ergab 105280 Unikate auf 1024X1024. Immer noch viele Duplikate. CasperOne Ich weiß, das ist nicht gleich Zufall. Es ist jedoch nicht meine Aufgabe, die Eingabe zufällig zu ordnen. GetHashCode () soll die Ausgabe randomisieren.
Warum die hohe Anzahl von Duplikaten?
Gleiche Schleife an
new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
ergibt 1024 x 1024 eindeutige Hash-Codes (perfekt).
Int32 hat das gleiche Problem.
Diese doppelten Hashwerte töten
Dictionary<KeyValuePair<UInt32, UInt32>, string>
Tuple generiert auch viele Duplikate, die bei Int32 im Vergleich zu Int16 nicht beeinträchtigt werden.
Die Zeit zum Generieren des unformatierten KVP und des unformatierten KPV.GetHashCode ist ähnlich.
Gleiche Anomalie mit HashSet.
Dictionary<KeyValuePair<UInt32, UInt32>, string> dKVPu32 = new Dictionary<KeyValuePair<UInt32, UInt32>, string>();
Dictionary<KeyValuePair<UInt16, UInt16>, string> dKVPu16 = new Dictionary<KeyValuePair<UInt16, UInt16>, string>();
KeyValuePair<UInt32, UInt32> kvpUint32;
KeyValuePair<UInt16, UInt16> kvpUint16;
int range = 1000;
Int32 hashCode;
HashSet<Int32> kvpUint32Hash = new HashSet<Int32>();
HashSet<Int32> kvpUint16Hash = new HashSet<Int32>();
Stopwatch sw = new Stopwatch();
sw.Start();
for (UInt32 i = 0; i < range; i++)
{
for (UInt32 j = 0; j < range; j++)
{
kvpUint32 = new KeyValuePair<UInt32, UInt32>(i, j);
}
}
Console.WriteLine("UInt32 raw " + sw.ElapsedMilliseconds.ToString());
// 7
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
for (UInt16 j = 0; j < range; j++)
{
kvpUint16 = new KeyValuePair<UInt16, UInt16>(i, j);
}
}
Console.WriteLine("UInt16 raw " + sw.ElapsedMilliseconds.ToString());
// 6
sw.Restart();
for (UInt32 i = 0; i < range; i++)
{
for (UInt32 j = 0; j < range; j++)
{
hashCode = new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
kvpUint32Hash.Add(hashCode);
}
}
Console.WriteLine("UInt32 GetHashCode " + sw.ElapsedMilliseconds.ToString() + " unique count " + kvpUint32Hash.Count.ToString());
// 285 1024
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
for (UInt16 j = 0; j < range; j++)
{
hashCode = new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
kvpUint16Hash.Add(hashCode);
}
}
Console.WriteLine("UInt16 GetHashCode " + sw.ElapsedMilliseconds.ToString() + " unique count " + kvpUint16Hash.Count.ToString());
// 398 1000000
sw.Restart();
Console.ReadLine();
for (UInt32 i = 0; i < range; i++)
{
for (UInt32 j = 0; j < range; j++)
{
dKVPu32.Add(new KeyValuePair<UInt32, UInt32>(i, j), String.Format("{0} {1}", i.ToString(), j.ToString()));
}
}
Console.WriteLine("hsKVPu32 pop " + sw.ElapsedMilliseconds.ToString());
// 92085
sw.Restart();
for (UInt32 i = 0; i < range; i++)
{
for (UInt32 j = 0; j < range; j++)
{
if (!dKVPu32.ContainsKey(new KeyValuePair<UInt32, UInt32>(i, j))) Debug.WriteLine("Opps"); ;
}
}
Console.WriteLine("hsKVPu32 find " + sw.ElapsedMilliseconds.ToString());
// 86578
dKVPu32.Clear();
dKVPu32 = null;
GC.Collect();
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
for (UInt16 j = 0; j < range; j++)
{
dKVPu16.Add(new KeyValuePair<UInt16, UInt16>(i, j), String.Format("{0} {1}", i.ToString(), j.ToString()));
}
}
Console.WriteLine("hsKVPu16 pop " + sw.ElapsedMilliseconds.ToString());
// 2201
sw.Restart();
for (UInt16 i = 0; i < range; i++)
{
for (UInt16 j = 0; j < range; j++)
{
if (!dKVPu16.ContainsKey(new KeyValuePair<UInt16, UInt16>(i, j))) Debug.WriteLine("Opps"); ;
}
}
sw.Stop();
Console.WriteLine("hsKVPu16 find " + sw.ElapsedMilliseconds.ToString());
// 431
P.S. Am schnellsten packt man .E.G. ((UInt32) int1 << 16) | int2;
Der Hash der ersten UInt32-Spalte entspricht dem Hash des KVP der nächsten beiden.
2281371105 8 992
2281371104 8 993
2281371107 8 994
2281371145 0 0
2281371147 0 2
2281371149 0 4
2281371151 0 6
2281371137 0 8
2281371144 0 1
2281371146 0 3
2281371148 0 5
2281371150 0 7
2281371136 0 9
2281371144 1 0
2281371145 1 1
2281371146 1 2
2281371147 1 3
2281371148 1 4
2281371149 1 5
2281371150 1 6
2281371151 1 7
2281371136 1 8
2281371137 1 9
2281371147 2 0
2281371146 2 1
2281371144 2 3
2281371151 2 4
2281371150 2 5
2281371149 2 6
2281371148 2 7
2281371139 2 8
Das einzige Muster, das ich gefunden habe, ist, dass entweder die Summe oder die Differenz oder der KVP übereinstimmt.
Konnte aber kein Muster finden, wann man summiert und wann man subtrahiert.
Es ist ein schlechter Hasch, also ist es von geringem Wert zu wissen, was es ist.