Sto considerando l'utilizzo dell'ereditarietà virtuale in un'applicazione in tempo reale. L'utilizzo dell'ereditarietà virtuale ha un impatto sulle prestazioni simile a quello di chiamare una funzione virtuale? Gli oggetti in questione verrebbero creati solo all'avvio, ma sono preoccupato che tutte le funzioni della gerarchia vengano inviate tramite un vtable o se solo quelle della classe di base virtuale siano.Impatto sulle prestazioni dell'ereditarietà virtuale
risposta
implementazioni comuni renderanno l'accesso a membri dati delle classi base virtuali utilizzano un riferimento indiretto aggiuntivo.
Come James fa notare nei suoi commenti, la chiamata di una funzione membro di una classe base in uno scenario di ereditarietà multipla richiede la regolazione del puntatore this
e, se tale classe di base è virtuale, l'offset della classe di base sub- l'oggetto nell'oggetto della classe derivata dipende dal tipo dinamico della classe derivata e dovrà essere calcolato in fase di runtime.
Se questo ha alcun impatto sulle prestazioni visibile sulle applicazioni del mondo reale dipende da molte cose:
fare basi virtuali avere membri di dati a tutti? Spesso, si tratta di classi di base astratte che devono essere derivate virtualmente, e le basi astratte che hanno qualsiasi membro di dati sono spesso un odore di codice in ogni caso.
a patto di avere basi virtuali con membri di dati, sono quelli accessibili in un percorso critico? Se un utente che fa clic su un pulsante in una GUI restituisce alcune dozzine di riferimenti indiretti aggiuntivi, nessuno se ne accorgerà.
Quale sarebbe il alternativa se le basi virtuali sono evitati? Non solo il design potrebbe essere inferiore, ma è anche probabile che anche il design alternativo abbia un impatto sulle prestazioni. Dopotutto deve raggiungere lo stesso obiettivo e TANSTAAFL. Poi hai scambiato una perdita di prestazioni per un'altra e un design inferiore.
Nota aggiuntiva: Date un'occhiata a Stan Lippmann di Inside the C++ Object Model, che risponde molto bene a queste domande.
Per chiarire, solo le chiamate alle classi di base virtuali membri/funzioni risultano in un'ulteriore indiretta? – Graeme
@Graeme: Non sono certo esperto in questo settore, ma non vedo come le chiamate al membro della classe base virtuale _functions_ comportino una perdita di prestazioni. Vengono inviati staticamente ('B :: f()') o dinamicamente attraverso la tabella virtuale della classe derivata, proprio come le funzioni membro di basi non virtuali. ICBWT. – sbi
@sbi: se la funzione non è virtuale, la funzione da chiamare può essere selezionata staticamente, ma il puntatore 'this' deve essere calcolato [o cercato] in fase di esecuzione, giusto? –
Sei sicuro che intendi l'ereditarietà virtuale? Se è così, è identico al costo di una normale chiamata di funzione virtuale. La ricerca concatenata vtable segue solo un percorso specificato.
Hai detto che questo era all'avvio. Il sovraccarico del disco (dal semplice caricamento del codice in memoria) richiederà probabilmente ordini di grandezza più lunghi rispetto alla mezza dozzina di istruzioni o così per ricerche vtable. Sarei un po 'sorpreso se potessi profilare questo e rilevare una differenza.
Dai uno sguardo al seguente studio sperimentale su larga scala pubblicato OOPSLA'96. Copio copia una voce bibtex, l'abstract e un link alla carta. Considererei questo lo studio sperimentale più completo sull'argomento fino ad oggi.
@article{driesen1996direct,
title={{The direct cost of virtual function calls in C++}},
author={Driesen, K. and H{\\"o}lzle, U.},
journal={ACM Sigplan Notices},
volume={31},
number={10},
pages={306--323},
issn={0362-1340},
year={1996},
publisher={ACM}
}
Abstract: Studiamo il costo diretto della funzione virtuale chiamate in programmi C++, assumendo la implementazione standard utilizzando le tabelle delle funzioni virtuali. Noi misuriamo questo sovraccarico sperimentalmente per un numero di programmi di benchmark di grandi dimensioni , utilizzando una combinazione di ispezione eseguibile e simulazione del processore. I nostri risultati mostrano che i programmi C++ misurati spendono una mediana del 5,2% del loro tempo e il 3,7% delle loro istruzioni nel codice di spedizione. Per le versioni "tutti i virtuali" dei programmi, l'overhead mediano sale a 13,7% (13% delle istruzioni). La variante "thunk" dell'implementazione della tabella di funzione virtuale riduce l'overhead di una mediana del 21% rispetto all'implementazione standard . Sui processori futuri, questi spese dovrebbero aumentare moderatamente
+1 per la bella referenza. –
Non sarei sorpreso se le ottimizzazioni del compilatore rendessero obsoleto questo studio. Fu pubblicato nel 1996. –
Um. Ma la domanda riguardava l'ereditarietà ___virtual___, non le ___virtual functions___. – sbi
A meno che non si sta utilizzando l'ereditarietà multipla, non c'è davvero la necessità di utilizzare l'ereditarietà virtuale. –
@ZacHowland, a meno che tu non stia usando il mocking con Google Test. (gtest) –
@Amigable: non c'è "a meno". Quando utilizzi Google Test, usi ancora l'ereditarietà virtuale solo quando gestisci l'ereditarietà multipla. –