A implementação do Visual C ++ de std::dequetem um tamanho de bloco máximo muito pequeno (~ 16 bytes, se bem me lembro; talvez 32) e, como tal, não funciona muito bem para aplicativos realistas. A deque<T>where sizeof(T) > 8(ou 16? É um número pequeno) tem aproximadamente as mesmas características de desempenho que a vector<T*>, onde cada elemento é alocado dinamicamente. Outras implementações têm tamanhos máximos de bloco diferentes, portanto, é difícil escrever código que tenha relativamente as mesmas características de desempenho em plataformas diferentes deque.
James McNellis
12
Deque não é um contêiner de memória contínua.
Mohamed El-Nakib,
@ravil Não, essa é a duplicata, apontando para esta questão.
1
difícil de acreditar, uma pergunta com um erro factual flagrante não corrigido estava em um saldo de 34 votos
sublinhado_d
2
@underscore_d é por isso que é uma pergunta. História diferente se fosse uma resposta;)
Assimilador
Respostas:
115
Os elementos em a nãodeque são contíguos na memória; elementos têm a garantia de ser. Portanto, se você precisa interagir com uma biblioteca C simples que precisa de matrizes contíguas, ou se você se preocupa (muito) com a localidade espacial, talvez prefira . Além disso, como há alguma contabilidade extra, outras operações são provavelmente (ligeiramente) mais caras do que suas operações equivalentes . Por outro lado, o uso de muitas / grandes instâncias de pode levar à fragmentação desnecessária do heap (desacelerando as chamadas para ).vectorvectorvectorvectornew
Parece que o link para "outro lugar" agora está morto (devido à moderação?).
esilk
37
Para saber a diferença, é preciso saber como dequegeralmente é implementado. A memória é alocada em blocos de tamanhos iguais e eles são encadeados (como uma matriz ou possivelmente um vetor).
Portanto, para encontrar o enésimo elemento, você encontra o bloco apropriado e acessa o elemento dentro dele. Este é um tempo constante, porque é sempre exatamente 2 pesquisas, mas ainda é mais do que o vetor.
vectortambém funciona bem com APIs que desejam um buffer contíguo porque são APIs C ou são mais versáteis ao serem capazes de pegar um ponteiro e um comprimento. (Assim, você pode ter um vetor embaixo ou um array regular e chamar a API do seu bloco de memória).
Onde dequetem suas maiores vantagens são:
Ao aumentar ou diminuir a coleção de qualquer extremidade
Quando você está lidando com tamanhos de coleção muito grandes.
Ao lidar com bools e você realmente quer bools em vez de um bitset.
O segundo deles é menos conhecido, mas para tamanhos de coleção muito grandes:
O custo de realocação é grande
A sobrecarga de ter que encontrar um bloco de memória contíguo é restritiva, então você pode ficar sem memória mais rápido.
Quando eu estava lidando com grandes coleções no passado e mudei de um modelo contíguo para um modelo de bloco, fomos capazes de armazenar uma coleção cerca de 5 vezes maior antes de ficar sem memória em um sistema de 32 bits. Isso ocorre em parte porque, ao realocar, ele realmente precisava armazenar o bloco antigo e também o novo antes de copiar os elementos.
Tendo dito tudo isso, você pode ter problemas com std::dequesistemas que usam alocação de memória "otimista". Embora suas tentativas de solicitar um tamanho de buffer grande para uma realocação de a vectorprovavelmente sejam rejeitadas em algum ponto com a bad_alloc, a natureza otimista do alocador provavelmente sempre concederá a solicitação de um buffer menor solicitado por a dequee isso provavelmente causará o sistema operacional para matar um processo para tentar adquirir alguma memória. Qualquer que seja escolhido pode não ser muito agradável.
As soluções alternativas nesse caso são definir sinalizadores no nível do sistema para substituir a alocação otimista (nem sempre viável) ou gerenciar a memória um pouco mais manualmente, por exemplo, usando seu próprio alocador que verifica o uso de memória ou similar. Obviamente não é o ideal. (O que pode responder à sua pergunta quanto à preferência pelo vetor ...)
Eu implementei vetor e deque várias vezes. deque é muito mais complicado do ponto de vista da implementação. Essa complicação se traduz em mais código e código mais complexo. Portanto, você normalmente verá um tamanho de código ao escolher deque em vez de vetor. Você também pode experimentar um pequeno golpe de velocidade se seu código usar apenas as coisas em que o vetor se destaca (ou seja, push_back).
Se você precisa de uma fila dupla, deque é o vencedor. Mas se você estiver fazendo a maioria das inserções e apagamentos no verso, o vetor será o vencedor claro. Quando você não tiver certeza, declare seu contêiner com um typedef (para que seja fácil alternar para frente e para trás) e meça.
Pergunta - o comitê considerou adicionar um híbrido dos dois (digamos, um "deck") ao C ++? (ou seja, um duplo finalizado vector.) Eu escrevi uma implementação vinculada a seguir em minha resposta . Pode ser tão rápido quanto um, vectormas muito mais amplamente aplicável (por exemplo, ao criar uma fila rápida).
user541686
5
std::dequenão tem memória contínua garantida - e geralmente é um pouco mais lento para acesso indexado. Um deque é tipicamente implementado como uma "lista de vetores".
Não acho que uma "lista de vetores" esteja correta: meu entendimento é que a maioria das implementações eram um "vetor de ponteiros para matrizes", embora dependa da sua definição de "lista" (eu li "lista" como "lista vinculada , "que não atenderia aos requisitos de complexidade.)
James McNellis
2
De acordo com http://www.cplusplus.com/reference/stl/deque/ , "ao contrário dos vetores, não é garantido que os deques tenham todos os seus elementos em locais de armazenamento contíguos, eliminando assim a possibilidade de acesso seguro através da aritmética de ponteiros."
Deques são um pouco mais complicados, em parte porque eles não têm necessariamente um layout de memória contíguo. Se você precisar desse recurso, não deve usar um deque.
(Anteriormente, minha resposta levantou uma falta de padronização (da mesma fonte acima, "deques pode ser implementado por bibliotecas específicas de maneiras diferentes"), mas isso na verdade se aplica a praticamente qualquer tipo de dados de biblioteca padrão.)
std::dequenão é menos padronizado do que std::vector. Não acredito que os requisitos de complexidade para std::dequepossam ser atendidos com armazenamento contíguo.
Jerry Coffin
1
Talvez minha formulação tenha sido pobre: embora seja verdade que a padronização não é completa, pelo que entendi, os vetores são padronizados para serem uma sequência conígua, e os deques não. Esse parece ser o fator decisivo.
patrickvacek
1
@JerryCoffin: Quais requisitos de complexidade dequenão podem ser atendidos com armazenamento contíguo?
user541686
1
@Mehrdad: Para ser honesto, não me lembro do que tinha em mente. Eu não olhei essa parte do padrão recentemente o suficiente para me sentir confortável declarando categoricamente que meu comentário anterior estava errado, mas olhando para ele agora, eu não consigo pensar em como seria certo também.
Jerry Coffin
3
@JerryCoffin: Os requisitos de complexidade são realmente triviais: você pode alocar um grande array e começar a empurrar sua sequência do meio para fora (acho que é isso que a implementação de Mehrdad faz) e, em seguida, realocar quando chegar ao fim. O problema com esta abordagem é que ela não satisfaz um dos requisitos de deque, ou seja, que a inserção nas extremidades não deve invalidar os referenciados aos elementos existentes. Este requisito implica memória descontínua.
Yakov Galka
0
Um deque é um contêiner de sequência que permite acesso aleatório aos seus elementos, mas não é garantido que tenha armazenamento contíguo.
A questão, se algum pode ser destilado entre os erros factuais e palavras que faltam, era por que alguém preferiria vector. Podemos inferir que porque não é um corolário. Dizer que você prefere deque, por razões desconhecidas, a partir de testes não especificados, não é uma resposta.
Por um lado, o vetor é freqüentemente muito mais rápido do que o deque. Se você realmente não precisa de todos os recursos do deque, use um vetor.
Por outro lado, às vezes você fazer características necessidades que vector não lhe dá, caso em que você deve usar um deque. Por exemplo, eu desafio qualquer um a tentar reescrever esse código , sem usar um deque e sem alterar enormemente o algoritmo.
Na verdade, na mesma série de operações push_backe pop_back, deque<int>é sempre pelo menos 20% mais rápido que vector<int>nos meus testes (gcc com O3). Acho que é por isso que dequeé a escolha padrão para coisas como std::stack...
igel
-1
Observe que a memória vetorial é realocada à medida que o array cresce. Se você tiver ponteiros para elementos do vetor, eles se tornarão inválidos.
Além disso, se você apagar um elemento, os iteradores se tornam inválidos (mas não "para (auto ...)").
std::deque
tem um tamanho de bloco máximo muito pequeno (~ 16 bytes, se bem me lembro; talvez 32) e, como tal, não funciona muito bem para aplicativos realistas. Adeque<T>
wheresizeof(T) > 8
(ou 16? É um número pequeno) tem aproximadamente as mesmas características de desempenho que avector<T*>
, onde cada elemento é alocado dinamicamente. Outras implementações têm tamanhos máximos de bloco diferentes, portanto, é difícil escrever código que tenha relativamente as mesmas características de desempenho em plataformas diferentesdeque
.Respostas:
Os elementos em a não
deque
são contíguos na memória; elementos têm a garantia de ser. Portanto, se você precisa interagir com uma biblioteca C simples que precisa de matrizes contíguas, ou se você se preocupa (muito) com a localidade espacial, talvez prefira . Além disso, como há alguma contabilidade extra, outras operações são provavelmente (ligeiramente) mais caras do que suas operações equivalentes . Por outro lado, o uso de muitas / grandes instâncias de pode levar à fragmentação desnecessária do heap (desacelerando as chamadas para ).vector
vector
vector
vector
new
Além disso, conforme apontado em outro lugar no StackOverflow , há mais boas discussões aqui: http://www.gotw.ca/gotw/054.htm .
fonte
Para saber a diferença, é preciso saber como
deque
geralmente é implementado. A memória é alocada em blocos de tamanhos iguais e eles são encadeados (como uma matriz ou possivelmente um vetor).Portanto, para encontrar o enésimo elemento, você encontra o bloco apropriado e acessa o elemento dentro dele. Este é um tempo constante, porque é sempre exatamente 2 pesquisas, mas ainda é mais do que o vetor.
vector
também funciona bem com APIs que desejam um buffer contíguo porque são APIs C ou são mais versáteis ao serem capazes de pegar um ponteiro e um comprimento. (Assim, você pode ter um vetor embaixo ou um array regular e chamar a API do seu bloco de memória).Onde
deque
tem suas maiores vantagens são:O segundo deles é menos conhecido, mas para tamanhos de coleção muito grandes:
Quando eu estava lidando com grandes coleções no passado e mudei de um modelo contíguo para um modelo de bloco, fomos capazes de armazenar uma coleção cerca de 5 vezes maior antes de ficar sem memória em um sistema de 32 bits. Isso ocorre em parte porque, ao realocar, ele realmente precisava armazenar o bloco antigo e também o novo antes de copiar os elementos.
Tendo dito tudo isso, você pode ter problemas com
std::deque
sistemas que usam alocação de memória "otimista". Embora suas tentativas de solicitar um tamanho de buffer grande para uma realocação de avector
provavelmente sejam rejeitadas em algum ponto com abad_alloc
, a natureza otimista do alocador provavelmente sempre concederá a solicitação de um buffer menor solicitado por adeque
e isso provavelmente causará o sistema operacional para matar um processo para tentar adquirir alguma memória. Qualquer que seja escolhido pode não ser muito agradável.As soluções alternativas nesse caso são definir sinalizadores no nível do sistema para substituir a alocação otimista (nem sempre viável) ou gerenciar a memória um pouco mais manualmente, por exemplo, usando seu próprio alocador que verifica o uso de memória ou similar. Obviamente não é o ideal. (O que pode responder à sua pergunta quanto à preferência pelo vetor ...)
fonte
Eu implementei vetor e deque várias vezes. deque é muito mais complicado do ponto de vista da implementação. Essa complicação se traduz em mais código e código mais complexo. Portanto, você normalmente verá um tamanho de código ao escolher deque em vez de vetor. Você também pode experimentar um pequeno golpe de velocidade se seu código usar apenas as coisas em que o vetor se destaca (ou seja, push_back).
Se você precisa de uma fila dupla, deque é o vencedor. Mas se você estiver fazendo a maioria das inserções e apagamentos no verso, o vetor será o vencedor claro. Quando você não tiver certeza, declare seu contêiner com um typedef (para que seja fácil alternar para frente e para trás) e meça.
fonte
vector
.) Eu escrevi uma implementação vinculada a seguir em minha resposta . Pode ser tão rápido quanto um,vector
mas muito mais amplamente aplicável (por exemplo, ao criar uma fila rápida).std::deque
não tem memória contínua garantida - e geralmente é um pouco mais lento para acesso indexado. Um deque é tipicamente implementado como uma "lista de vetores".fonte
De acordo com http://www.cplusplus.com/reference/stl/deque/ , "ao contrário dos vetores, não é garantido que os deques tenham todos os seus elementos em locais de armazenamento contíguos, eliminando assim a possibilidade de acesso seguro através da aritmética de ponteiros."
Deques são um pouco mais complicados, em parte porque eles não têm necessariamente um layout de memória contíguo. Se você precisar desse recurso, não deve usar um deque.
(Anteriormente, minha resposta levantou uma falta de padronização (da mesma fonte acima, "deques pode ser implementado por bibliotecas específicas de maneiras diferentes"), mas isso na verdade se aplica a praticamente qualquer tipo de dados de biblioteca padrão.)
fonte
std::deque
não é menos padronizado do questd::vector
. Não acredito que os requisitos de complexidade parastd::deque
possam ser atendidos com armazenamento contíguo.deque
não podem ser atendidos com armazenamento contíguo?deque
, ou seja, que a inserção nas extremidades não deve invalidar os referenciados aos elementos existentes. Este requisito implica memória descontínua.Um deque é um contêiner de sequência que permite acesso aleatório aos seus elementos, mas não é garantido que tenha armazenamento contíguo.
fonte
Acho boa ideia fazer teste de desempenho de cada case. E tome a decisão confiando nestes testes.
Eu prefiro do
std::deque
questd::vector
na maioria dos casos.fonte
vector
. Podemos inferir que porque não é um corolário. Dizer que você preferedeque
, por razões desconhecidas, a partir de testes não especificados, não é uma resposta.Você não preferiria que o vetor deque de acordo com os resultados do teste (com a fonte).
Claro, você deve testar em seu aplicativo / ambiente, mas em resumo:
Mais algumas reflexões e uma nota a considerar circular_buffer.
fonte
Por um lado, o vetor é freqüentemente muito mais rápido do que o deque. Se você realmente não precisa de todos os recursos do deque, use um vetor.
Por outro lado, às vezes você fazer características necessidades que vector não lhe dá, caso em que você deve usar um deque. Por exemplo, eu desafio qualquer um a tentar reescrever esse código , sem usar um deque e sem alterar enormemente o algoritmo.
fonte
push_back
epop_back
,deque<int>
é sempre pelo menos 20% mais rápido quevector<int>
nos meus testes (gcc com O3). Acho que é por isso quedeque
é a escolha padrão para coisas comostd::stack
...Observe que a memória vetorial é realocada à medida que o array cresce. Se você tiver ponteiros para elementos do vetor, eles se tornarão inválidos.
Além disso, se você apagar um elemento, os iteradores se tornam inválidos (mas não "para (auto ...)").
Editar: mudou 'deque' para 'vetor'
fonte