Какой контейнер STL следует использовать для FIFO?

Какой контейнер STL лучше всего подходит моим потребностям? У меня в основном есть контейнер размером 10 элементов, в котором я постоянно push_back новых элементов, а pop_front самый старый элемент (около миллиона раз).

В настоящее время я использую std::deque для задачи, но задавался вопросом, будет ли std::list более эффективным, поскольку мне не нужно будет перераспределять себя (или, может быть, я ошибаюсь std::deque для std::vector?). Или есть еще более эффективный контейнер для моей потребности?

P.S. Мне не нужен произвольный доступ

Ответ 1

Поскольку существует множество ответов, вы можете быть смущены, но суммировать:

Используйте std::queue. Причина этого проста: это структура FIFO. Вы хотите FIFO, вы используете std::queue.

Это делает ваше намерение понятным для кого-то еще и даже для вас. A std::list или std::deque нет. Список может вставляться и удаляться в любом месте, что не является структурой FIFO, и deque может добавлять и удалять с любого конца, что также не может сделать структура FIFO.

Вот почему вы должны использовать queue.

Теперь вы спросили о производительности. Во-первых, всегда помните это важное правило: Хороший код во-первых, производительность последней.

Причина этого проста: люди, которые стремятся к успеху перед чистотой и элегантностью, почти всегда заканчиваются. Их код становится медлительным, потому что они отказались от всего, что хорошо, чтобы действительно ничего не вынести из него.

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

Что все сказано, std::queue - это только адаптер. Он обеспечивает безопасный интерфейс, но использует внутренний контейнер внутри. Вы можете выбрать этот базовый контейнер, и это обеспечивает большую гибкость.

Итак, какой основной контейнер следует использовать? Мы знаем, что std::list и std::deque предоставляют необходимые функции (push_back(), pop_front() и front()), так как мы решаем?

Во-первых, поймите, что выделение (и освобождение памяти) памяти - это не то, что нужно делать, в общем, потому что это связано с выходом в ОС и просить ее что-то сделать. A list должен выделять память каждый раз, когда что-то добавляется, и освобождать ее, когда она уходит.

A deque, с другой стороны, выделяет куски. Он будет выделяться реже, чем a list. Подумайте об этом как о списке, но каждый кусок памяти может содержать несколько узлов. (Конечно, я бы предположил, что вы действительно узнаете, как это работает.)

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

Вторая вещь для понимания - производительность кеша. Выход в ОЗУ медленный, поэтому, когда CPU действительно нужен, он делает все возможное из этого времени, беря с собой кусок памяти обратно в кеш. Поскольку deque выделяет в кусках памяти, вероятно, что доступ к элементу в этом контейнере приведет к тому, что CPU также вернет остальную часть контейнера. Теперь любые дальнейшие обращения к deque будут быстрыми, поскольку данные находятся в кеше.

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

Итак, учитывая, что a deque должен быть лучшим выбором. Вот почему это контейнер по умолчанию при использовании queue. Все сказанное, это все еще только (очень) образованное предположение: вам придется профилировать этот код, используя deque в одном тесте и list в другом, чтобы действительно знать наверняка.

Но помните: получите код, работающий с чистым интерфейсом, а затем беспокоитесь о производительности.

Джон обеспокоен тем, что упаковка list или deque приведет к снижению производительности. Еще раз, он и я не можем сказать наверняка, не профилируя его сами, но есть вероятность, что компилятор будет выстраивать вызовы, которые делает queue. То есть, когда вы скажете queue.push(), он просто скажет queue.container.push_back(), полностью пропустив вызов функции.

Еще раз, это только обоснованное предположение, но использование queue не приведет к ухудшению производительности по сравнению с использованием базового контейнера raw. Как я уже говорил, используйте queue, потому что он чист, прост в использовании и безопасен, и если он действительно становится профилем проблемы и тестом.

Ответ 2

Проверьте std:: queue. Он обертывает базовый тип контейнера, а контейнер по умолчанию - std:: deque.

Ответ 3

Если производительность действительно важна, проверьте Boost round buffer library.

Ответ 4

Я постоянно push_back новые элементы а pop_front самый старый элемент (около миллиона раз).

Миллион действительно не является большим числом компьютеров. Как предложили другие, используйте std::queue в качестве своего первого решения. В маловероятном случае, когда он будет слишком медленным, определите узкое место с использованием профилировщика (не угадайте!) И повторите реализацию с использованием другого контейнера с тем же интерфейсом.

Ответ 5

Почему бы не std::queue? Все, что у него есть: push_back и pop_front.

Ответ 6

A queue, вероятно, более простой интерфейс, чем deque, но для такого небольшого списка разница в производительности, вероятно, незначительна.

То же самое касается list. Это просто выбор того, какой API вы хотите.