2011-05-05 3 views
18

Я использую unordered_map из gnu ++ 0x для хранения огромного количества данных. Я хочу предварительно выделить пространство для большого количества элементов, так как я могу связать используемое общее пространство.Предварительно выделяющие ведра в C++ unordered_map

То, что я хотел бы быть в состоянии сделать это вызов:

std::unordered_map m; 
m.resize(pow(2,x)); 

где х известно.

unordered_map не поддерживает это. Я предпочел бы использовать unordered_map, если это возможно, так как он в конечном итоге станет частью стандарта.

Некоторые другие ограничения:

нужны надежные O (1) доступ и мутацию карте. Желаемые функции хэша и сравнения уже нестандартны и несколько дороги. O (log n) (как и для std :: map) слишком дорого.

-> Дорогие хеш и сравнение также делают слишком дорогой рост на основе амортизации. Каждая дополнительная вставка требует O (n) операций от этих функций, что приводит к дополнительному квадратичному члену во время выполнения алгоритма, поскольку требования к экспоненциальному хранению требуют O (n) роста.

ответ

27
m.rehash(pow(2,x)); 

если pow(2, x) - это количество ведер, которые вы хотите предварительно распределить. Вы также можете:

m.reserve(pow(2,x)); 

но теперь pow(2, x) это количество элементов, которые вы планируете на вставке. Обе функции ничего не делают, кроме предустановленных ведер. Они не вставляют никаких элементов. И оба они предназначены для использования именно в вашем случае использования.

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

Вот точная формулировка, что последние (N4660) используется для указания аргумента rehash:

a.rehash(n): Постусловие:a.bucket_count() >= a.size()/a.max_load_factor() and a.bucket_count() >= n.

Это условие гарантирует, что bucket()_count() >= n, и что load_factor() остается на уровне max_load_factor().

Впоследствии reserve(n) определена в терминах rehash(n):

a.reserve(n): То же, что a.rehash(ceil(n/a.max_load_factor())).

+0

Вы используете подсказку, как если бы она находилась в: итератор std :: set :: insert (iterator hint, const value_type & value); http://en.cppreference.com/w/cpp/container/set/insert, выглядит неправильно. –

2

Я бы предложил написать собственный распределитель для std::unordered_map, который выделяет память точно так, как вы хотите.

4

Я не думаю, что для неупорядоченной карты важно иметь предварительно выделенную память. Ожидается, что STL будет O (n) амортизированным временем вставки. Спасите себя от хлопот написания своего распределителя, пока не узнаете, что это, по-моему, бутылочная горловина вашего кода.

+0

STL гарантирует O (n) амортизированное время вставки, но обычным способом реализации этого является увеличение количества ведер по постоянному коэффициенту, а затем перефразирование каждого существующего элемента. Это происходит O (log n) раз, если вы сохраняете n элементов на карте. Когда n имеет размер 2 ^, это добавляет дополнительный коэффициент большой к числу выполненных вставок. Я пытаюсь сбрить этот фактор. – JAD

+0

«это добавляет дополнительный фактор больших» Нет, добавляет дополнительный коэффициент 2. Вы понимаете, как работают амортизированные операции? Единственная реальная причина, по которой этот ответ неверен, заключается в том, что он «не гарантирует» O (n) амортизированное время вставки, он обеспечивает ожидаемое O (n) амортизированное время вставки с экспоненциально высокой вероятностью над случайно вставленными элементами. Если вы знаете точные размеры, которые будут регулировать ведра, и используемая хеш-функция, все же можно обмануть хэш-таблицу и заставить N коллизий для каждой вставки. – codetaku

-2

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

+0

Некоторые моменты, которые вы делаете, не имеют смысла, или вы не поняли себя. Например, «если преобразованное значение динамически изменяется, это размер (например, вектор)». Независимо от того, сколько элементов у вас есть в векторе (или любом контейнере или классе, если на то пошло), 'sizeof (std :: vector )' остается тем же (для того же самого 'T'). «Карта» зарезервирует точное пространство для 'std :: vector' из 1 элемента или' std :: vector' из 1 мил элементов. «вы можете зарезервировать самый большой контейнер, который может когда-либо существовать» - еще один момент, который я не рассматриваю в качестве разумного совета в контексте этого вопроса. – bolov

0

Конструктор принимает параметр «size_type bucket_count» в соответствии с http://en.cppreference.com/w/cpp/container/unordered_map/unordered_map

так самый простой способ сделать то, что говорит ваш пример кода:

std::unordered_map m{ pow(2,x) }; 

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

 Смежные вопросы

  • Нет связанных вопросов^_^