Какой контейнер STL следует использовать для FIFO?
какой контейнер STL лучше всего подойдет моим потребностям? У меня в основном есть 10 элементов широкий контейнер, в котором я постоянно push_back
новые элементы, в то время как pop_front
ing самый старый элемент (около миллиона раз).
в настоящее время я использую std::deque
для задачи, но было интересно, если std::list
было бы более эффективно, так как мне не нужно было бы перераспределять себя (или, может быть, я ошибаюсь std::deque
на std::vector
?). Или есть еще более эффективный контейнер для моего нужно?
P. S. Мне не нужен случайный доступ к
6 ответов
поскольку существует множество ответов, вы можете быть смущены, но подытожить:
использовать std::queue
. Причина этого проста: это структура FIFO. Вы хотите FIFO, вы используете std::queue
.
это делает ваше намерение ясным для кого-либо еще, и даже для себя. А std::list
или std::deque
нет. Список может вставлять и удалять в любом месте, что не является тем, что должна делать структура FIFO, и deque
добавить и удалите с обоих концов, что также не может сделать структура FIFO.
вот почему вы должны использовать queue
.
Итак, вы спросили о производительности. Во-первых, всегда помните это важное правило: хороший код, во-первых, производительность последнего.
причина этого проста: люди которые стремятся для представления перед чистотой и элегантностью почти всегда заканчивают последнее. Их код превращается в кашу, потому что они отказался от всего хорошего, чтобы действительно ничего из этого не получить.
написав хороший, читаемый код, большинство из вас проблемы с производительностью решат сами. И если позже вы обнаружите, что ваша производительность отсутствует, теперь легко добавить профилировщик в ваш хороший, чистый код и узнать, где проблема.
вот и все сказано,std::queue
Это только адаптер. Она обеспечивает безопасный интерфейс, но использует другой контейнер внутри. Вы можете выбрать эту основу контейнер, и это позволяет много гибкости.
Итак, какой базовый контейнер вы должны использовать? Мы знаем, что std::list
и std::deque
обеспечивают необходимые функции (push_back()
, pop_front()
и front()
), так как мы решим?
во-первых, поймите, что выделение (и освобождение) памяти-это не быстрая вещь, потому что она включает в себя выход в ОС и просить ее что-то сделать. А list
нужно выделять память каждый раз что-то добавляется и освобождается, когда оно уходит.
A deque
, С другой стороны, выделяет в куски. Он будет выделять реже, чем list
. Думайте об этом как о списке, но каждый кусок памяти может содержать несколько узлов. (Конечно, я бы посоветовал вам действительно узнать, как это работает.)
Итак, с этим однимdeque
должен работать лучше, потому что он не имеет дело с памятью так часто. Смешанный с фактом, что вы обрабатываете данные константы размер, вероятно, не придется выделять после первого прохода через данные, в то время как список будет постоянно выделять и освобождать.
второе, что нужно понять производительность кэша. Выход в ОЗУ медленный, поэтому, когда CPU действительно нужно, он делает лучшее из этого времени, взяв с собой кусок памяти обратно в кэш. Потому что deque
выделяет в блоках памяти, вероятно, что доступ к элементу в этом контейнере вызовет CPU, чтобы вернуть остальную часть контейнера. Теперь любой дальнейший доступ к deque
будет быстрым, потому что данные находятся в кэше.
это не похоже на список, где данные выделяются по одному за раз. Это означает, что данные могут быть распределены по всему месту в памяти, а производительность кэша будет плохой.
Итак, учитывая это, a deque
должно быть лучшим выбором. Вот почему это контейнер по умолчанию при использовании queue
. Что все сказанное, это все еще только (очень) образованная догадка: вам придется профилировать этот код, используя deque
в одном тесте и list
в другом, чтобы действительно знать наверняка.
но помните: получите код, работающий с чистым интерфейсом, а затем беспокоиться о производительности.
Джон поднимает озабоченность тем, что обертывание list
или deque
приведет к снижению производительности. Еще раз, ни он, ни я не можем сказать наверняка, не профилируя его сами, но есть вероятность, что компилятор будет встроен звонки, которые queue
делает. То есть, когда вы говорите queue.push()
, это очень просто сказать queue.container.push_back()
, пропуская вызов функции полностью.
еще раз повторюсь, это только догадка, но с помощью queue
не ухудшит производительность по сравнению с использованием базового контейнера raw. Как я уже говорил, используйте queue
, потому что он чистый, простой в использовании и безопасный, и если он действительно становится проблемным профилем и тестом.
когда производительность действительно имеет значение, проверьте увеличить круговую буферную библиотеку.
Я постоянно
push_back
новые элементы в то время какpop_front
ing самый старый элемент (примерно миллион раз).
миллион на самом деле не большое число в вычислениях. Как предлагали другие, используйте std::queue
Как ваше первое решение. В маловероятном случае, если он будет слишком медленным, определите узкое место с помощью профилировщика (не угадайте!) и повторно реализовать с помощью другого контейнера с тем же интерфейсом.