Qual contêiner STL atenderia melhor às minhas necessidades? Eu basicamente tenho um contêiner de 10 elementos de largura no qual eu continuamente push_back
novos elementos enquanto pop_front
ingiro o elemento mais antigo (cerca de um milhão de vezes).
No momento, estou usando um std::deque
para a tarefa, mas std::list
gostaria de saber se a seria mais eficiente, já que não precisaria se realocar (ou talvez eu esteja confundindo a std::deque
com a std::vector
?). Ou existe um contêiner ainda mais eficiente para minha necessidade?
PS Eu não preciso de acesso aleatório
std::deque
não será realocado. É um híbrido de astd::list
e a,std::vector
onde aloca pedaços maiores do que a,std::list
mas não realoca como astd::vector
.Respostas:
Como há uma infinidade de respostas, você pode ficar confuso, mas para resumir:
Use um
std::queue
. A razão para isso é simples: é uma estrutura FIFO. Você quer FIFO, você usa umstd::queue
.Ele deixa sua intenção clara para qualquer outra pessoa, e até para você mesmo. A
std::list
oustd::deque
não. Uma lista pode inserir e remover em qualquer lugar, o que não é o que uma estrutura FIFO deve fazer, e umdeque
pode adicionar e remover de qualquer uma das extremidades, o que também é algo que uma estrutura FIFO não pode fazer.É por isso que você deve usar a
queue
.Agora, você perguntou sobre o desempenho. Em primeiro lugar, lembre-se sempre desta importante regra prática : Bom código primeiro, desempenho por último.
A razão para isso é simples: pessoas que buscam desempenho antes da limpeza e elegância quase sempre terminam por último. O código deles se torna uma massa de mingau, porque eles abandonaram tudo o que era bom para realmente não conseguir nada com isso.
Escrevendo um código bom e legível primeiro, a maioria dos problemas de desempenho se resolverá por si mesmos. E se mais tarde você descobrir que seu desempenho está deficiente, agora é fácil adicionar um criador de perfil ao seu código bom e limpo e descobrir onde está o problema.
Dito isso,
std::queue
é apenas um adaptador. Ele fornece a interface segura, mas usa um contêiner diferente no interior. Você pode escolher esse contêiner subjacente e isso permite uma boa dose de flexibilidade.Então, qual contêiner subjacente você deve usar? Sabemos que
std::list
estd::deque
ambos fornecem as funções necessárias (push_back()
,pop_front()
efront()
), então como é que vamos decidir?Em primeiro lugar, entenda que alocar (e desalocar) memória não é algo rápido de se fazer, geralmente, porque envolve ir ao SO e pedir que ele faça algo. A
list
precisa alocar memória toda vez que algo é adicionado e desalocá-lo quando ele for removido.A
deque
, por outro lado, aloca em blocos. Ele alocará com menos frequência do que alist
. Pense nisso como uma lista, mas cada bloco de memória pode conter vários nós. (Claro, sugiro que você realmente aprenda como funciona .)Então, só com isso um
deque
deveria ter um desempenho melhor, porque não lida com a memória com tanta frequência. Combinado com o fato de você estar manipulando dados de tamanho constante, provavelmente não será necessário alocar após a primeira passagem pelos dados, enquanto uma lista estará constantemente alocando e desalocando.Uma segunda coisa a entender é o desempenho do cache . Ir para a RAM é lento, então quando a CPU realmente precisa, ela tira o melhor proveito desse tempo, levando um pedaço de memória de volta para o cache. Como um
deque
aloca em pedaços de memória, é provável que acessar um elemento neste contêiner fará com que a CPU traga de volta o resto do contêiner também. Agora, qualquer outro acesso aodeque
será rápido, porque os dados estão no cache.Isso é diferente de uma lista, onde os dados são alocados um de cada vez. Isso significa que os dados podem ser espalhados por todo o lugar na memória, e o desempenho do cache será ruim.
Portanto, considerando isso, a
deque
deve ser uma escolha melhor. É por isso que é o contêiner padrão ao usar umqueue
. Dito isso, este ainda é apenas um palpite (muito) educado: você terá que traçar o perfil deste código, usando umdeque
em um teste elist
no outro para realmente saber com certeza.Mas lembre-se: faça o código funcionar com uma interface limpa e se preocupe com o desempenho.
John levanta a preocupação de que envolver um
list
oudeque
causará uma diminuição no desempenho. Mais uma vez, ele e eu podemos dizer com certeza sem criar o perfil, mas as chances são de que o compilador irá embutir as chamadas que elequeue
faz. Ou seja, quando você dizqueue.push()
, ele realmente apenas dizqueue.container.push_back()
, ignorando completamente a chamada de função.Mais uma vez, essa é apenas uma suposição educada, mas usar um
queue
não prejudicará o desempenho, quando comparado ao uso do contêiner básico bruto. Como eu disse antes, use oqueue
, porque é limpo, fácil de usar e seguro, e se realmente se torna um problema, perfil e teste.fonte
Confira
std::queue
. Ele envolve um tipo de contêiner subjacente e o contêiner padrão éstd::deque
.fonte
queue
é esse tipo. Bom código primeiro, desempenho depois. Inferno, a maior parte do desempenho vem do uso de um bom código em primeiro lugar.queue
fornece a segurança não aumentaria o desempenho, como venho dizendo. Você sugeriu umlist
, que provavelmente terá um desempenho pior.Onde o desempenho realmente importa, verifique a biblioteca de buffer circular Boost .
fonte
Um milhão não é realmente um número grande na computação. Como outros sugeriram, use a
std::queue
como sua primeira solução. No caso improvável de ser muito lento, identifique o gargalo usando um criador de perfil (não adivinhe!) E reimplemente usando um contêiner diferente com a mesma interface.fonte
Porque não
std::queue
? Tudo o que tem épush_back
epop_front
.fonte
Uma fila é provavelmente uma interface mais simples do que um deque, mas para uma lista tão pequena, a diferença no desempenho é provavelmente insignificante.
O mesmo vale para a lista . É apenas uma questão de escolha de qual API você deseja.
fonte
Use a
std::queue
, mas esteja ciente das compensações de desempenho das duasContainer
classes padrão .Por padrão,
std::queue
é um adaptador na parte superiorstd::deque
. Normalmente, isso dará um bom desempenho quando você tiver um pequeno número de filas contendo um grande número de entradas, o que é indiscutivelmente o caso comum.No entanto, não se preocupe com a implementação de std :: deque . Especificamente:
"... deques normalmente têm um grande custo mínimo de memória; um deque contendo apenas um elemento deve alocar seu array interno completo (por exemplo, 8 vezes o tamanho do objeto em libstdc ++ de 64 bits; 16 vezes o tamanho do objeto ou 4096 bytes, o que for maior , em libc ++ de 64 bits). "
Para compensar isso, presumindo que uma entrada de fila seja algo que você gostaria de enfileirar, ou seja, de tamanho razoavelmente pequeno, então se você tiver 4 filas, cada uma contendo 30.000 entradas, a
std::deque
implementação será a opção de escolha. Por outro lado, se você tiver 30.000 filas, cada uma contendo 4 entradas, é mais do que provável que astd::list
implementação seja ótima, pois você nunca amortizará astd::deque
sobrecarga nesse cenário.Você lerá muitas opiniões sobre como o cache é rei, como Stroustrup odeia listas vinculadas, etc., e tudo isso é verdade, sob certas condições. Apenas não aceite com fé cega, porque em nosso segundo cenário, é bastante improvável que a
std::deque
implementação padrão funcione. Avalie seu uso e meça.fonte
Este caso é simples o suficiente para que você possa escrever o seu próprio. Aqui está algo que funciona bem para situações de microcontrolador em que o uso de STL ocupa muito espaço. É uma boa maneira de passar dados e sinais do manipulador de interrupções para o loop principal.
fonte