новый KeyValuePair <UInt32, UInt32> (i, j) .GetHashCode (); Высокая частота дубликатов
В поисках быстрого составного ключа для словаря я натолкнулся на аномалию, которую не могу ни понять, ни оправдать.
В ограниченном тестировании
Dictionary<KeyValuePair<UInt32, UInt32>, string>
значительно медленнее (200: 1), чем
Dictionary<KeyValuePair<UInt16, UInt16>, string>
Тест на две петли от 0 до 1000 Заполните, а затем ContainsKey
Poplulate ContainsKey
UInt32 92085 86578
UInt16 2201 431
Проблема в том, что
new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
дает много дубликатов.
В цикле i и j 1024 создаются только 1024 уникальных хеш-значения.
На основе лавинного комментария от CasperOne были опробованы i * 31 и j * 97 (два простых числа), в результате чего 105280 уникален для 1024X1024. Еще много дубликатов. CasperOne Я знаю, что это не то же самое, что случайное. Но это не моя работа, чтобы рандомизировать ввод. GetHashCode () должен рандомизировать вывод.
Why the high number of duplicates?
Тот же цикл на
new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
дает 1024 X 1024 уникальных хэш-кодов (идеально).
Int32 имеет ту же проблему.
Эти дублирующие хэш-значения убивают
Dictionary<KeyValuePair<UInt32, UInt32>, string>
Tuple также генерирует много дубликатов, которые не ухудшаются в Int32 по сравнению с Int16.
Время создания необработанного 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
Постскриптум Самый быстрый - это упаковать .E.G. ((UInt32) int1 & lt; & lt; 16) | int2;
Хеш первого столбца UInt32 равен хешу 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
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
Единственный образец, который я нашел, - это то, что либо сумма, либо разница, либо KVP совпадают.
Но не смог найти образец, когда суммировать, а когда вычитать.
Это плохой хэш, поэтому знание того, что это такое, мало что значит.