Nel caso generale (essere precisi qui è difficile), i globals vengono recuperati dalla memoria ma non dallo stack (a meno che non siano già memorizzati in un registro), i loop possono essere ottimizzati a seconda delle informazioni che il compilatore ha su ciò che loop fa (può eseguire lo srotolamento del loop?) e nel terzo caso dipende dal codice effettivo.Poiché i primi due sono già stati trattati in altre domande, mi concentrerò sulla terza domanda.
Esiste un'ottimizzazione comune denominata (denominata) Return Value Optimization (N) RVO che il compilatore può eseguire per evitare copie non necessarie.
// RVO // NRVO // cannot perform RVO
type foo() { type bar() { type baz() {
value a; type a; type a,b;
// operate on a // modify a // pass a and b to other functions
return type(a); return a; if (random() > x) return a;
} } else return b;
}
Sia foo
e bar
, il compilatore è in grado di analizzare il codice e determinare che il temporaneo type(a)
in foo
o il nome locale variabile a
in bar
sono il valore di ritorno della funzione, in modo che possa costruisci quegli oggetti al posto del valore di ritorno (secondo la convenzione di chiamata) ed evita di copiarlo. Contrariamente a quello con baz
in cui il compilatore deve creare oggetti a
e b
prima di sapere effettivamente quale deve essere restituito. In questo caso il compilatore non può ottimizzare nulla, deve eseguire le operazioni e solo alla fine copia o a
o al valore restituito.
Ogniqualvolta sulle prestazioni del compilatore (N) RVO o se non é effettivamente impossibile da eseguire, la modifica della firma funzione di ricevere l'oggetto con riferimento non fornirà un vantaggio di prestazione e farà codice sul luogo di chiamata meno leggibile per funzioni che crea nuovi oggetti.
Questo dovrebbe essere usato come una regola generale, ma notando che come sempre, ci sono delle eccezioni, e casi in cui uno o l'altro potrebbe essere leggermente migliore delle prestazioni. Ma per la maggior parte dei casi, e a meno che la misurazione delle prestazioni non si riveli altrimenti, dovresti scrivere il codice il più vicino possibile alla semantica del design. Se una funzione crea un nuovo oggetto, restituiscilo per valore, se una funzione modifica un oggetto, passa per riferimento.
Alcuni casi speciali possono essere una funzione che crea vettori e viene chiamato in un ciclo chiuso, dove avere un singolo vettore che viene passato per riferimento, cancellato nella funzione e quindi riempito ridurrà il numero di allocazioni di memoria (clear()
in un vettore non rilascia la memoria, quindi non ha bisogno di riallocarlo nella prossima iterazione).
All'altro estremo, quando le chiamate di funzione sono concatenate e con la combinazione corretta di valore di ritorno e passaggio per valore, è possibile evitare copie aggiuntive non passando i riferimenti in -a riferimento non-const richiede un non- oggetto temporaneo.
(1) né, (2) non lo sono, (3) non è necessario, a causa di NRVO. –
Cosa intendi nel punto 3.? Passare gli argomenti per funzionare in base al riferimento o restituire il valore della funzione con uno degli argomenti della funzione. –
@Pawel: RVO significa Return Value Optimization, che significa costruire il valore sul posto (dove viene restituito) piuttosto che incorrere nel costo di una copia. Quando si attiva questa ottimizzazione, restituire per valore non costa più del passaggio di un riferimento o di un puntatore. –