Dictionary の高速な複合キーを探していると、理解も正当化もできない異常に遭遇しました。
限定テストで
Dictionary<KeyValuePair<UInt32, UInt32>, string>
よりも大幅に遅い (200:1)
Dictionary<KeyValuePair<UInt16, UInt16>, string>
0 から 1000 までの 2 つのループでテストします。
Poplulate ContainsKey
UInt32 92085 86578
UInt16 2201 431
問題はそれです
new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
多くの複製が得られます。
i と j 1024 のループでは、1024 個の一意のハッシュ値のみが作成されます。
CasperOne からの雪崩のコメントに基づいて、i*31 と j*97 (2 つの素数) を試したところ、1024X1024 で 105280 の一意の結果が得られました。相変わらず重複が多い。CasperOne ランダムと同じではないことはわかっています。しかし、入力をランダム化するのは私の仕事ではありません。GetHashCode() は、出力をランダム化することになっています。
重複の数が多いのはなぜですか?
同一ループオン
new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
1024 X 1024 の一意のハッシュ コードが生成されます (完全)。
Int32 にも同じ問題があります。
これらの重複したハッシュ値は殺します
Dictionary<KeyValuePair<UInt32, UInt32>, string>
タプルはまた、Int16 と比較して Int32 で低下しない多くの重複を生成します。
生の KVP と生の KPV.GetHashCode を生成する時間は似ています。
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
PS 最速は .EG ((UInt32)int1 << 16) | をパックすることです。int2;
最初の UInt32 列のハッシュは、次の 2 つの KVP のハッシュと同じです。
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
22813711510 1 6
2281371151 1 7
2281371136 8
2281371137 1 9 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
私が見つけた唯一のパターンは、合計または差、または KVP が一致することです。
しかし、いつ加算し、いつ減算するかのパターンを見つけることができませんでした。
これは悪いハッシュであるため、それが何であるかを知ることはほとんど価値がありません。