Funzioni inline in C ++. Qual e il punto?

19

Secondo quanto ho letto, il compilatore non è obbligato a sostituire la funzione chiamata di una funzione inline con il suo corpo, ma lo farà se può. Questo mi ha fatto pensare: perché abbiamo la parola inline se questo è il caso? Perché non eseguire tutte le funzioni inline di default e lasciare che sia il compilatore a capire se è possibile sostituire le chiamate con il corpo della funzione o no?

    
posta EpsilonVector 10.01.2011 - 19:14
fonte

6 risposte

41

inline è da C; non era nuovo in C ++.

Esistono parole chiave C ( register e inline ) progettate per consentire al programmatore di assistere nell'ottimizzazione del codice. Al giorno d'oggi questi sono generalmente ignorati, dal momento che i compilatori possono fare meglio l'assegnazione dei registri e decidere quando utilizzare le funzioni inline (infatti, un compilatore può sia inline che non inline una funzione in momenti diversi). La generazione del codice sui processori moderni è molto più complicata rispetto a quelli più deterministici comuni quando Ritchie stava inventando C.

Ciò che la parola significa ora, in C ++, è che può avere più definizioni identiche e deve essere definito in ogni unità di traduzione che lo utilizza. (In altre parole, devi assicurarti che possa essere in linea.) Puoi avere una funzione inline in un'intestazione senza problemi e le funzioni membro definite in una definizione di classe sono automaticamente inline in modo efficace.

    
risposta data 10.01.2011 - 19:27
fonte
27

Originariamente inline era un suggerimento molto strong che le chiamate alla funzione dovrebbero essere sottolineate.

Ma l'unico effetto garantito di inline è di consentire a una funzione di essere definita (efficacemente identicamente) in più unità di traduzione, ad esempio, che la definizione viene inserita in un file di intestazione.

Al giorno d'oggi, alcuni compilatori sono molto interessati a seguire il suggerimento in-line, ad es. g ++. E alcuni compilatori lo prendono meno sul serio, ad es. Visual C ++. Ma tutti devono rispettare la garanzia.

È spiacevole che questi due significati - suggerimento per l'ottimizzazione e quella che potremmo definire una definizione scartata di livello linker - risiedano con la stessa parola chiave, perché significa che non puoi praticamente averne uno senza l'altro.

È anche un peccato che inline (o meglio, una parola chiave separata sulla definizione scartata) non sia applicata a data .

La necessità di dati discardabili a livello di linker è aumentata in quanto i moduli di sola intestazione sono diventati più popolari. Ad esempio, molte librerie secondarie Boost sono solo di intestazione.

Per i dati è possibile, tuttavia, applicare un piccolo trucco con i modelli. Definiscilo in un template di classe, fornisci typedef con parametro template void (o qualsiasi altra cosa). Questo perché la regola One Definition rende un'eccezione specifica per i modelli.

Note:
¹% variabili diinline saranno supportate in C ++ 17 .

    
risposta data 10.01.2011 - 19:33
fonte
6

Perché non rendere tutte le funzioni in linea per impostazione predefinita? Perché è un compromesso di ingegneria. Esistono almeno due tipi di "ottimizzazione": accelerare il programma e ridurre la dimensione (impronta di memoria) del programma. Inlining generalmente accelera le cose. Si sbarazza della funzione chiamata overhead, evitando di spingere e poi tirando i parametri dalla pila. Tuttavia, aumenta anche l'ingombro della memoria del programma, poiché ogni chiamata di funzione deve ora essere sostituita con il codice completo della funzione. Per rendere le cose ancora più complicate, ricorda che la CPU memorizza blocchi di memoria usati frequentemente in una cache della CPU per un accesso ultra rapido. Se si rende l'immagine della memoria del programma abbastanza grande, il programma non sarà in grado di utilizzare la cache in modo efficiente e, nel peggiore dei casi, l'inlining potrebbe rallentare il programma. In una certa misura il compilatore può calcolare quali sono i trade off e potrebbe essere in grado di prendere decisioni migliori di quelle che è possibile, semplicemente guardando il codice sorgente.

    
risposta data 10.01.2011 - 20:11
fonte
3

Per capire "in linea" devi capire la storia e come era la vita 20 (e 30) anni fa

Stavamo scrivendo codice su computer che avevano poca memoria, quindi non era possibile per un compilatore elaborare tutto il codice che componeva un programma in una volta sola. Anche il compilatore era molto lento, quindi non volevi dover ricompilare il codice che non era stato modificato - prendere più di 24 ore (su un computer che costava più di un'auto di fascia alta) per ricompilare tutto il codice era normale per alcuni progetti. lavorato su.

Quindi ogni file di codice è stato compilato separatamente in un file oggetto. Ogni file oggetto è iniziato con l'elenco di tutte le funzioni in esso contenute, insieme con l'"indirizzo" della funzione. Un file oggetto aveva anche un elenco di tutte le funzioni chiamate in altri file oggetto insieme alla posizione della chiamata.

Un linker prima legge tutti i file oggetto e crea un elenco di tutte le funzioni che hanno esportato , insieme al file in cui si trovavano e al loro indirizzo. Quindi rileggerebbe tutti i file oggetto, trasferendoli nel file di programma, aggiornando tutte le chiamate di funzione "esterne" con l'indirizzo della funzione.

Il linker non ha modificato o ottimizzato il codice macchina prodotto dal compilatore in alcun modo se non per correggere i riferimenti a chiamate di funzioni esterne. Il linker faceva parte del sistema operativo e precedeva la maggior parte dei compilatori. Quando le persone scrivevano un nuovo compilatore, avevano bisogno che funzionasse con i linker correnti e di essere in grado di collegarsi ai file oggetto correnti, altrimenti non si potevano effettuare chiamate di sistema.

Il compilatore ha visto solo il codice nel file ".c" o ".cpp" che stava compilando insieme a tutti i file di intestazione inclusi. Quindi non è stato possibile effettuare alcuna ottimizzazione basata sul codice in altri file ".c" o ".cpp".

La parola chiave "in linea" consentiva di definire il corpo di una funzione (metodo) in un file di intestazione, consentendo quindi al compilatore di utilizzare il codice della funzione durante la compilazione del codice che lo chiama. Ad esempio, supponiamo di avere una classe di raccolta definita in un file .cpp anther, questa classe avrebbe un metodo "isEmpty", che conteneva una riga di codice, ci sarebbe una grande accelerazione del programma risultante se invece di una chiamata a una funzione , la chiamata alla funzione è stata sostituita con questa riga.

La parola chiave "in linea" è stata vista al momento come un modo "economico e facile" per consentire l'incapsulamento dei dati evitando il costo delle chiamate di funzione, senza che molti programmatori avessero semplicemente accesso ai campi privati dell'oggetto. (Macro in cui un modo molto peggiore di "allineare" il codice che era comune al momento.)

In questi giorni i "linker" fanno un sacco di ottimizzazione del codice e tendono ad essere scritti da qualche team come compilatore. Il compilatore spesso controlla che il codice sia corretto e lo "comprime", lasciando la maggior parte dell'attività di creazione del codice macchina al linker.

    
risposta data 10.08.2016 - 14:13
fonte
2

Vediamo cosa dice lo standard (evidenziate parti importanti in grassetto):

2. A function declaration with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions shall still be respected.

C++ standard, ISO/IEC 14882:2003, 7.1.2 Function Specifiers [dcl.fct.spec]

Quindi, se vuoi essere sicuro, dovresti leggere la documentazione del tuo compilatore.

Inlining di tutto è una cattiva idea, in quanto potrebbe causare un sacco di codice duplicato della macchina ...

Quindi devi sapere:

There are no simple answers: You have to play with it to see what is best. Do not settle for simplistic answers like, "Never use inline functions" or "Always use inline functions" or "Use inline functions if and only if the function is less than N lines of code." These one-size-fits-all rules may be easy to write down, but they will produce sub-optimal results.

C++ FAQ, Inline Functions, 9.3 Do inline functions improve performance?

    
risposta data 10.01.2011 - 19:52
fonte
1

Lascia che ti dia una buona ragione per usare la parola chiave inline.

Su un sistema integrato, come una stampante di biglietti o un sistema simile più piccolo. Il processore è molto limitato e una chiamata di funzione (preparazione dei parametri di funzione su stack, chiamata, recupero dei parametri dallo stack e risposta put back ecc.) Può richiedere diversi ms per l'esecuzione, oltre alla funzione stessa.

Diciamo che il tempo di chiamata è di circa 60 ms (solo per la chiamata, non la funzione effettiva) e hai 50 iterazioni (loop o chiamate iterative in un albero).

Il tempo di andare avanti e indietro da quella chiamata di funzione richiederà 60 * 50 = 3000 (3 secondi).

Se hai la memoria, faresti sicuramente un inline per salvare 3 secondi.

Quindi inline sono fondamentalmente usati quando hai bisogno di velocità di esecuzione. In alcuni progetti con cui ero coinvolto, il tempo di chiamata era più lungo del tempo di esecuzione, una situazione classica in cui utilizzare in linea.

    
risposta data 19.01.2011 - 14:51
fonte

Leggi altre domande sui tag