Stl map performance?

Я использую map<MyStruct, I*> map1;. Видимо, там 9% от моего общего времени на приложение. В частности, на одной строке одной из моих основных функций. Карта не очень большая (< 1k почти всегда, < 20 является общей).

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

Дополнительная информация: Я всегда проверяю перед добавлением элемента. Если существует ключ, мне нужно сообщить о проблеме. Затем, после точки, я буду использовать карту для поиска и не добавлю больше элементов.

Ответ 1

Сначала вам нужно понять, что такое карта и какие операции, которые вы выполняете, представляют. A std::map представляет собой сбалансированное двоичное дерево, lookup будет принимать операции O( log N ), каждый из которых представляет собой сравнение ключей плюс некоторые дополнительные, которые вы можете игнорировать в большинстве случаев (управление указателями). Вставка занимает примерно одно и то же время, чтобы найти точку вставки, а также выделение нового node, фактического ввода в дерево и перебалансировки. Сложность снова O( log N ), хотя скрытые константы выше.

Когда вы пытаетесь определить, находится ли ключ на карте перед вставкой, вы несете затраты на поиск и, если это не удается, то же самое стоит найти точку вставки. Вы можете избежать дополнительных затрат, используя std::map::insert, которые возвращают пару с помощью итератора и bool, сообщающий вам, действительно ли произошло вставка или элемент уже существует.

Кроме того, вам нужно понять, насколько дорого стоит сравнить ваши ключи, которые выпадают из того, что показывает вопрос (MyStruct может содержать только один int или тысячу из них), что вам нужно принимать во внимание.

Наконец, может быть, что map не самая эффективная структура данных для ваших нужд, и вам может потребоваться использовать либо std::unordered_map (хеш-таблицу), которая ожидала вставки постоянной времени (если хеш-функция не ужасна), либо для небольших наборов данных даже обычный упорядоченный массив (или std::vector), на котором вы можете использовать бинарный поиск для поиска элементов (это уменьшит количество распределений за счет более дорогих вставок, но если удерживаемые типы достаточно малы, это может стоить того)

Как всегда с производительностью, измерьте, а затем попытайтесь понять, где тратится время. Также обратите внимание, что 10% времени, потраченного на определенную функцию или структуру данных, может быть много или почти ничего вообще, в зависимости от того, что представляет собой ваше приложение. Например, если ваше приложение просто выполняет поиск и вставки в набор данных, и для этого требуется всего 10% от процессора, вам нужно много чего оптимизировать во всем мире!

Ответ 2

Вероятно, будет быстрее просто сделать insert и проверить, является ли pair.second false, если ключ уже существует:

как это

if ( myMap.insert( make_pair( MyStruct, I* ) ).second == false)
{
  // report error
}
else
  // inserted new value

... вместо того, чтобы каждый раз звонить find.

Ответ 3

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

Ответ 4

Это может быть длинный снимок, но для небольших коллекций иногда наиболее важным фактором является cache.

Так как std::map реализует дерево Red-Black Tree, которое [AFAIK] не очень эффективно кэширует - возможно, реализация карты как std::vector<pair<MyStruct,I*>> будет хорошей идеей и будет использовать бинарный поиск там [вместо отображения карты -ups], по крайней мере, он должен быть эффективен, только когда вы начинаете искать [останавливать вставленные элементы], поскольку std::vector с большей вероятностью подходит к кешу, чем map.

Этот фактор [cpu-cache] обычно игнорируется и скрывается как постоянный в большой нотации O, но для больших коллекций он может иметь большой эффект.

Ответ 5

Как вы используете карту, вы выполняете поиск на основе экземпляра MyStruct и в зависимости от конкретной реализации, требуемое сравнение может быть или не быть дорогостоящим.

Ответ 6

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

Если вы достаточно хорошо понимаете проблему, вы должны указать, как ваша реализация будет выше.

Является ли map правильной структурой? Если это так, то реализация стандартной библиотеки, вероятно, будет хорошего качества (хорошо оптимизирована).

Можно ли упростить сравнение MyStruct?

Где проблема - изменение размера? Поиск?

Вы минимизировали копию и назначили затраты для своих структур?

Ответ 7

Как указано в комментариях, без правильного кода, вы не получите никаких универсальных ответов. Однако, если MyStruct действительно огромный, копирование стека может быть дорогостоящим. Возможно, имеет смысл хранить указатели на MyStruct и реализовать свой собственный механизм сравнения:

template <typename T> struct deref_cmp {
  bool operator()(std::shared_ptr<T> lhs, std::shared_ptr<T> rhs) const {
    return *lhs < *rhs;
  }
};

std::map<std::shared_ptr<MyStruct>, I*, deref_cmp<MyStruct>> mymap;

Однако, это то, что вам придется профилировать. Это может ускорить процесс.

Вы бы искали такой элемент, как этот

template <typename T> struct NullDeleter {
  void operator()(T const*) const {}
};
// needle being a MyStruct
mymap.find(std::shared_ptr<MyStruct>(&needle,NullDeleter()));

Излишне говорить, что существует больше возможностей для оптимизации.