Escolha entre vector :: resize () e vector :: reserve ()

151

Estou pré-alocando alguma memória para minha vectorvariável de membro. O código abaixo é parte mínima

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Agora, em algum momento, se for t_Names.size()igual 1000. Pretendo aumentar o tamanho em 100. Então, se atingir 1100, aumente novamente 100e assim por diante.

Minha pergunta é: o que escolher entre vector::resize()e vector::reserve(). Existe alguma escolha melhor nesse tipo de cenário?

Edit : Eu tenho uma espécie de estimativa precisa para o t_Names. Estimo que seja em torno 700de 800. No entanto, em certas situações (raramente), pode crescer mais do que 1000.

iammilind
fonte
34
Você percebe que isso significa que o crescimento do vetor não é mais amortizado em tempo constante e você perde um dos benefícios de desempenho do uso std::vector.
Blastfurnace 13/09/11
1
Relacionado, consulte C ++ facilitado: como os vetores crescem no site do Dr. Dobbs.
JWW

Respostas:

262

As duas funções fazem coisas muito diferentes!

O resize()método (e a passagem do argumento para o construtor é equivalente a isso) inserirá ou excluirá o número apropriado de elementos no vetor para torná-lo determinado tamanho (ele possui um segundo argumento opcional para especificar seu valor). Isso afetará a size()iteração sobre todos esses elementos, o push_back será inserido depois deles e você poderá acessá-los diretamente usando o operator[].

O reserve()método apenas aloca memória, mas a deixa não inicializada. Isso afeta apenas capacity(), mas size()será inalterado. Não há valor para os objetos, porque nada é adicionado ao vetor. Se você inserir os elementos, nenhuma realocação acontecerá, porque foi feita com antecedência, mas esse é o único efeito.

Então depende do que você quer. Se você deseja uma matriz de 1000 itens padrão, use resize(). Se você deseja uma matriz na qual deseja inserir 1000 itens e evitar algumas alocações, use reserve().

EDIT: O comentário de Blastfurnace me fez ler a pergunta novamente e perceber que, no seu caso, a resposta correta é não pré-alocar manualmente. Continue inserindo os elementos no final conforme necessário. O vetor será realocado automaticamente conforme necessário e o fará com mais eficiência do que a maneira manual mencionada. O único caso em que reserve()faz sentido é quando você tem uma estimativa razoavelmente precisa do tamanho total que precisará facilmente disponível com antecedência.

EDIT2: Edição da pergunta do anúncio: se você tiver uma estimativa inicial, reserve()essa estimativa. Se isso não for suficiente, deixe o vetor fazer a coisa certa.

Jan Hudec
fonte
Eu editei a pergunta. Eu tenho certa estimativa para o vector.
Iammilind
3
@ Jan: bem, é frágil ou não, de acordo com a dificuldade que você fez para manter a propriedade necessária. Algo como x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }é bastante robusto no que diz respeito à reserva do espaço. Não tenho idéia de quantos elementos serão realmente adicionados, mas tenho um limite superior. É claro que em caso de dúvida, com vetores, você pode simplesmente usar índices em vez de iteradores, a diferença geralmente é insignificante.
21711 Steve Jobs (
4
Sua redação faz sentido para alguém já ciente da resposta correta, mas pode facilmente enganar as pessoas que precisam fazer a pergunta. "resize () ... inserirá um determinado número de elementos no vetor" - somente verdadeiro na primeira vez em que é usado - geralmente insere a diferença entre o número solicitado e o pré-existente size(). "O método reserve () aloca apenas memória" - ele pode ou não alocar memória, dependendo se capacity()já é suficiente, também pode precisar mover elementos e desalocar sua memória original. "quer evitar algumas alocações" e copia etc #
Tony Delroy
19
Na verdade, reservar antes de empurrar é vital e deve ser usado. Suponha que você esteja codificando algum tipo de carregador de modelo 3d e o modelo tenha 15.000 vértices. Se você tentar fazer pushback de cada vértice enquanto carrega sem pré-alocá-los primeiro, levará um tempo sério. Pessoalmente, experimentei isso, tentei carregar um modelo de carro .obj com quase 100000 vértices. Demorou 30 segundos. Depois refatorei o código usando a pré-alocação com .reserve (), agora leva 3 segundos. Colocar um .reserve (100000) no início do código economiza 27 segundos.
deniz
1
@deniz Isso é verdade trivial na escala 100000, mas não é verdade na escala 100-300, onde a reserva pode ser um desperdício se for desnecessária.
deworde
30

resize()não apenas aloca memória, como também cria quantas instâncias quanto o tamanho desejado para o qual você passa resize()como argumento. Mas reserve()apenas aloca memória, não cria instâncias. Isso é,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Saída ( demonstração online ):

1
1
0
1

Portanto, resize()pode não ser desejável, se você não quiser os objetos criados por padrão. Vai ser lento também. Além disso, se você adicionar push_back()novos elementos, o size()vetor aumentará ainda mais alocando nova memória (o que também significa mover os elementos existentes para o espaço de memória recém-alocado). Se você usou reserve()no início para garantir que já há memória alocada suficiente, o size()vetor aumentará quando você fizer push_back()isso, mas ele não alocará nova memória novamente até que fique sem o espaço reservado para ele .

Nawaz
fonte
6
Depois de fazer reserve(N), podemos usar operator []inofensivamente. correto?
Iammilind 13/09
2
Enquanto a maioria das implementações irá alocar a quantidade exata que você solicite por reserve, a especificação requer apenas ele aloca pelo menos que muito, portanto, algumas implementações podem arredondar para algum limite e, portanto, apresentam maior capacidade do que 1000.
Jan Hudec
16
@iammilind: Não, se o índice for maior ou igual a v.size(). Observe que reserve(N)não muda size()de vetor.
Nawaz
5
@iammilind: INcorrect. Depois de chamar reSERVE, nenhuma entrada é adicionada, apenas a memória suficiente para adicioná-la é obtida.
Jan Hudec
2

Na sua descrição, parece que você deseja "reservar" o espaço de armazenamento alocado do vetor t_Names.

Observe que resizeinicialize o vetor recém-alocado onde reserveapenas aloca, mas não constrói. Portanto, 'reserva' é muito mais rápido que 'redimensionar'

Você pode consultar a documentação referente à diferença de redimensionamento e reserva

mergulho
fonte
1
Em vez disso, consulte aqui: vetor e capacidade ( por quê? )
#
1
Obrigado pela adição de link, sehe
dip
2

reserve quando não desejar que os objetos sejam inicializados quando reservados. Além disso, você pode preferir diferenciar e rastrear logicamente sua contagem versus sua contagem de uso ao redimensionar. portanto, há uma diferença de comportamento na interface - o vetor representará o mesmo número de elementos quando reservado e será 100 elementos maior quando redimensionado no seu cenário.

Existe alguma escolha melhor nesse tipo de cenário?

depende inteiramente de seus objetivos ao combater o comportamento padrão. algumas pessoas preferem alocadores personalizados - mas realmente precisamos de uma idéia melhor do que você está tentando resolver em seu programa para aconselhá-lo bem.

fwiw, muitas implementações de vetores simplesmente dobram a contagem de elementos alocados quando precisam crescer - você está tentando minimizar os tamanhos de alocação de pico ou está tentando reservar espaço suficiente para algum programa sem bloqueio ou algo mais?

justin
fonte
" reserva quando você não deseja que os objetos sejam inicializados quando reservados. " A formulação correta é quando você não deseja que os objetos existam . Não é como uma matriz não inicializada de um tipo trivialmente construtível, onde os objetos não podem ser lidos, mas podem ser atribuídos; em vez disso, apenas a memória é reservada, mas não existem objetos nela; portanto, eles não podem ser acessados ​​usando operator[]nada.
underscore_d