unordered_map для поиска индексов массива

Я хочу найти индексы набора эффективно. Я использую unordered_map и делаю обратную карту вот так

std::unordered_map <int, int> myHash (size); 
Int i = 0;
for (it = someSet.begin(); it != someSet.end(); it++)
{
    myHash.insert({*it , i++});
 }

Это работает, но неэффективно. Я сделал это, чтобы в любое время, когда мне нужны индексы, я мог получить к ним доступ O (1). Анализ производительности показывает мне, что эта часть стала горячей точкой моего кода.

VTune сообщает мне, что оператор new является моей точкой доступа. Я предполагаю, что что-то происходит внутри unordered_map. Мне кажется, что это дело должно быть рассмотрено эффективно. Я еще не мог найти хороший способ. Есть ли лучшее решение? правильный конструктор? Может быть, мне следует передать больше информации конструктору. Я просмотрел список инициализации, но это не совсем то, что я хочу.

Обновление: позвольте мне добавить еще немного информации. Набор не так важен; Я сохраняю набор в массив (отсортированный). Позже мне нужно найти индекс уникальных значений. Я могу сделать это в журнале, но это недостаточно быстро. Вот почему я решил использовать хэш. После этого размер набора (столбцы подматрицы) не меняется.

Это возникает из-за вычисления разреженной матрицы, которое мне нужно, чтобы найти индекс подматрицы в большей матрице. Поэтому размер и шаблон поиска зависят от входной матрицы. Это работает разумно на небольших проблемах. Я мог бы использовать таблицу поиска, но пока я планирую делать это параллельно, таблица поиска для каждого потока может быть дорогостоящей. У меня есть точный размер хеша на момент создания. Я думал, отправив его в конструктор, он перестанет перераспределяться. Я действительно не понимаю, почему он перераспределяет так много.


person Aznaveh    schedule 30.10.2020    source источник
comment
Int? Вы имеете в виду int?   -  person tadman    schedule 31.10.2020
comment
Сколько элементов вы конвертируете? Сколько поисковых запросов вы делаете? Затраты на создание справочной таблицы могут превысить любую экономию, которую вы получите, поэтому это может быть ложной оптимизацией. Существует некоторое пороговое значение, при котором количество элементов › N и количество поисковых запросов › M дают положительные результаты, но ниже этого значения фактически отрицательные результаты.   -  person tadman    schedule 31.10.2020
comment
@tadman Я просто скопировал свой код и упростил его здесь. Забыл изменить эту часть. Всё равно не важно. Int длинное целое   -  person Aznaveh    schedule 31.10.2020
comment
@tadman Это часть более крупного проекта. он отлично работает для небольших размеров ввода, но не работает, когда размер увеличивается   -  person Aznaveh    schedule 31.10.2020
comment
Вам нужно будет изучить, какова отдача от этой стратегии, как я объяснял ранее. Я бы написал вокруг этой вещи класс-оболочку, который выполняет оптимизацию, если считает, что это будет продуктивно, и просто делает это по умолчанию в противном случае. Это облегчает настройку.   -  person tadman    schedule 31.10.2020
comment
Зачем вам индекс элемента set? Даже если он у вас есть, доступ к элементу (с использованием std::distance() равен O(n).   -  person Eugene    schedule 31.10.2020
comment
@Юджин, это часть более крупного проекта. В конце концов я сохраняю набор в массиве.   -  person Aznaveh    schedule 31.10.2020
comment
Кажется, это не имеет смысла в проекте любого размера. Если вы спрашиваете об эффективности, то вам также нужно объяснить, зачем вам индекс. Обратите внимание, что поиск элемента в исходном наборе происходит быстрее: это O (log (n)), а при использовании вашего индекса это O (n).   -  person Eugene    schedule 31.10.2020
comment
Хэш @Eugene превращает O (длинный) в O (1). Я не понимаю, откуда O(n)   -  person Aznaveh    schedule 31.10.2020
comment
Да, доступ к неупорядоченной карте для получения индекса - это O (1). Я просто не могу представить себе ситуацию, когда наличие индекса будет для чего-то полезно. За свой более чем 20-летний опыт работы с C++ я никогда не чувствовал необходимости брать индекс элемента набора (вместо этого может быть полезно хранить итератор). Поэтому я прошу привести пример, как вы будете использовать индекс и какое преимущество в скорости он получит.   -  person Eugene    schedule 31.10.2020
comment
O(n) исходит из использования std::distance(). Где еще вы могли бы использовать индекс?   -  person Eugene    schedule 31.10.2020
comment
Если у вас нет идеального хэша, вы не гарантируете, что получите O (1), а в худшем случае вы получите O (N).   -  person Surt    schedule 31.10.2020
comment
Мне непонятно, какое значение является индексом каждого значения в наборе в порядке итерации. Не существует метода набора для возврата значения с заданным индексом. Это выглядит как решение в поисках проблемы.   -  person Sam Varshavchik    schedule 31.10.2020
comment
@Eugene, выполняющий поиск по индексу, имеет смысл, поскольку итератор становится недействительным при изменении размера.   -  person ALX23z    schedule 31.10.2020
comment
@ALX23z std::set недействителен при изменении размера, у него нет изменения размера ...   -  person Surt    schedule 31.10.2020
comment
Проблема, скорее всего, связана с размером массива. Слишком большой поиск наверняка вызовет проблемы из-за слишком больших фрагментированных выделений. Рассмотрим алгоритмический обходной путь для вашего проекта. Попробуйте найти индексы каким-то другим способом или используйте pmr для размещения в unordered_map. Если вы просто добавляете элементы, возможно, вы могли бы просто сделать большое резервирование и просто поместить элементы один за другим.   -  person ALX23z    schedule 31.10.2020
comment
@Surt, когда он писал SomeSet, он сказал, что хранит индексы массива.   -  person ALX23z    schedule 31.10.2020


Ответы (2)


Проблема в том, что std::unordered_map, в основном реализованный в виде списка векторов, крайне неудобен для кэширования и будет особенно плохо работать с небольшими ключами/значениями (например, int,int в вашем случае), не говоря уже о том, что требуется тонна (повторных) распределений.

В качестве альтернативы вы можете попробовать стороннюю хеш-карту, реализующую открытую адресацию с линейное зондирование (много слов, но базовая структура представляет собой просто вектор, т.е. гораздо более удобна для кэширования). Например, Google dense_hash_map или это: flat_hash_map. Оба могут использоваться в качестве замены для unordered_map и только дополнительно требуют назначения одного значения int в качестве пустого ключа.

person rustyx    schedule 31.10.2020
comment
std::unordered_map не имеет проблем с перераспределением. Возможно, таблица поиска требует таких, но не базовых элементов. Тем не менее, он делает тонны распределений, поэтому он не рекомендуется для больших хэшей. - person ALX23z; 31.10.2020
comment
В итоге я реализую свой собственный хеш, используя линейное зондирование. Это намного эффективнее. - person Aznaveh; 08.11.2020

std::unordered_map‹int, int› часто реализуется так, как будто это

std::vector<std::list<std::par<int, int>>> 

Это вызывает множество выделений и освобождений каждого узла, каждое (де-)распределение использует блокировку, которая вызывает конкуренцию.

Вы можете немного помочь этому, используя emplace вместо вставки, или вы можете прыгнуть в фантастический новый мир аллокаторов pmr. Если ваше создание и уничтожение pmr::unordered_map является однопоточным, вы сможете получить от него много дополнительной производительности. См. Джейсон Тернерс Еженедельник C++ — выпуск 222 — стандартные контейнеры в 3,5 раза быстрее благодаря PMR!, его пример немного маловат, но вы можете получить общее представление.

person Surt    schedule 30.10.2020
comment
Описание проблемы верное, но я не уверен, что PMR — лучшая рекомендация. Хеш-таблицы Google широко используются, но есть и другие более быстрые варианты — probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable хорошо читается. - person Tony Delroy; 31.10.2020