Заметил, что в MSVC реализации std::unordered_map<int, int>::bucket_count() всегда возвращает степень двойки.
Тогда как другие опробованные реализации (STL из онлайн компиляторов gcc и clang, а также boost) возвращают простое число.
Коллеги, как думаете, такое поведение MSVC — это, скорее, хорошо (битовая операция вместо деления на простое число) или плохо (более вероятны коллизии с отсеченным до индекса букета хешем) ?
AG>Коллеги, как думаете, такое поведение MSVC — это, скорее, хорошо (битовая операция вместо деления на простое число) или плохо (более вероятны коллизии с отсеченным до индекса букета хешем) ?
Здравствуйте, uzhas, Вы писали:
U>в бусте несколько политик как раздувать кол-во корзин
жаль, в интерфейс не протянуто, приходится доверять автопилоту.
но там интересный комментарий:
// While the mix policy is generally faster, the prime policy is a lot
// faster when a large number consecutive integers are used, because
// there are no collisions. Since that is probably quite common, use
// prime policy for integeral types. But not the smaller ones, as they
// don't have enough unique values for this to be an issue.
к сожалению то, что они называют mix policy, активируется только на 64-битном size_t, соответственно, на 32-битных сборках там всегда будет prime policy
Как бы ещё узнать, хорошая ли хеш функция.
Допустим, я использую как ключи целые числа, реже строки, ещё реже — хендлы и указатели.
Для всего этого есть стандартная хеш функция.
С одной стороны, логично предположить, что стандартная реализация хеш таблицы рассчитана на работу со стандартной же хеш функцией на обычных данных.
Здравствуйте, Alexander G, Вы писали:
AG>Как бы ещё узнать, хорошая ли хеш функция. AG>Допустим, я использую как ключи целые числа, реже строки, ещё реже — хендлы и указатели. AG>Для всего этого есть стандартная хеш функция.
Возьми набор своих данных и поставь натурный эксперимент: сколько будет коллизий.
Вычисление хеша тоже не за нулевое время делается, хороший хеш может убить производительность посильнее коллизий.
У меня был такой опыт, когда надо было хранить в таблице пятёрки чисел; но природа этих чисел такова, что первые два почти уникально идентифицировали весь кортеж. Поэтому написал тупую формулу с умножением этих двух чисел на два больших простых числа, взятых наобум. И это ускорило реалтаймовую программу на какую-то ощутимую величину, по сравнению со стандартным хешированием кортежа.
Здравствуйте, Кодт, Вы писали: К>Возьми набор своих данных и поставь натурный эксперимент: сколько будет коллизий. К>Вычисление хеша тоже не за нулевое время делается, хороший хеш может убить производительность посильнее коллизий.
Проверил на целочисленных данных. Апроксимированный мой случай выглядит как "тысяча небольших чисел подряд"
Здесь http://rextester.com/l/cpp_online_compiler_visual удобно проверять.
Там можно попробовать и разные компиляторы и буст, и показывается время выполнения (хотя там непонятно, что за конфигурация)
На gcc и clang с 64-битным size_t куча коллизий в boost::, нет их в std:: , но при этом boost несколько быстрее
На vc с 32-битным size_t куча коллизий в std:: и их нет в boost, при этом boost сильно быстрее.
Результаты vc в онлайн компиляторе подозрительные (долго слишком, и разница std и boost слишком большая, возможно, там дебаг).
Проверил студию локально, boost всё же быстрее.
код
#include <iostream>
#include <boost/unordered_set.hpp>
#include <unordered_set>
#include <map>
#include <functional>
#include <algorithm>
#include <iterator>
volatile size_t c;
int main()
{
std::cout << "size_t is " << std::numeric_limits<size_t>::digits << " bit\n";
std::unordered_set<int> v;
//boost::unordered_set<int> v;for (int i = 2000 ; i < 3000 ; i++ )
{
v.emplace(i);
}
for (int times = 0 ; times < 50000 ; times++)
{
for (int i = 1000 ; i < 3000 ; i++ )
{
c = v.count(i);
}
}
std::map<int, int, std::greater<int> > collisions;
for (int bucket = 0, count = v.bucket_count(); bucket < count ; bucket++)
{
size_t size = v.bucket_size(bucket);
if ( size > 1 )
collisions[size]++;
}
if ( collisions.empty() )
{
std::cout << "No collisions\n";
}
else
{
for ( auto& it : collisions )
{
std::cout << it.first << ":" << it.second << "\n";
}
}
}
На строках (небольшое количество слов некоторого скриптоязыка) получается коллизий в бусте не меньше, и std чуть лучше при тесте с локальной студией.
Здравствуйте, Alexander G, Вы писали:
AG>На строках (небольшое количество слов некоторого скриптоязыка) получается коллизий в бусте не меньше, и std чуть лучше при тесте с локальной студией.
Вопрос не в том, на каком примитивном наборе та или иная хеш-таблица будет работать лучше-хуже.
А как она будет работать на типичном твоём наборе.
Если имеешь дело со строками, то — какая природа этих строк.
Возможно, что в качестве хеша сойдёт дворд из первых четырёх букв: если твои строки различаются прямо с первой-четвёртой позиции. Считать CRC для длинных строк в этом случае будет избыточно.
Но так же очевидно, что этот хеш очень плохо дружит с политикой "количество корзин — степень 2", так как итоговый хеш (по модулю от количества корзин) будет не двордом, а байтом или вордом от первых 1 или 2 букв соответственно.