Usando matrizes ou std :: vectors em C ++, qual é a diferença de desempenho?
208
Em nosso curso de C ++, eles sugerem não usar mais matrizes de C ++ em novos projetos. Tanto quanto sei, o próprio Stroustroup sugere não usar matrizes. Mas existem diferenças significativas de desempenho?
Por que você acha que existe uma lacuna de desempenho.
Martin York
99
Porque geralmente com melhor funcionalidade vem o pior desempenho.
tunnuz
19
Concordo com a otimização prematura, mas escolher o melhor método de armazenamento antecipadamente faz muito sentido. Geralmente, no mundo real, o código precisa ser enviado e o próximo produto desenvolvido e a etapa de otimização nunca acontece.
Ant
132
eu gostaria que as pessoas parassem de gritar "otimização prematura!" sempre que alguém fizer uma pergunta simples relacionada ao desempenho! responda à pergunta e não presuma apenas prematuramente que as pessoas estejam fazendo algo prematuramente.
D7samurai 22/05
4
@ d7samaurai: concordo, eu ainda tenho que ver alguém tente usarint main(int argc, const std::vector<string>& argv)
Mark K Cowan
Respostas:
189
O uso de matrizes C ++ com new(ou seja, matrizes dinâmicas) deve ser evitado. Existe o problema de manter o controle do tamanho e excluí-los manualmente e fazer todo o tipo de tarefas domésticas.
O uso de matrizes na pilha também é desencorajado, porque você não tem verificação de alcance, e passar a matriz perderá qualquer informação sobre seu tamanho (conversão de matriz em ponteiro). boost::arrayNesse caso, você deve usar , que agrupa uma matriz C ++ em uma classe pequena e fornece uma sizefunção e iteradores para iterar sobre ela.
Agora as matrizes std :: vector vs. C ++ nativas (extraídas da internet):
// Comparison of assembly code generated for basic indexing, dereferencing, // and increment operations on vectors and arrays/pointers.// Assembly code was generated by gcc 4.1.0 invoked with g++ -O3 -S on a // x86_64-suse-linux machine.#include<vector>struct S
{int padding;
std::vector<int> v;int* p;
std::vector<int>::iterator i;};int pointer_index (S & s){return s.p[3];}// movq 32(%rdi), %rax// movl 12(%rax), %eax// retint vector_index (S & s){return s.v[3];}// movq 8(%rdi), %rax// movl 12(%rax), %eax// ret// Conclusion: Indexing a vector is the same damn thing as indexing a pointer.int pointer_deref (S & s){return*s.p;}// movq 32(%rdi), %rax// movl (%rax), %eax// retint iterator_deref (S & s){return*s.i;}// movq 40(%rdi), %rax// movl (%rax), %eax// ret// Conclusion: Dereferencing a vector iterator is the same damn thing // as dereferencing a pointer.void pointer_increment (S & s){++s.p;}// addq $4, 32(%rdi)// retvoid iterator_increment (S & s){++s.i;}// addq $4, 40(%rdi)// ret// Conclusion: Incrementing a vector iterator is the same damn thing as // incrementing a pointer.
Nota: Se você alocar matrizes com newobjetos não pertencentes a classe (como simples int) ou classes sem um construtor definido pelo usuário e não desejar inicializar seus elementos inicialmente, o uso de newmatrizes alocadas poderá ter vantagens de desempenho, pois std::vectorinicializa todos os elementos para valores padrão (0 para int, por exemplo) na construção (créditos para @bernie por me lembrar).
Quem inventou a maldita sintaxe da AT&T? Só se eu soubesse ... :)
Mehrdad Afshari
4
Isso não é verdade para o compilador Visual C ++. Mas para o GCC é.
Toto
5
O ponto na minha resposta é que o vetor não precisa ser mais lento que as operações correspondentes do ponteiro. Claro, ele pode ser (fácil de conseguir, permitindo activar o modo de depuração, também) :)
Johannes Schaub - litb
18
+1 para "Indexar um vetor é a mesma coisa que indexar um ponteiro". e para as outras conclusões também.
Nawaz
3
@ Piotr99 Não vou discutir com você, mas quando você aprende assembly após aprender linguagens de nível superior, a sintaxe da Intel faz muito mais sentido do que algumas versões anteriores, prefixadas (números), sufixos (instruções) e obscuras (acessando a memória ) natureza da sintaxe da AT&T.
Cole Johnson
73
Preâmbulo para pessoas com micro-otimizadores
Lembrar:
"Os programadores perdem muito tempo pensando ou se preocupando com a velocidade das partes não críticas de seus programas, e essas tentativas de eficiência realmente têm um forte impacto negativo quando a depuração e a manutenção são consideradas. Devemos esquecer as pequenas eficiências, por exemplo, sobre 97% das vezes: a otimização prematura é a raiz de todo mal. No entanto, não devemos desperdiçar nossas oportunidades nesses 3% críticos ".
Não use uma matriz C em vez de um vetor (ou o que seja) apenas porque você acredita que é mais rápido, pois deveria ser de nível inferior. Você estaria errado.
Use por vetor padrão (ou o contêiner seguro adaptado às suas necessidades) e, se o seu profiler disser que é um problema, veja se você pode otimizá-lo, usando um algoritmo melhor ou alterando o contêiner.
Dito isto, podemos voltar à pergunta original.
Matriz estática / dinâmica?
As classes de matriz C ++ são mais bem comportadas do que a matriz C de baixo nível, porque sabem muito sobre si mesmas e podem responder a perguntas que as matrizes C não podem. Eles são capazes de limpar depois de si mesmos. E o mais importante, eles geralmente são escritos usando modelos e / ou inlining, o que significa que o que parece muito código na depuração resolve pouco ou nenhum código produzido na criação da versão, o que significa que não há diferença com a concorrência menos segura interna.
Ao todo, ele se enquadra em duas categorias:
Matrizes dinâmicas
Usar um ponteiro para uma matriz malloc-ed / new-ed será tão rápido quanto a versão std :: vector e muito menos seguro (consulte a publicação do litb ).
Então use um std :: vector.
Matrizes estáticas
Usar uma matriz estática será, na melhor das hipóteses:
Às vezes, usar um vectorbuffer em vez de um buffer bruto gera um custo visível, porque o vectorinicializará o buffer na construção, enquanto o código que ele substitui não, como observou Bernie em sua resposta .
Se esse for o caso, você poderá lidar com isso usando um em unique_ptrvez de a vectorou, se o caso não for excepcional em sua linha de código, escreva uma classe buffer_ownerque seja a proprietária dessa memória e ofereça acesso fácil e seguro a ela, incluindo bônus como redimensioná-lo (usando realloc?) ou o que você precisar.
Obrigado por abordar matrizes estáticas também - std :: vector é inútil se você não tiver permissão para alocar memória dinamicamente por motivos de desempenho.
Tom
10
Quando você diz "Usar uma matriz estática será, na melhor das hipóteses, tão rápido quanto a versão boost :: array", isso mostra o quanto você é tendencioso. Deve ser o contrário, Boost: array pode ser, na melhor das hipóteses, rápido como matrizes estáticas.
toto
3
@oto: É um mal-entendido: você deve lê-lo como "Usar um array estático será o melhor ((tão rápido quanto a versão boost :: array) && (muito menos seguro))". Vou editar a postagem para esclarecer isso. A propósito, obrigado pelo benefício da dúvida.
paercebal
1
e quanto a std :: array?
paulm
4
Sempre mostre a cotação completa. "Os programadores perdem muito tempo pensando ou se preocupando com a velocidade das partes não críticas de seus programas, e essas tentativas de eficiência têm um forte impacto negativo quando a depuração e a manutenção são consideradas. Devemos esquecer as pequenas eficiências, por exemplo, sobre 97% do tempo: a otimização prematura é a raiz de todo mal. No entanto, não devemos desperdiçar nossas oportunidades nesses 3% críticos. " Caso contrário, torna-se uma mordida sem sentido.
metamorfose
32
Vetores são matrizes sob o capô. O desempenho é o mesmo.
Um lugar em que você pode encontrar um problema de desempenho é não dimensionar o vetor corretamente, para começar.
À medida que o vetor é preenchido, ele se redimensiona e isso pode implicar em uma nova alocação de matriz, seguida por n construtores de cópia, seguida por cerca de n chamadas de destruidor, seguidas por uma exclusão de matriz.
Se sua construção / destruição for cara, é muito melhor criar o vetor com o tamanho correto para começar.
Existe uma maneira simples de demonstrar isso. Crie uma classe simples que mostre quando é construída / destruída / copiada / atribuída. Crie um vetor dessas coisas e comece a pressioná-las na parte traseira do vetor. Quando o vetor for preenchido, haverá uma cascata de atividades à medida que o vetor for redimensionado. Em seguida, tente novamente com o vetor dimensionado para o número esperado de elementos. Você verá a diferença.
Pendantry: o desempenho tem o mesmo grande O. std :: vector faz um pouco de contabilidade, o que presumivelmente custa uma pequena quantidade de tempo. OTOH, você acaba fazendo a mesma contabilidade ao rolar suas próprias matrizes dinâmicas.
dmckee --- gatinho ex-moderador
sim, eu entendo. O impulso de sua pergunta, porém, foi quais são as diferenças de desempenho ... Tentei abordar isso.
21468 EvilTeach
O std :: vector do Gcc realmente aumenta a capacidade um por um se você chamar push_back.
precisa
3
@bjhend Então o gcc std::vectorsoa como não-compatível? Acredito que o padrão exija que vector::push_backamortizem a complexidade constante, e aumentar a capacidade em 1 em cada um push_backserá n ^ 2 após a contabilização de realocações. - presumindo-se algum tipo de aumento exponencial da capacidade push_backe insert, caso reservecontrário, um aumento constante de fator nas cópias de conteúdo vetorial. Um fator de crescimento de vetor exponencial de 1,5 significaria ~ 3x mais cópias se você não o fizesse reserve().
Yakk - Adam Nevraumont 5/12/12
3
@bjhend você está errado. A norma proíbe o crescimento exponencial: § 23.2.3, parágrafo 16, diz "A Tabela 101 lista as operações que são fornecidas para alguns tipos de contêineres de sequência, mas não para outros. Uma implementação deve fornecer essas operações para todos os tipos de contêineres mostrados na coluna" contêiner "e implementá-los-á de modo a levar tempo constante amortizado. " (a tabela 101 é a que contém push_back). Agora, por favor, pare de espalhar FUD. Nenhuma implementação convencional viola esse requisito. A biblioteca C ++ padrão da Microsoft cresce com um fator de 1,5x e o GCC cresce com um fator de 2x.
No entanto, pode haver casos em que você ainda precisa de matrizes. Ao fazer interface com código de baixo nível (por exemplo, assembly) ou bibliotecas antigas que exigem matrizes, talvez você não consiga usar vetores.
Não é verdade. Os vetores degradam muito bem em matrizes / ponteiros se você usar:
vector<double>vector;vector.push_back(42);double*array=&(*vector.begin());// pass the array to whatever low-level code you have
Isso funciona para todas as principais implementações de STL. No próximo padrão, será necessário que funcione (mesmo que funcione bem hoje).
O padrão atual não diz isso. Está implícito e é implementado como armazenamento contínuo. Mas o padrão apenas diz que é um contêiner de acesso aleatório (usando iteradores). O próximo padrão será explícito.
Frank Krueger
1
& * v.begin () apenas aplica o operador & ao resultado da remoção da referência ao iterador. A des-referência pode retornar QUALQUER tipo. Usar o endereço do operador pode retornar QUALQUER tipo. O padrão não define isso como um ponteiro para uma área contígua da memória.
31868 Frank Krueger
15
O texto original da Norma de 1998 realmente não a exigia, mas havia um adendo em 2003 que trata disso, portanto é realmente coberto pela Norma. herbsutter.wordpress.com/2008/04/07/…
Nemanja Trifunovic
2
O C ++ 03 diz explicitamente que &v[n] == &v[0] + né válido, desde que nesteja dentro da faixa de tamanho. O parágrafo que contém esta declaração não foi alterado no C ++ 11.
precisa
2
por que não usar std :: vector :: data ()?
paulm
15
Você tem ainda menos motivos para usar matrizes simples no C ++ 11.
Existem 3 tipos de matrizes na natureza, da mais rápida à mais lenta, dependendo dos recursos que elas possuem (é claro que a qualidade da implementação pode tornar as coisas muito rápidas, mesmo no caso 3 da lista):
Estático com tamanho conhecido em tempo de compilação. ---std::array<T, N>
Dinâmico com tamanho conhecido em tempo de execução e nunca redimensionado. A otimização típica aqui é que, se a matriz puder ser alocada diretamente na pilha. - Não disponível . Talvezdynarray em C ++ TS após C ++ 14. Em C existem VLAs
Dinâmico e redimensionável em tempo de execução. ---std::vector<T>
Para 1. matrizes estáticas simples com número fixo de elementos, usestd::array<T, N> no C ++ 11.
Para 2. matrizes de tamanho fixo especificadas em tempo de execução, mas que não mudam de tamanho, há discussão no C ++ 14, mas ele foi movido para uma especificação técnica e, finalmente, feito do C ++ 14.
Pois 3. std::vector<T>normalmente pedirá memória na pilha . Isso pode ter consequências no desempenho, embora você possa usar std::vector<T, MyAlloc<T>>para melhorar a situação com um alocador personalizado. A vantagem em comparação T mytype[] = new MyType[n];é que você pode redimensioná-lo e não decair para um ponteiro, como fazem as matrizes simples.
Use os tipos de biblioteca padrão mencionados para evitar que as matrizes se deteriorem nos ponteiros . Você economizará tempo de depuração e o desempenho será exatamente o mesmo que com matrizes simples se você usar o mesmo conjunto de recursos.
Depois de revisar os comentários do órgão nacional para n3690, esse componente da biblioteca foi excluído do documento de trabalho C ++ 14 em uma Especificação Técnica separada. Este contêiner não faz parte do rascunho C ++ 14 a partir da n3797. de en.cppreference.com/w/cpp/container/dynarray
Mohamed El-Nakib
1
resposta muito boa breve e resumido, mais detalhes do que qualquer outro.
Mohamed El-Nakib
6
Vá com STL. Não há penalidade de desempenho. Os algoritmos são muito eficientes e fazem um bom trabalho ao lidar com os tipos de detalhes nos quais a maioria de nós não pensaria.
STL é uma biblioteca altamente otimizada. De fato, até é sugerido o uso do STL em jogos onde pode ser necessário alto desempenho. As matrizes são muito suscetíveis a erros para serem usadas nas tarefas diárias. Os compiladores de hoje também são muito inteligentes e podem realmente produzir um excelente código com o STL. Se você sabe o que está fazendo, o STL geralmente pode fornecer o desempenho necessário. Por exemplo, inicializando vetores com o tamanho necessário (se você souber desde o início), você pode basicamente atingir o desempenho da matriz. No entanto, pode haver casos em que você ainda precisa de matrizes. Ao fazer interface com código de baixo nível (por exemplo, assembly) ou bibliotecas antigas que exigem matrizes, talvez você não consiga usar vetores.
Como o vetor é contíguo, ainda é muito fácil interagir com bibliotecas que exigem matrizes.
Greg Rogers
Sim, mas se você quiser mexer com as coisas internas do vetor, haveria menos vantagem em usar um vetor. A propósito, a palavra-chave era "talvez não".
Mehrdad Afshari
3
só sei de um caso em que vetores não podem ser usados: se o tamanho for 0. então & a [0] ou & * a.begin () não funcionarão. c ++ 1x irá corrigir que com a introdução de uma função a.data () que devolve o tampão interno mantendo os elementos
Johannes Schaub - litb
O cenário específico em minha mente, quando escrevi, era de matrizes baseadas em pilha.
Mehrdad Afshari
1
Vetor de interface ou qualquer contêiner contíguo com C: vec.data()para dados e vec.size()tamanho. É tão fácil.
A conclusão é que matrizes de números inteiros são mais rápidas que vetores de números inteiros (5 vezes no meu exemplo). Entretanto, matrizes e vetores apresentam a mesma velocidade para dados mais complexos / não alinhados.
Se você compilar o software no modo de depuração, muitos compiladores não incorporarão as funções de acessador do vetor. Isso tornará a implementação do vetor stl muito mais lenta nas circunstâncias em que o desempenho é um problema. Isso também facilitará a depuração do código, pois você pode ver no depurador quanta memória foi alocada.
No modo otimizado, eu esperaria que o vetor stl se aproximasse da eficiência de uma matriz. Isso ocorre porque muitos dos métodos de vetor estão agora alinhados.
Isso é importante mencionar. A criação de perfil de material STL de depuração é muito, muito lenta. E é uma das razões pelas quais as pessoas pensam que o STL é lento.
Erik Aronesty
3
Definitivamente, há um impacto no desempenho do uso de uma std::vectormatriz bruta versus uma bruta quando você deseja um buffer não inicializado (por exemplo, para usar como destino memcpy()). Um std::vectorinicializará todos os seus elementos usando o construtor padrão. Uma matriz bruta não.
A especificação c ++ para o std:vectorconstrutor que usa um countargumento (é a terceira forma) afirma:
`Constrói um novo contêiner a partir de uma variedade de fontes de dados, opcionalmente usando uma alocação de alocador fornecida pelo usuário.
3) Constrói o contêiner com as instâncias padrão de T. inseridas por padrão. Nenhuma cópia é feita.
Complexidade
2-3) Linear na contagem
Uma matriz bruta não incorre nesse custo de inicialização.
A diferença de desempenho entre os dois depende muito da implementação - se você comparar um std :: vector mal implementado com uma implementação de matriz ideal, a matriz venceria, mas mudaria de posição e o vetor venceria ...
Contanto que você compare maçãs com maçãs (tanto a matriz quanto o vetor têm um número fixo de elementos ou são redimensionados dinamicamente), eu pensaria que a diferença de desempenho é desprezível desde que você siga a prática de codificação STL. Não se esqueça de que o uso de contêineres C ++ padrão também permite que você faça uso dos algoritmos pré-processados que fazem parte da biblioteca C ++ padrão e a maioria deles provavelmente terá um desempenho melhor do que a implementação média do mesmo algoritmo que você constrói. .
Dito isto, IMHO o vetor vence em um cenário de depuração com um STL de depuração, já que a maioria das implementações de STL com um modo de depuração adequado pode pelo menos destacar / apontar os erros típicos cometidos pelas pessoas ao trabalhar com contêineres padrão.
Ah, e não esqueça que a matriz e o vetor compartilham o mesmo layout de memória, para que você possa usar vetores para passar dados ao código C ou C ++ herdado que espera matrizes básicas. Lembre-se de que a maioria das apostas não ocorre nesse cenário e você está lidando com a memória bruta novamente.
Eu acho que para atender aos requisitos de desempenho (O (1) pesquisas e inserções), você quase precisa implementar std :: vector <> usando matrizes dinâmicas. Certamente esta é a maneira óbvia de fazê-lo.
dmckee --- ex-moderador gatinho
Não apenas os requisitos de desempenho, mas também o requisito de armazenamento contíguo. Uma implementação ruim de vetor colocará muitas camadas de indireção entre a matriz e a API. Uma boa implementação de vetor permitirá código embutido, SIMD usado em loops etc.
Max Lybbert
Uma implementação de vetor ruim, conforme descrito, não seria compatível com o padrão. Se você quiser indiretamente, std::dequepode ser usado.
1919 Phil1970
1
Se você não precisar ajustar dinamicamente o tamanho, terá uma sobrecarga de memória para salvar a capacidade (um ponteiro / tamanho_t). É isso aí.
Pode haver algum caso extremo em que você tenha um acesso vetorial dentro de uma função embutida dentro de uma função embutida, onde você foi além do que o compilador incorporará e forçará uma chamada de função. Isso seria tão raro que não valeria a pena se preocupar - em geral, eu concordo com litb .
Estou surpreso que ninguém tenha mencionado isso ainda - não se preocupe com o desempenho até que se prove que é um problema, e faça uma avaliação comparativa.
Eu diria que a principal preocupação não é desempenho, mas segurança. Você pode cometer muitos erros com matrizes (considere redimensionar, por exemplo), onde um vetor economizaria muita dor.
Os vetores usam um pouco mais de memória que as matrizes, pois contêm o tamanho da matriz. Eles também aumentam o tamanho do disco rígido dos programas e provavelmente a pegada de memória dos programas. Esses aumentos são pequenos, mas podem ser importantes se você estiver trabalhando com um sistema incorporado. Embora a maioria dos lugares em que essas diferenças sejam importantes sejam locais em que você usaria C em vez de C ++.
Se isso importa, obviamente você não está usando matrizes de tamanho dinâmico e, como tal, suas matrizes não precisam alterar o tamanho. (Se o fizessem, você armazenaria o tamanho de alguma forma). Portanto, você também pode usar o boost :: array, a menos que eu esteja enganado - e o que faz você dizer que isso precisa "armazenar o tamanho" em algum lugar?
contradiz as conclusões de "Comparação de código de assembly gerado para operações básicas de indexação, desreferenciamento e incremento em vetores e matrizes / ponteiros".
Deve haver uma diferença entre as matrizes e vetores. O teste diz isso ... apenas tente, o código está lá ...
Às vezes, matrizes são realmente melhores que vetores. Se você está sempre manipulando um conjunto de objetos de comprimento fixo, as matrizes são melhores. Considere os seguintes trechos de código:
A essência é que há uma pequena quantidade de sobrecarga com cada subvetor com informações de tamanho e não haverá necessariamente serialização de dados (como ocorre com as matrizes c multidimensionais). Essa falta de serialização pode oferecer mais do que oportunidades de micro otimização. Se você estiver fazendo arrays multidimensionais, talvez seja melhor estender o std :: vector e rolar sua própria função get / set / redimensionar bits.
Assumindo uma matriz de comprimento fixo (por exemplo , int* v = new int[1000];vs std::vector<int> v(1000);, com o tamanho de vser mantido fixo em 1000), a única consideração de desempenho que realmente importa (ou pelo menos importava para mim quando eu estava em um dilema semelhante) é a velocidade de acesso a um elemento. Procurei o código vetorial do STL e aqui está o que encontrei:
Esta função certamente será incorporada pelo compilador. Portanto, desde que a única coisa que você planeja fazer vseja acessar seus elementos operator[], parece que realmente não deve haver nenhuma diferença no desempenho.
int main(int argc, const std::vector<string>& argv)
Respostas:
O uso de matrizes C ++ com
new
(ou seja, matrizes dinâmicas) deve ser evitado. Existe o problema de manter o controle do tamanho e excluí-los manualmente e fazer todo o tipo de tarefas domésticas.O uso de matrizes na pilha também é desencorajado, porque você não tem verificação de alcance, e passar a matriz perderá qualquer informação sobre seu tamanho (conversão de matriz em ponteiro).
boost::array
Nesse caso, você deve usar , que agrupa uma matriz C ++ em uma classe pequena e fornece umasize
função e iteradores para iterar sobre ela.Agora as matrizes std :: vector vs. C ++ nativas (extraídas da internet):
Nota: Se você alocar matrizes com
new
objetos não pertencentes a classe (como simplesint
) ou classes sem um construtor definido pelo usuário e não desejar inicializar seus elementos inicialmente, o uso denew
matrizes alocadas poderá ter vantagens de desempenho, poisstd::vector
inicializa todos os elementos para valores padrão (0 para int, por exemplo) na construção (créditos para @bernie por me lembrar).fonte
Preâmbulo para pessoas com micro-otimizadores
Lembrar:
(Obrigado à metamorfose pela cotação completa)
Não use uma matriz C em vez de um vetor (ou o que seja) apenas porque você acredita que é mais rápido, pois deveria ser de nível inferior. Você estaria errado.
Use por vetor padrão (ou o contêiner seguro adaptado às suas necessidades) e, se o seu profiler disser que é um problema, veja se você pode otimizá-lo, usando um algoritmo melhor ou alterando o contêiner.
Dito isto, podemos voltar à pergunta original.
Matriz estática / dinâmica?
As classes de matriz C ++ são mais bem comportadas do que a matriz C de baixo nível, porque sabem muito sobre si mesmas e podem responder a perguntas que as matrizes C não podem. Eles são capazes de limpar depois de si mesmos. E o mais importante, eles geralmente são escritos usando modelos e / ou inlining, o que significa que o que parece muito código na depuração resolve pouco ou nenhum código produzido na criação da versão, o que significa que não há diferença com a concorrência menos segura interna.
Ao todo, ele se enquadra em duas categorias:
Matrizes dinâmicas
Usar um ponteiro para uma matriz malloc-ed / new-ed será tão rápido quanto a versão std :: vector e muito menos seguro (consulte a publicação do litb ).
Então use um std :: vector.
Matrizes estáticas
Usar uma matriz estática será, na melhor das hipóteses:
Então use um std :: array .
Memória não inicializada
Às vezes, usar um
vector
buffer em vez de um buffer bruto gera um custo visível, porque ovector
inicializará o buffer na construção, enquanto o código que ele substitui não, como observou Bernie em sua resposta .Se esse for o caso, você poderá lidar com isso usando um em
unique_ptr
vez de avector
ou, se o caso não for excepcional em sua linha de código, escreva uma classebuffer_owner
que seja a proprietária dessa memória e ofereça acesso fácil e seguro a ela, incluindo bônus como redimensioná-lo (usandorealloc
?) ou o que você precisar.fonte
Vetores são matrizes sob o capô. O desempenho é o mesmo.
Um lugar em que você pode encontrar um problema de desempenho é não dimensionar o vetor corretamente, para começar.
À medida que o vetor é preenchido, ele se redimensiona e isso pode implicar em uma nova alocação de matriz, seguida por n construtores de cópia, seguida por cerca de n chamadas de destruidor, seguidas por uma exclusão de matriz.
Se sua construção / destruição for cara, é muito melhor criar o vetor com o tamanho correto para começar.
Existe uma maneira simples de demonstrar isso. Crie uma classe simples que mostre quando é construída / destruída / copiada / atribuída. Crie um vetor dessas coisas e comece a pressioná-las na parte traseira do vetor. Quando o vetor for preenchido, haverá uma cascata de atividades à medida que o vetor for redimensionado. Em seguida, tente novamente com o vetor dimensionado para o número esperado de elementos. Você verá a diferença.
fonte
std::vector
soa como não-compatível? Acredito que o padrão exija quevector::push_back
amortizem a complexidade constante, e aumentar a capacidade em 1 em cada umpush_back
será n ^ 2 após a contabilização de realocações. - presumindo-se algum tipo de aumento exponencial da capacidadepush_back
einsert
, casoreserve
contrário, um aumento constante de fator nas cópias de conteúdo vetorial. Um fator de crescimento de vetor exponencial de 1,5 significaria ~ 3x mais cópias se você não o fizessereserve()
.Para responder a algo que Mehrdad disse:
Não é verdade. Os vetores degradam muito bem em matrizes / ponteiros se você usar:
Isso funciona para todas as principais implementações de STL. No próximo padrão, será necessário que funcione (mesmo que funcione bem hoje).
fonte
&v[n] == &v[0] + n
é válido, desde quen
esteja dentro da faixa de tamanho. O parágrafo que contém esta declaração não foi alterado no C ++ 11.Você tem ainda menos motivos para usar matrizes simples no C ++ 11.
Existem 3 tipos de matrizes na natureza, da mais rápida à mais lenta, dependendo dos recursos que elas possuem (é claro que a qualidade da implementação pode tornar as coisas muito rápidas, mesmo no caso 3 da lista):
std::array<T, N>
dynarray
em C ++ TS após C ++ 14. Em C existem VLAsstd::vector<T>
Para 1. matrizes estáticas simples com número fixo de elementos, use
std::array<T, N>
no C ++ 11.Para 2. matrizes de tamanho fixo especificadas em tempo de execução, mas que não mudam de tamanho, há discussão no C ++ 14, mas ele foi movido para uma especificação técnica e, finalmente, feito do C ++ 14.
Pois 3.
std::vector<T>
normalmente pedirá memória na pilha . Isso pode ter consequências no desempenho, embora você possa usarstd::vector<T, MyAlloc<T>>
para melhorar a situação com um alocador personalizado. A vantagem em comparaçãoT mytype[] = new MyType[n];
é que você pode redimensioná-lo e não decair para um ponteiro, como fazem as matrizes simples.Use os tipos de biblioteca padrão mencionados para evitar que as matrizes se deteriorem nos ponteiros . Você economizará tempo de depuração e o desempenho será exatamente o mesmo que com matrizes simples se você usar o mesmo conjunto de recursos.
fonte
Vá com STL. Não há penalidade de desempenho. Os algoritmos são muito eficientes e fazem um bom trabalho ao lidar com os tipos de detalhes nos quais a maioria de nós não pensaria.
fonte
STL é uma biblioteca altamente otimizada. De fato, até é sugerido o uso do STL em jogos onde pode ser necessário alto desempenho. As matrizes são muito suscetíveis a erros para serem usadas nas tarefas diárias. Os compiladores de hoje também são muito inteligentes e podem realmente produzir um excelente código com o STL. Se você sabe o que está fazendo, o STL geralmente pode fornecer o desempenho necessário. Por exemplo, inicializando vetores com o tamanho necessário (se você souber desde o início), você pode basicamente atingir o desempenho da matriz. No entanto, pode haver casos em que você ainda precisa de matrizes. Ao fazer interface com código de baixo nível (por exemplo, assembly) ou bibliotecas antigas que exigem matrizes, talvez você não consiga usar vetores.
fonte
vec.data()
para dados evec.size()
tamanho. É tão fácil.Sobre a contribuição de duli .
A conclusão é que matrizes de números inteiros são mais rápidas que vetores de números inteiros (5 vezes no meu exemplo). Entretanto, matrizes e vetores apresentam a mesma velocidade para dados mais complexos / não alinhados.
fonte
Se você compilar o software no modo de depuração, muitos compiladores não incorporarão as funções de acessador do vetor. Isso tornará a implementação do vetor stl muito mais lenta nas circunstâncias em que o desempenho é um problema. Isso também facilitará a depuração do código, pois você pode ver no depurador quanta memória foi alocada.
No modo otimizado, eu esperaria que o vetor stl se aproximasse da eficiência de uma matriz. Isso ocorre porque muitos dos métodos de vetor estão agora alinhados.
fonte
Definitivamente, há um impacto no desempenho do uso de uma
std::vector
matriz bruta versus uma bruta quando você deseja um buffer não inicializado (por exemplo, para usar como destinomemcpy()
). Umstd::vector
inicializará todos os seus elementos usando o construtor padrão. Uma matriz bruta não.A especificação c ++ para o
std:vector
construtor que usa umcount
argumento (é a terceira forma) afirma:Uma matriz bruta não incorre nesse custo de inicialização.
Veja também Como posso evitar que std :: vector <> inicialize todos os seus elementos?
fonte
A diferença de desempenho entre os dois depende muito da implementação - se você comparar um std :: vector mal implementado com uma implementação de matriz ideal, a matriz venceria, mas mudaria de posição e o vetor venceria ...
Contanto que você compare maçãs com maçãs (tanto a matriz quanto o vetor têm um número fixo de elementos ou são redimensionados dinamicamente), eu pensaria que a diferença de desempenho é desprezível desde que você siga a prática de codificação STL. Não se esqueça de que o uso de contêineres C ++ padrão também permite que você faça uso dos algoritmos pré-processados que fazem parte da biblioteca C ++ padrão e a maioria deles provavelmente terá um desempenho melhor do que a implementação média do mesmo algoritmo que você constrói. .
Dito isto, IMHO o vetor vence em um cenário de depuração com um STL de depuração, já que a maioria das implementações de STL com um modo de depuração adequado pode pelo menos destacar / apontar os erros típicos cometidos pelas pessoas ao trabalhar com contêineres padrão.
Ah, e não esqueça que a matriz e o vetor compartilham o mesmo layout de memória, para que você possa usar vetores para passar dados ao código C ou C ++ herdado que espera matrizes básicas. Lembre-se de que a maioria das apostas não ocorre nesse cenário e você está lidando com a memória bruta novamente.
fonte
std::deque
pode ser usado.Se você não precisar ajustar dinamicamente o tamanho, terá uma sobrecarga de memória para salvar a capacidade (um ponteiro / tamanho_t). É isso aí.
fonte
Pode haver algum caso extremo em que você tenha um acesso vetorial dentro de uma função embutida dentro de uma função embutida, onde você foi além do que o compilador incorporará e forçará uma chamada de função. Isso seria tão raro que não valeria a pena se preocupar - em geral, eu concordo com litb .
Estou surpreso que ninguém tenha mencionado isso ainda - não se preocupe com o desempenho até que se prove que é um problema, e faça uma avaliação comparativa.
fonte
Eu diria que a principal preocupação não é desempenho, mas segurança. Você pode cometer muitos erros com matrizes (considere redimensionar, por exemplo), onde um vetor economizaria muita dor.
fonte
Os vetores usam um pouco mais de memória que as matrizes, pois contêm o tamanho da matriz. Eles também aumentam o tamanho do disco rígido dos programas e provavelmente a pegada de memória dos programas. Esses aumentos são pequenos, mas podem ser importantes se você estiver trabalhando com um sistema incorporado. Embora a maioria dos lugares em que essas diferenças sejam importantes sejam locais em que você usaria C em vez de C ++.
fonte
O seguinte teste simples:
Matriz C ++ vs Explicação do teste de desempenho vetorial
contradiz as conclusões de "Comparação de código de assembly gerado para operações básicas de indexação, desreferenciamento e incremento em vetores e matrizes / ponteiros".
Deve haver uma diferença entre as matrizes e vetores. O teste diz isso ... apenas tente, o código está lá ...
fonte
Às vezes, matrizes são realmente melhores que vetores. Se você está sempre manipulando um conjunto de objetos de comprimento fixo, as matrizes são melhores. Considere os seguintes trechos de código:
onde a versão vetorial de X é
e a versão do array do X é:
A versão do array será main () será mais rápida porque estamos evitando a sobrecarga de "novo" todas as vezes no loop interno.
(Esse código foi postado em comp.lang.c ++ por mim).
fonte
Se você estiver usando vetores para representar um comportamento multidimensional, há um impacto no desempenho.
Os vetores 2d + causam um impacto no desempenho?
A essência é que há uma pequena quantidade de sobrecarga com cada subvetor com informações de tamanho e não haverá necessariamente serialização de dados (como ocorre com as matrizes c multidimensionais). Essa falta de serialização pode oferecer mais do que oportunidades de micro otimização. Se você estiver fazendo arrays multidimensionais, talvez seja melhor estender o std :: vector e rolar sua própria função get / set / redimensionar bits.
fonte
Assumindo uma matriz de comprimento fixo (por exemplo ,
int* v = new int[1000];
vsstd::vector<int> v(1000);
, com o tamanho dev
ser mantido fixo em 1000), a única consideração de desempenho que realmente importa (ou pelo menos importava para mim quando eu estava em um dilema semelhante) é a velocidade de acesso a um elemento. Procurei o código vetorial do STL e aqui está o que encontrei:Esta função certamente será incorporada pelo compilador. Portanto, desde que a única coisa que você planeja fazer
v
seja acessar seus elementosoperator[]
, parece que realmente não deve haver nenhuma diferença no desempenho.fonte