2009-08-11 5 views
65

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

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

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

+5

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

+2

Я собирался это сделать, но я тоже искал теоретический ответ. –

+1

'std :: deque' не перераспределяется. Это гибрид 'std :: list' и' std :: vector', где он выделяет более крупные куски, чем 'std :: list', но не будет перераспределяться, как' std :: vector'. –

ответ

151

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

Используйте 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, с другой стороны, выделяет куски. Он будет выделять реже, чем list. Подумайте об этом как о списке, но каждый кусок памяти может содержать несколько узлов. (Конечно, я бы посоветовал вам really learn how it works.)

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

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

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

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

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

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

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

+8

+1 - и если окажется, что boost :: circle_buffer <> имеет лучшую производительность, просто используйте это как основной контейнер (он также предоставляет требуемые push_back(), pop_front(), front() и back())). –

+3

+1 красиво объяснено –

+2

Принято для объяснения этого в деталях (это то, что мне нужно, спасибо, что нашли время). Что касается хорошего предыдущего кода, я должен признать, что это один из моих самых больших дефолтов, я всегда стараюсь делать что-то отлично при первом запуске ... Я действительно написал код, используя deque, который был первым жестким, но поскольку вещь была не " (как предполагается, почти в реальном времени), я догадался, что должен немного улучшить его. Как сказал Нейл, я действительно должен был использовать профайлер, хотя ... Хотя я счастлив, что сделал эти ошибки сейчас, пока это не имеет большого значения. Большое спасибо всем вам. –

3

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

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

+0

Но мне было интересно, если константа push_back делает очередь или deque перераспределять себя. –

+0

std :: queue - это оболочка вокруг другого контейнера, поэтому очередь, конвертирующая deque, будет менее эффективной, чем необработанный deque. –

+1

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

26

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

+0

Я сомневаюсь, что упаковка deque с очередью будет иметь лучшие характеристики производительности, чем просто использование deque. –

+0

Вы думаете, что это будет хуже? Дело в том, что очередь - это структура FIFO для использования в C++. Он обеспечивает правильный интерфейс, и давайте вам выбрать, к какому контейнеру он адаптируется, поэтому, если список заканчивается лучше, это так же просто, как сказать «использовать список», и никаких других изменений кода. [http://www.cplusplus.com/reference/stl/queue/] – GManNickG

+0

Точка, которую я хотел сделать с моим первым вопросом, была: вы не знаете. Вы можете догадаться, но вряд ли вы увидите разницу в производительности. Inlining удалит «оболочку», поэтому она * нравится *, используя «list», «deque» или что-то еще, с точки зрения производительности, но на самом деле «оболочка» гарантирует правильную структуру структуры. – GManNickG

5

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

6

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

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

+1

Ну, дело в том, что это большое количество, поскольку то, что я хочу сделать, должно быть в реальном времени. Хотя вы правы, что я должен был использовать профилировщик, чтобы определить причину ... –

+0

Дело в том, что я действительно не привык использовать профилировщик (мы использовали gprof немного в одном из наших классов, но мы действительно не углублялись ...). Если вы может указать мне на некоторые ресурсы, я бы очень признателен! PS. Я использую VS2008 –

+0

@Gab: Какой у VS2008 у вас есть (Express, Pro ...)? Некоторые из них поставляются с профилировщиком. – sbi

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