Choix entre vector :: resize () et vector :: reserve ()

151

Je pré-alloue de la mémoire à ma vectorvariable membre. Le code ci-dessous est une partie minimale

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

Maintenant, à un moment donné, si les t_Names.size()égaux 1000. J'ai l'intention d'augmenter la taille de 100. Ensuite, s'il atteint 1100, augmentez à nouveau de 100et ainsi de suite.

Ma question est, que choisir entre vector::resize()et vector::reserve(). Y a-t-il un meilleur choix dans ce genre de scénario?

Edit : j'ai une sorte d'estimation précise pour le t_Names. J'estime que c'est autour 700de 800. Cependant, dans certaines situations (rarement), il peut augmenter plus de 1000.

iammilind
la source
34
Vous réalisez que cela signifie que la croissance vectorielle n'est plus amortie à temps constant et que vous perdez l'un des avantages de performance de l'utilisation std::vector.
Blastfurnace
1
En relation, consultez C ++ simplifié: comment les vecteurs se développent sur le site de Dr. Dobbs.
jww

Réponses:

262

Les deux fonctions font des choses très différentes!

La resize()méthode (et passer l'argument au constructeur équivaut à cela) insérera ou supprimera le nombre approprié d'éléments dans le vecteur pour lui donner une taille donnée (elle a un deuxième argument facultatif pour spécifier leur valeur). Cela affectera le size(), l'itération passera sur tous ces éléments, push_back s'insérera après eux et vous pourrez y accéder directement en utilisant le operator[].

La reserve()méthode alloue uniquement de la mémoire, mais la laisse non initialisée. Cela n'affecte que capacity(), mais size()restera inchangé. Il n'y a pas de valeur pour les objets, car rien n'est ajouté au vecteur. Si vous insérez ensuite les éléments, aucune réallocation ne se produira, car cela a été fait à l'avance, mais c'est le seul effet.

Cela dépend donc de ce que vous voulez. Si vous voulez un tableau de 1000 éléments par défaut, utilisez resize(). Si vous voulez un tableau dans lequel vous prévoyez d'insérer 1000 éléments et que vous voulez éviter quelques allocations, utilisez reserve().

EDIT: Le commentaire de Blastfurnace m'a fait relire la question et me rendre compte que dans votre cas, la bonne réponse est de ne pas préallouer manuellement. Continuez simplement à insérer les éléments à la fin selon vos besoins. Le vecteur se réallouera automatiquement si nécessaire et le fera plus efficacement que la méthode manuelle mentionnée. Le seul cas où reserve()cela a du sens est lorsque vous avez une estimation raisonnablement précise de la taille totale dont vous aurez besoin facilement disponible à l'avance.

EDIT2: Modification de la question de l'annonce: si vous avez une estimation initiale, alors reserve()cette estimation. Si cela ne suffit pas, laissez simplement le vecteur faire son travail.

Jan Hudec
la source
J'ai édité la question. J'ai une certaine estimation pour le vector.
iammilind
3
@Jan: eh bien, c'est fragile ou pas selon la difficulté que vous vous faites d'entretenir la propriété requise. Quelque chose comme 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); }est assez robuste en ce qui concerne la réservation de l'espace. Je n'ai aucune idée du nombre d'éléments qui seront réellement ajoutés, mais j'ai une limite supérieure. Bien sûr, en cas de doute, avec des vecteurs, vous pouvez simplement utiliser des index au lieu d'itérateurs, la différence est généralement négligeable.
Steve Jessop du
4
Votre formulation a du sens pour quelqu'un qui connaît déjà la bonne réponse, mais pourrait facilement induire en erreur les personnes qui doivent poser la question. "resize () ... insérera un nombre donné d'éléments dans le vecteur" - vrai seulement la première fois qu'il est utilisé - il insère généralement la différence entre le nombre demandé et le nombre préexistant size(). «La méthode reserve () n'alloue que de la mémoire» - elle peut allouer ou non de la mémoire selon qu'elle capacity()est déjà suffisante, elle peut aussi avoir besoin de déplacer des éléments et de désallouer leur mémoire d'origine. "veux éviter quelques allocations" et des copies, etc.
Tony Delroy
19
En fait, réserver avant de pousser est vital et doit être utilisé. Supposons que vous codez une sorte de chargeur de modèle 3D et que le modèle a environ 15000 sommets. Si vous essayez de repousser chaque sommet pendant le chargement sans les pré-allouer au préalable, cela prendra beaucoup de temps. J'ai personnellement vécu cela, j'ai essayé de charger un modèle de voiture .obj avec près de 100000 sommets, cela a pris 30 secondes. Ensuite, j'ai refactoré le code en utilisant la pré-allocation avec .reserve (), maintenant cela prend 3 secondes. Il suffit de mettre un .reserve (100000) au début du code pour gagner 27 secondes.
deniz
1
@deniz C'est trivial à l'échelle 100000, mais très faux à l'échelle 100-300, où la réservation peut être inutile si elle est faite inutilement.
deworde
30

resize()non seulement alloue de la mémoire, il crée également autant d'instances que la taille souhaitée que vous passez resize()en argument. Mais reserve()n'alloue que de la mémoire, il ne crée pas d'instances. C'est,

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

Sortie ( démo en ligne ):

1
1
0
1

Cela resize()n'est peut-être pas souhaitable si vous ne voulez pas les objets créés par défaut. Ce sera aussi lent. En outre, si vous y push_back()ajoutez de nouveaux éléments, la valeur size()du vecteur augmentera encore en allouant une nouvelle mémoire (ce qui signifie également déplacer les éléments existants vers l'espace mémoire nouvellement alloué). Si vous avez utilisé reserve()au début pour vous assurer qu'il y a déjà assez de mémoire allouée, la valeur size()du vecteur augmentera lorsque vous push_back()y arriverez , mais elle n'allouera pas de nouvelle mémoire tant qu'elle n'aura pas épuisé l'espace que vous lui avez réservé .

Nawaz
la source
6
Après avoir fait reserve(N), nous pouvons utiliser operator []sans danger. correct ?
iammilind
2
Alors que la plupart des implémentations alloueront le montant exact que vous demandez reserve, la spécification exige seulement qu'elle alloue au moins autant, de sorte que certaines implémentations peuvent arrondir à une certaine limite et afficher ainsi une capacité supérieure à 1000.
Jan Hudec
16
@iammilind: Non, si l'index est supérieur ou égal à v.size(). Notez que reserve(N)cela ne change pas size()de vecteur.
Nawaz
5
@iammilind: INcorrect. Après avoir appelé reSERVE, aucune entrée n'est ajoutée, seule la mémoire suffisante pour les ajouter est obtenue.
Jan Hudec
2

D'après votre description, il semble que vous vouliez "réserver" l'espace de stockage alloué du vecteur t_Names.

Notez que resizeinitialiser le vecteur nouvellement alloué où reservealloue simplement mais ne construit pas. Par conséquent, «réserver» est beaucoup plus rapide que «redimensionner»

Vous pouvez vous référer à la documentation concernant la différence de redimensionnement et de réserve

tremper
la source
1
S'il vous plaît se référer ici à la place: vecteur et capacité ( pourquoi )
sehe
1
Merci pour l'ajout de lien, sehe
dip
2

réserver lorsque vous ne souhaitez pas que les objets soient initialisés lors de la réservation. Vous pouvez également préférer différencier et suivre logiquement son nombre par rapport à son nombre d’utilisations lorsque vous redimensionnez. il y a donc une différence de comportement dans l'interface - le vecteur représentera le même nombre d'éléments lorsqu'il est réservé, et sera 100 éléments plus grand lorsqu'il sera redimensionné dans votre scénario.

Y a-t-il un meilleur choix dans ce genre de scénario?

cela dépend entièrement de vos objectifs lorsque vous combattez le comportement par défaut. certaines personnes privilégieront les allocateurs personnalisés - mais nous avons vraiment besoin d'une meilleure idée de ce que vous essayez de résoudre dans votre programme pour bien vous conseiller.

fwiw, de nombreuses implémentations vectorielles doubleront simplement le nombre d'éléments alloués lorsqu'elles doivent croître - essayez-vous de minimiser les tailles d'allocation de pointe ou essayez-vous de réserver suffisamment d'espace pour un programme sans verrouillage ou autre?

Justin
la source
" réserver lorsque vous ne voulez pas que les objets soient initialisés lorsqu'ils sont réservés. " La formulation correcte est lorsque vous ne voulez pas que les objets existent . Ce n'est pas comme un tableau non initialisé d'un type trivialement constructible, où les objets ne peuvent pas être lus mais peuvent être assignés; au contraire, seule la mémoire est réservée, mais aucun objet n'y existe, donc ils ne sont pas accessibles en utilisant operator[]ou quoi que ce soit.
underscore_d