2015-05-04 15 views
45

Alcune persone non sanno che è possible to pass and return structs by value in C. La mia domanda riguarda il compilatore che esegue copie non necessarie quando restituisce le strutture in C. I compilatori C come GCC usano l'ottimizzazione Return value optimization(RVO) o si tratta di un concetto solo C++? Tutto ciò che ho letto su RVO e su copia elision riguarda il C++.Ottimizzazione del valore di ritorno e copia elisione in C

Consideriamo un esempio. Attualmente sto implementando uno double-double data type in C (o meglio float-float per iniziare perché trovo semplice test dell'unità). Considera il seguente codice.

typedef struct { 
    float hi; 
    float lo; 
} doublefloat; 

doublefloat quick_two_sum(float a, float b) { 
    float s = a + b; 
    float e = b - (s - a); 
    return (doublefloat){s, e}; 
} 

Sarà il compilatore fare una copia temporanea del valore doublefloat torno oppure può essere la copia temporanea eliso?

E l'ottimizzazione del valore di ritorno con nome (NRVO) in C? Ho un'altra funzione

doublefloat df64_add(doublefloat a, doublefloat b) { 
    doublefloat s, t; 
    s = two_sum(a.hi, b.hi); 
    t = two_sum(a.lo, b.lo); 
    s.lo += t.hi; 
    s = quick_two_sum(s.hi, s.lo); 
    s.lo += t.lo; 
    s = quick_two_sum(s.hi, s.lo); 
    return s; 
} 

In questo caso sto restituendo una struttura denominata. È possibile elidere la copia temporanea in questo caso?

Si dovrebbe affermare che questa è una domanda generale per C e che gli esempi di codice che ho usato qui sono solo degli esempi (quando ottimizzo questo userò comunque SIMD con intrinseco). Sono consapevole che potrei guardare l'output dell'assembly per vedere cosa fa il compilatore, ma penso che sia comunque una domanda interessante.

+3

@BaummitAugen, non ero sicuro se avrei dovuto usare il tag C++. Ma penso di aver chiarito nella mia domanda che si tratta di C. Speravo che il tag C++ attirasse persone esperte in entrambe le lingue. –

+0

@BaummitAugen c'è un tale concetto in C. Ho rimosso il tag 'C' come mi sembra erreano. –

+0

@IvayloStrandjev la domanda riguarda C, il tag si applica alla domanda, no? – BeyelerStudios

risposta

35

RVO/NRVO sono chiaramente consentito dalla regola "come-se" in C.

In C++ è possibile ottenere osservabili effetti collaterali perché hai sovraccaricato il costruttore, distruttore, e/o operatore di assegnazione per dare quegli effetti collaterali (ad esempio, stampare qualcosa quando accade una di quelle operazioni), ma in C non si ha alcuna capacità di sovraccaricare quegli operatori, e quelli incorporati non hanno effetti collaterali osservabili.

Senza sovraccaricarli, non si ottengono effetti collaterali osservabili da copia elisione, e quindi nulla impedisce a un compilatore di farlo.

+4

L'indirizzo della variabile nella funzione e l'indirizzo della variabile assegnata all'esterno possono essere identici in quanto hanno una durata non sovrapposta: il bridge temporaneo non può avere l'indirizzo preso, e non può condividere l'indirizzo della seconda variabile rilevato. Questo, penso, rende impossibile rilevare che sia successo (in teoria) sotto lo standard: in pratica, se la variabile nella funzione e la variabile assegnata all'esterno hanno lo stesso indirizzo, le probabilità sono che tu sia testimone di NRVO. – Yakk

+1

Provando questo su gcc, g ++, clang, clang ++, si scopre che ognuno fa correttamente NRVO eccetto gcc '-xc', che genera copie superflue quando si ha:' struct s f() {struct s x = g(); ritorno x; } ' – Peaker

31

Il motivo per cui è coperto molto per C++ è perché in C++, RVO ha effetti collaterali (cioè non chiama il distruttore degli oggetti temporanei né il costruttore di copie o l'operatore di assegnazione degli oggetti risultanti).

In C, non sono possibili effetti collaterali, ma solo potenziali miglioramenti delle prestazioni. Non vedo ragioni per cui un simile compilatore non possa essere eseguito da qualche compilatore. Almeno, non c'è nulla che lo proibisca nello standard.

In ogni caso, l'ottimizzazione dipende dal livello del compilatore e dall'ottimizzazione, quindi non ci scommetterei su percorsi di codice critici, a meno che il compilatore utilizzato sia ben definito e non previsto di modifiche (il che è ancora spesso il caso).

Problemi correlati