Scelta tra vector :: resize () e vector :: reserve ()


151

Sto pre-allocando un po 'di memoria alla mia vectorvariabile membro. Di seguito il codice è una parte minima

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

Ora a un certo punto del tempo, se t_Names.size()uguale 1000. Ho intenzione di aumentare le dimensioni di 100. Quindi se raggiunge 1100, aumenta di nuovo di 100così via.

La mia domanda è: cosa scegliere tra vector::resize()e vector::reserve(). C'è qualche scelta migliore in questo tipo di scenario?

Modifica : ho una sorta di stima precisa per il t_Names. Stimo che sia intorno 700a 800. Tuttavia, in alcune (raramente) situazioni, può crescere di più 1000.


34
Ti rendi conto che farlo significa che la crescita vettoriale non è più ammortizzata a tempo costante e perdi uno dei vantaggi in termini di prestazioni dell'utilizzo std::vector.
Blastfurnace,

1
In relazione, vedi C ++ reso più semplice: come i vettori crescono sul sito del Dr. Dobbs.
jww

Risposte:


262

Le due funzioni fanno cose molto diverse!

Il resize()metodo (e passare l'argomento al costruttore è equivalente a quello) inserirà o eliminerà il numero appropriato di elementi nel vettore per renderlo di dimensioni (ha un secondo argomento opzionale per specificare il loro valore). size()Influirà su , l'iterazione passerà su tutti quegli elementi, push_back li inserirà dopo e potrai accedervi direttamente usando operator[].

Il reserve()metodo alloca solo la memoria, ma la lascia non inizializzata. Colpisce solo capacity(), ma size()rimarrà invariato. Non esiste alcun valore per gli oggetti, poiché non viene aggiunto nulla al vettore. Se poi inserisci gli elementi, non si verificherà alcuna riallocazione, perché è stato fatto in anticipo, ma questo è l'unico effetto.

Quindi dipende da cosa vuoi. Se si desidera un array di 1000 articoli predefiniti, utilizzare resize(). Se si desidera un array in cui si prevede di inserire 1000 elementi e si desidera evitare un paio di allocazioni, utilizzare reserve().

EDIT: il commento di Blastfurnace mi ha fatto leggere di nuovo la domanda e rendermi conto che nel tuo caso la risposta corretta non è preallocata manualmente. Continua a inserire gli elementi alla fine di cui hai bisogno. Il vettore verrà riallocato automaticamente in base alle esigenze e lo farà in modo più efficiente rispetto al modo manuale menzionato. L'unico caso in cui reserve()ha senso è quando hai una stima ragionevolmente precisa della dimensione totale di cui avrai bisogno facilmente in anticipo.

EDIT2: Modifica domanda annuncio: se hai una stima iniziale, allora reserve()quella stima. Se risulta non essere abbastanza, lascia che il vettore faccia la sua cosa.


Ho modificato la domanda. Ho una certa stima per il vector.
iammilind,

3
@Jan: beh, è ​​fragile o no a seconda di quanto sia stato difficile per te mantenere la proprietà richiesta. Qualcosa di simile 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); }è piuttosto robusto per quanto riguarda la prenotazione dello spazio. Non ho idea di quanti elementi verranno effettivamente aggiunti, ma ho un limite superiore. Naturalmente in caso di dubbio, con i vettori è possibile utilizzare solo gli indici anziché gli iteratori, la differenza è generalmente trascurabile.
Steve Jessop,

4
La tua formulazione ha senso per qualcuno già a conoscenza della risposta corretta, ma potrebbe facilmente fuorviare le persone che hanno bisogno di porre la domanda. "resize () ... inserirà un determinato numero di elementi nel vettore" - vero solo la prima volta che viene usato - generalmente inserisce la differenza tra il numero richiesto e il preesistente size(). "Il metodo reserve () alloca solo la memoria" - può o meno allocare memoria a seconda che capacity()sia già sufficiente, potrebbe anche essere necessario spostare elementi e deallocare la loro memoria originale. "voglio evitare un paio di allocazioni" e copie ecc.
Tony Delroy,

19
In realtà, prenotare prima di spingere è vitale e deve essere usato. Supponiamo che stai codificando una sorta di caricatore di modelli 3d e che il modello abbia circa 15000 vertici. Se provi a respingere ogni vertice durante il caricamento senza prima assegnarli, ci vorrà molto tempo. L'ho sperimentato personalmente, ho provato a caricare un modello di auto .obj con quasi 100000 vertici, ci sono voluti 30 secondi. Quindi ho riformattato il codice usando la pre-allocazione con .reserve (), ora ci vogliono 3 secondi. Basta inserire un .reserve (100000) all'inizio del codice risparmiato 27 secondi.
deniz

1
@deniz Questo è banale vero su scala 100000, ma molto non vero su scala 100-300, dove riservare può essere dispendioso se fatto inutilmente.
Deworde

30

resize()non solo alloca la memoria, ma crea anche quante istanze della dimensione desiderata a cui si passa resize()come argomento. Ma reserve()alloca solo la memoria, non crea istanze. Questo è,

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

Output ( demo online ):

1
1
0
1

Quindi resize()potrebbe non essere desiderabile, se non si desidera gli oggetti creati per impostazione predefinita. Sarà anche lento. Inoltre, se si push_back()aggiungono nuovi elementi, il size()vettore aumenterà ulteriormente allocando nuova memoria (il che significa anche spostare gli elementi esistenti nello spazio di memoria appena allocato). Se hai usato reserve()all'inizio per assicurarti che ci sia già abbastanza memoria allocata, il size()vettore aumenterà quando ci sei push_back(), ma non allocherà di nuovo nuova memoria fino a quando non esaurirà lo spazio che ti è stato riservato .


6
Dopo averlo fatto reserve(N), possiamo usare operator []innocuamente. corretta ?
iammilind,

2
Mentre la maggior parte delle implementazioni assegnerà l'importo esatto richiesto reserve, la specifica richiede solo che alloca almeno quel tanto, quindi alcune implementazioni possono arrotondare per eccesso a qualche limite e quindi mostrare una capacità superiore a 1000.
Jan Hudec

16
@iammilind: No, se l'indice è maggiore o uguale a v.size(). Nota che reserve(N)non cambia il size()vettore.
Nawaz,

5
@iammilind: errato. Dopo aver chiamato reSERVE, non vengono aggiunte voci, si ottiene solo memoria sufficiente per aggiungerle.
Jan Hudec,

2

Dalla tua descrizione, sembra che tu voglia "riservare" lo spazio di archiviazione allocato di t_Names vettoriali.

Prendi nota che resizeinizializza il vettore reserveappena allocato dove alloca ma non costruisce. Quindi, "riserva" è molto più veloce di "ridimensiona"

È possibile fare riferimento alla documentazione relativa alla differenza tra ridimensionamento e prenotazione


1
Si prega di fare riferimento qui invece: vettore e capacità ( perché? )
vedi il

1
Grazie per l'aggiunta del link, vedi
dip

2

riservare quando non si desidera inizializzare gli oggetti quando riservati. inoltre, potresti preferire differenziare logicamente e tenere traccia del suo conteggio rispetto al conteggio degli usi quando ridimensioni. quindi c'è una differenza comportamentale nell'interfaccia: il vettore rappresenterà lo stesso numero di elementi quando riservato e sarà più grande di 100 elementi quando ridimensionato nel tuo scenario.

C'è qualche scelta migliore in questo tipo di scenario?

dipende interamente dai tuoi obiettivi quando combatti il ​​comportamento predefinito. alcune persone favoriranno gli allocatori personalizzati, ma abbiamo davvero bisogno di un'idea migliore di ciò che stai cercando di risolvere nel tuo programma per consigliarti bene.

in seguito, molte implementazioni vettoriali raddoppieranno semplicemente il numero di elementi allocati quando devono crescere - stai cercando di ridurre al minimo le dimensioni di allocazione dei picchi o stai cercando di riservare spazio sufficiente per qualche programma senza blocco o qualcos'altro?


" riserva quando non vuoi che gli oggetti vengano inizializzati quando riservati. " La formulazione corretta è quando non vuoi che gli oggetti esistano . Non è come una matrice non inizializzata di tipo banalmente costruibile, in cui gli oggetti non possono essere letti ma possono essere assegnati; piuttosto, solo la memoria è riservata, ma non esistono oggetti in essa, quindi non è possibile accedervi utilizzando operator[]o altro.
underscore_d
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.