2013-10-18 14 views
9

Questo codice:C++ ottimizzazione del valore di ritorno

#include <vector> 

std::vector<float> getstdvec() { 
    std::vector<float> v(4); 

    v[0] = 1; 
    v[1] = 2; 
    v[2] = 3; 
    v[3] = 4; 

    return v; 
} 

int main() { 
    std::vector<float> v(4); 

    for (int i = 0; i != 1000; ++i) 
    { 
     v = getstdvec(); 
    } 
} 

mia comprensione non corretta qui è che la funzione getstdvec non dovrebbe avere per allocare in realtà il vettore che è il ritorno. Quando eseguo questo in valgrind/callgrind, vedo che ci sono 1001 chiamate a malloc; 1 per la dichiarazione iniziale del vettore in main e 1000 per ogni iterazione del ciclo.

Cosa dà? Come posso restituire un vettore (o qualsiasi altro oggetto) da una funzione come questa senza doverla allocare ogni volta?

modifica: sono consapevole di poter passare semplicemente il vettore per riferimento. Avevo l'impressione che fosse possibile (e anche preferibile) scrivere una funzione come questa che restituisca un oggetto senza incorrere in un'assegnazione non necessaria.

+0

Per la modifica: abbiamo bisogno di un esempio reale del problema che stai cercando di risolvere piuttosto che di questo codice di esempio estremamente ridotto per fornire una soluzione non pass-per-referenza. –

+0

@MarkB, è davvero così semplice: voglio una funzione che restituisca un vettore senza dover effettuare una copia/allocazione non necessaria. Avevo l'impressione che qualcosa riguardante RVO o rvalues ​​rendesse possibile questa cosa molto semplice. Un semplice esempio del mondo reale proverebbe a fare y = k * x per i vettori ye x, scalare k. La funzione pass-by-reference tradizionale sembrerebbe 'void mult (const float & k, const vec & x, vec & y)'. Ma chiaramente una chiamata di funzione 'y = mult (k, x)' è preferibile a 'mult (k, x, y)'. – Aurelius

+1

RVO (Return value optimization) è qualcosa che il compilatore fa al tuo codice. Il tuo codice deve prima fare qualcosa che può essere ottimizzato (passa un temporaneo che viene nuovamente assegnato allo stesso oggetto, ad esempio). Potresti aver guardato quel codice e pensato - hmm, potrei ottimizzarlo passando un riferimento a getstdvec. Perché il compilatore non lo fa? Bene, il passaggio di un riferimento NON è implicito nel codice. Puoi solo aspettarti che il compilatore ottimizzi le cose che fa il tuo codice, non cose che potrebbe fare. – iheanyi

risposta

1

La risposta più semplice è passare l'oggetto vettoriale già creato nella funzione.

std::vector<float> getstdvec(std::vector<float> &myvec){ 

In questo caso non si ha davvero tornare così

void getstdvec(std::vector<float> &myvec){ 
3

si può passare per riferimento ... copiare elisione fa in modo che v = getstdvect() alloca v (nella tua principale) direttamente alla v (nel tuo getstdvec()) e salta la copia solitamente associata alla restituzione per valore, ma NON salterà la v (4) nella tua funzione. Per fare questo, è necessario prendere il vettore in per riferimento:

#include <vector> 
void getstdvec(std::vector<float>& v){ 
    v.resize(4);//will only realocate if v is wrong size 
    v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4; 
    return v; 
} 
int main() { 
    std::vector<float> v(4); 
    for (int i=0; i!=1000;++i) 
    getstdvec(v); 
} 
1

Come posso restituire un vettore (o qualsiasi altro oggetto) da una funzione come questa, senza dover allocare ogni tempo?

nel vostro senso, si dichiara un vettore locale con dimensioni 4, in modo che ogni volta che la funzione viene chiamata, che sta per allocare la memoria. Se si intende che si modifica sempre sullo stesso vettore, si può prendere in considerazione di passare il vettore per riferimento.

Ad esempio:

void getstdvec(std::vector<float>& vec) 
{        //^^ 
    //do something with vec 
} 

all'interno main, si dichiara il vettore e allocare lo spazio come quello che hai fatto. A questo punto effettuare le seguenti operazioni:

for (int i=0; i!=1000;++i) 
{  //^^^minor: Don't use magic number in the code like this, 
     //define a const instead 
    getstdvec(vec); 
} 
1

in vece utilizzare il valore di ritorno, è possibile utilizzare un riferimento:

void getstdvec(std::vector<float> &v) 

che può evitare la copia di oggetto temporaneo

2

Stai facendo copia -assegnazione del ciclo, non costruzione della copia. L'ottimizzazione RVO si applica solo alla costruzione di variabili da un valore di ritorno, non assegnandole.

Non riesco a capire il vero problema che stai cercando di risolvere qui. Con maggiori dettagli potrebbe essere possibile fornire una buona risposta che risolva il problema sottostante.

Allo stato attuale, per tornare dalla funzione in questo modo è necessario creare un vettore temporaneo da restituire ogni volta che viene chiamata la funzione.

15

Quando si chiama una funzione, per un tipo di ritorno come std::vector<T> il compilatore fornisce memoria per l'oggetto restituito. La funzione chiamata è responsabile della costruzione dell'istanza restituita in questo slot di memoria.

Il RVO/NRVO ora consente al compilatore di omettere la creazione di un oggetto temporaneo locale, copiando il valore restituito nello slot di memoria da esso, distruggendo l'oggetto temporaneo e infine tornando al chiamante. Invece, la funzione chiamata semplicemente costruisce l'oggetto locale nella memoria dello slot di ritorno direttamente e alla fine della funzione, restituisce semplicemente.

Dal punto di vista del chiamante, questo è trasparente: fornisce memoria per il valore restituito e quando viene richiamata la funzione, c'è un'istanza valida. Il chiamante ora può usare questo oggetto ed è responsabile per chiamare il distruttore e liberare la memoria in seguito.

Ciò significa che il RVO/NRVO funziona solo quando si chiama una funzione per costruire una nuova istanza, non quando la si assegna. Il seguente è un esempio in cui potrebbe essere applicato RVO/NRVO:

std::vector<float> v = getstdvec(); 

ma codice originale utilizza un ciclo e in ogni iterazione, il risultato da getstdvec() deve essere costruito e questo temporaneo è assegnata v. Non c'è modo che il RVO/NRVO possa rimuoverlo.

Problemi correlati