Какой контейнер выбрать для быстрого поиска / вставки с огромными объемами данных?

0

Так что это мысленный эксперимент. Я хочу иметь огромную коллекцию структур, таких как:

struct
{
    KeyType key;
    ValueType value;
}

И мне нужен быстрый доступ с помощью ключа и быстрая вставка новых значений.

Я бы не использовал std :: map, потому что у него слишком большая накладная память для одной структуры, и для огромного количества данных она может быть радикальной. Правильно?

Поэтому в следующем случае я бы рассмотрел использование отсортированных std :: vector и binary_search. Это отлично подходит для поиска, но добавление новых значений в вектор будет слишком медленным. Представьте, что вам нужно добавить новое значение в начало отсортированного массива, вам нужно будет переместить данные вправо aaaaaAAAALOT!

Что делать, если я использую deque? Поскольку я знаю, что O (1) для push_back/push_front, но все же O (n) для вставки (поскольку в любом случае ему придется перемещать данные, тем меньше данных).

Вопросы:

1) Является ли O (n) введением данных в deque намного быстрее в реальной ситуации, чем O (n) в векторе?

2) Что происходит, когда вы вставляете значение в Deque и ведро, в которое оно должно войти, заполнено?

3) Есть ли другой предпочтительный тип контейнера, если вам нужно хранить много данных и нужны две быстрые операции: поиск и вставка?

Благодарю!

  • 0
    Сначала укажите типы поиска и количество ожидаемых членов и операций по типу: вставки, удаления, поиски (различного типа - девятый самый старый, точный ключ, ближайший ключ ...)
  • 1
    Хотя сложность времени важна, иерархия памяти также может доминировать в производительности. Теоретический анализ может только сузить наш выбор. Измерение - это единственный практический способ точно определить, какой выбрать.
Теги:
algorithm
vector
deque

4 ответа

3

Я бы не использовал std :: map, потому что у него слишком большая накладная память для одной структуры, и для огромного количества данных она может быть радикальной. Правильно?

Это зависит от размера ваших структур... Чем больше они тем меньше накладных расходов, тем больше доля использования памяти. Например, реализация std::map может в среднем сказать 20 байтов служебных данных для каждого элемента (я только что сделал эту меру в вашей собственной системе), поэтому, если ваш размер структуры находится в сотнях байт - кто заботится...? Но, если структура содержит 2 ints, это большая доля....

Поэтому в следующем случае я бы рассмотрел использование отсортированных std :: vector и binary_search. Это отлично подходит для поиска, но добавление новых значений в вектор будет слишком медленным. Представьте, что вам нужно добавить новое значение в начало отсортированного массива, вам нужно будет переместить данные вправо aaaaaAAAALOT!

Полностью неподходящий....

1) Является ли O (n) введением данных в deque намного быстрее в реальной ситуации, чем O (n) в векторе?

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

2) Что происходит, когда вы вставляете значение в Deque и ведро, в которое оно должно войти, заполнено?

Как и выше, его нужно будет перетасовать, переполнив либо:

  • последний элемент станет первым элементом следующего "ведра", перемещая все эти элементы вдоль и переливаясь в следующее ведро и т.д.

  • первый элемент станет последним элементом предыдущего ведра, перемещая все эти элементы вдоль и переливаясь в следующее ведро и т.д.

3) Есть ли другой предпочтительный тип контейнера, если вам нужно хранить много данных и нужны две быстрые операции: поиск и вставка?

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

3

3) Есть ли другой предпочтительный тип контейнера, если вам нужно хранить много данных и нужны две быстрые операции: поиск и вставка?

Рассмотрим использование std::unordered_map, который представляет собой реализацию хэш-карты. Вставка, поиск и удаление - все O (1) в среднем случае. Это предполагает, что вы будете только искать предмет, основанный на его точном ключе; если ваши поисковые запросы могут иметь разные ограничения, вам потребуется либо другая структура, либо вам нужно несколько карт для сопоставления различных ключей, которые вы будете искать для соответствующего объекта.

Это требует наличия хэш-функции для KeyType, либо как часть стандартной библиотеки, либо предоставленной вами.

2

Там нет контейнера, который обеспечит вам лучшее из всех миров. Как вы говорите, вам нужен лучший поиск/вставка с минимальным объемом пространства, необходимого для хранения элементов.

Ниже, если список контейнеров, который вы могли бы рассмотреть для своей реализации: -

ВЕКТОР: -

Сильные стороны: -

1) Space is allocated only for holding data. 
2) Good for random access.
3) Container of choice if insertions/deletions are not in the middle of the container.

Слабое место:-

1) poor performance if insertions/deletions are at the middle.
2) rellocations happen if reserve is not used properly.

DEQUE: -

Выберите deque over vector в том случае, если вставки/удаления находятся как в начале, так и в конце контейнера.

КАРТА:-

Недостаток над вектором: -

1) more space is allocated for holding pointers.

Преимущества над вектором: -

1) better insertions/deletions/lookup as compared to vector.

Если используется std::unordered_map то эти операции с словарями будут амортизированы O (1).

  • 0
    Еще одним большим преимуществом по сравнению с дека вектора IMHO является то , что одна избежать перераспределения.
  • 0
    @geoalgo: ты уверен в этом? Насколько я знаю, вставки и удаления внутри deque делают недействительными указатели и итераторы, поэтому перераспределение будет разрешено.
Показать ещё 1 комментарий
1

Во-первых, чтобы прямо ответить на ваши вопросы:

1) Является ли O (n) введением данных в deque намного быстрее в реальной ситуации, чем O (n) в векторе?

Количество элементов, которые нужно перемещать, (в среднем) составляет лишь половину по сравнению с вектором. Тем не менее, это может на самом деле ухудшиться, поскольку данные хранятся в несмежной памяти, поэтому копирование/перемещение одного и того же количества элементов намного менее эффективно (его нельзя, например, реализовать в терминах одной операции memcopy).

2) Что происходит, когда вы вставляете значение в Deque и ведро, в которое оно должно войти, заполнено?

По крайней мере, для реализации gnu gcc Libstdc++ каждое ведро, кроме первого и последнего, всегда заполнено. Я полагаю, что вставка в середину означает, что все элементы перемещаются/копируются на один слот ближе к концу (спереди или сзади), и эффект рябит все ведра до тех пор, пока не будет достигнут первый или последний.

Таким образом, единственный сценарий, где std :: deque последовательно лучше, чем вектор, - если вы используете его как (suprise) в очереди (только вставляя и удаляя элементы с фронта или конца) и для чего оптимизируется реализация. Он не оптимизирован для вставок посередине.

3) Есть ли другой предпочтительный тип контейнера, если вам нужно хранить много данных и нужны две быстрые операции: поиск и вставка?

Как уже было сказано другими: хеш-таблица типа std :: unordered_map - это структура данных, которую вы ищете.

Из того, что я слышал, std :: unordered_map является немного субоптимальной реализацией, если он, поскольку использует ведра для разрешения хеш-коллизий, и эти ковши реализуются как связанные списки (вот очень интересный разговор Чандлера Каррута о общая тема работы различных структур данных). Для случайного доступа к крупным структурам данных локализация кэша должна иметь значение намного меньше, поэтому, вероятно, это не такая большая проблема в вашем случае.

Наконец, я хотел бы упомянуть, что если ваши значения и типы ключей являются небольшими POD и в зависимости от того, насколько велика ваша огромная коллекция (речь идет о нескольких миллионах или, скорее, о миллиардах элементов) и о том, как часто вам приходится вставлять/удалять элементы, все равно могут быть случаи, когда простой std :: vector превосходит любой другой контейнер STL. Итак, как всегда: если ваш мысленный эксперимент когда-либо станет реальностью, попробуйте и измерите.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню