c++ - quante - parole chiave seo



Le parole chiave di ottimizzazione in C e C++ sono ragionevoli? (6)

È un bersaglio mobile, perché la tecnologia del compilatore sta migliorando. (Beh, a volte è più cambiando che migliorando, ma questo ha lo stesso effetto di rendere i tuoi tentativi di ottimizzazione discutibili, o peggio.)

In generale, non devi indovinare se una parola chiave di ottimizzazione o altra tecnica di ottimizzazione è buona o meno. Uno deve imparare un po 'su come funzionano i computer, compresa la piattaforma specifica che si sta prendendo di mira e su come funzionano i compilatori.

Quindi una regola sull'utilizzo di varie tecniche di ottimizzazione è chiedere se so che il compilatore non farà il lavoro migliore qui? Sono disposto a impegnarmi per un po 'di tempo - il compilatore rimarrà stabile mentre questo codice è in uso, sono disposto a riscrivere il codice quando il compilatore cambia questa situazione? In genere, devi essere un ingegnere esperto e esperto per sapere quando puoi fare meglio del compilatore. Aiuta anche se puoi parlare con gli sviluppatori del compilatore.

Ciò significa che le persone non possono darti una risposta che abbia una linea guida definita. Dipende da quale compilatore stai usando, qual è il tuo progetto, quali sono le tue risorse e quali sono i tuoi obiettivi e così via.

Anche se alcune persone dicono di non provare a ottimizzare il compilatore, ci sono varie aree di ingegneria del software in cui le persone fanno meglio di un compilatore e in cui vale la pena pagare le persone per questo.

Quindi abbiamo tutti ascoltato la linea di register non usare, il ragionamento è che cercare di ottimizzare un compilatore è una commissione da pazzi.

register , da quello che so, in realtà non dice nulla sui registri della CPU, solo che una variabile data non può essere referenziata indirettamente. Mi azzardo a supporre che venga spesso definito obsoleto perché i compilatori possono rilevare automaticamente la mancanza di indirizzamento rendendo così trasparenti tali ottimizzazioni.

Ma se siamo fermi su questo argomento, non può essere livellato in ogni parola chiave basata sull'ottimizzazione in C? Perché utilizziamo le restrict inline e C99 ad esempio?

Suppongo che alcune cose come l'aliasing rendano la deduzione di alcune ottimizzazioni difficili o addirittura impossibili, quindi dov'è la linea tracciata prima di iniziare ad avventurarsi in territorio Sufficientemente Smart Compiler ?

Dove dovrebbe essere tracciata la linea in C e C ++ tra un cucchiaio e l'alimentazione di un'informazione di ottimizzazione del compilatore e assumendo che sappia cosa sta facendo?

EDIT : Jens Gustedt ha sottolineato che la mia fusione di C e C ++ non è corretta poiché due delle parole chiave hanno differenze semantiche e una non esiste nel C ++ standard. Ho avuto un buon link sul register in C ++ che aggiungerò se lo trovo ...


Answer #1

Concordo sul fatto che il register e la inline siano in qualche modo simili a questo riguardo. Se il compilatore può vedere il corpo del callee durante la compilazione di un sito di chiamata, dovrebbe essere in grado di prendere una buona decisione sull'inlining. L'uso della parola chiave inline in C e C ++ ha più a che fare con la meccanica di rendere il corpo della funzione visibile che con qualsiasi altra cosa.

restrict , tuttavia, è diverso. Quando compila una funzione, il compilatore non ha idea di cosa saranno i siti di chiamata. Essere in grado di non assumere aliasing può consentire ottimizzazioni che altrimenti sarebbero impossibili.


Answer #2

La differenza è la seguente:

  • register è un'ottimizzazione molto locale (cioè all'interno di una funzione). L'allocazione dei registri è un problema relativamente risolto sia dai compilatori più intelligenti sia da un numero più grande di registri (principalmente il primo ma dicono che x86-64 ha più registri quindi x86 ed entrambi hanno un numero più grande quindi dicono processore a 8 bit)
  • inline è più difficile in quanto è l'ottimizzazione inter-procedura. Tuttavia, dal momento che comporta una profondità di ricorsione relativamente piccola e un numero ridotto di procedure (se la procedura in linea è troppo grande non si ha la sensazione di inserirlo) potrebbe essere lasciato al compilatore in sicurezza.
  • restrict è molto più difficile. Per sapere a fondo che i due puntatori non sono alias, è necessario analizzare l'intero programma (incluse librerie, sistema, plug-in, ecc.) E anche in questo caso incorrere in problemi. Tuttavia l'informazione è più chiara per programmatore E fa parte delle specifiche.

Prendi in considerazione un codice molto semplice:

void my_memcpy(void *dst, const void *src, size_t size) {
    for (size_t i = 0; i < size; i++) {
        ((char *)dst)[i] = ((const char *)str)[i];
    }
}

C'è un vantaggio nel rendere questo codice efficiente? Sì, la memcpy tende ad essere molto utile (ad esempio per copiare GC). Questo codice può essere vettorizzato (qui - spostato dalle parole - diciamo 128b anziché 8b)? Il compilatore dovrebbe dedurre che dst e src non hanno alias in alcun modo e le regioni da loro indicate sono indipendenti. size possono dipendere dall'input dell'utente o dal comportamento in fase di esecuzione o da altri elementi che rendono praticamente impossibile l'analisi - problemi simili al problema dell'arresto - in generale non possiamo analizzare tutto senza eseguirlo. Oppure potrebbe essere parte della libreria C (presumo librerie condivise) e viene chiamata dal programma, quindi tutti i siti di chiamata non sono nemmeno conosciuti al momento della compilazione. Senza tale analisi il programma mostrerebbe un comportamento diverso con l'ottimizzazione. D'altra parte il programmatore potrebbe assicurarsi che si tratti di oggetti diversi semplicemente conoscendo il progetto (anche di livello superiore) anziché la necessità di analisi bottom-up.

restrict possono anche essere parte della documentazione in quanto potrebbe essere un programmatore che ha scritto la procedura in modo che non possa gestire 2 puntatori aliasing. Ad esempio, se vogliamo copiare la memoria da posizioni di aliasing, il codice sopra riportato non è corretto.

Quindi, per riassumere - Sufficientemente Smart Compiler non sarebbe in grado di dedurre i restrict (a meno che non ci spostiamo verso i compilatori che comprendono il significato del codice) senza conoscere l'intero programma. Anche allora sarebbe vicino alla indecidibilità. Tuttavia, per l' ottimizzazione locale i compilatori sono già sufficientemente intelligenti. La mia ipotesi è che il compilatore sufficientemente intelligente con l'intera analisi del programma sarebbe in grado di dedurre in molti casi interessanti comunque.

PS. Per locale intendo una singola funzione. Quindi l'ottimizzazione locale non può assumere nulla su argomenti, variabili globali, ecc.


Answer #3

Questa è una domanda infuocata, ma mi tufferò comunque.

I compilatori sono molto più bravi a ottimizzare il tuo programmatore medio. C'è stato un tempo in cui ho programmato un 2580Hz a 68030 e ho avuto qualche vantaggio dall'uso del register perché l'ottimizzatore del compilatore era così scarso. Ma questo era nel 1990.

Vedo in inline tanto male quanto register .

In generale, misurare prima di modificare. Se ti accorgi che il tuo codice funziona così male, vuoi usare il register o la inline , fare un respiro profondo, stare indietro e cercare prima un algoritmo migliore.

Negli ultimi tempi (ovvero negli ultimi 5 anni) ho seguito le basi di codice e rimosso le funzioni inline bizzeffe, senza alcun visibile cambiamento nelle prestazioni. Le dimensioni del codice, tuttavia, beneficiano sempre della rimozione dei metodi in inline . Questo non è un grosso problema per la tua meraviglia multicore standard in stile x86 dell'età moderna, ma è importante se lavori nello spazio integrato.


Answer #4

inline viene utilizzato nello scenario in cui si implementa una funzione non basata su un modello all'interno dell'intestazione, quindi includerla da più unità di compilazione.

Ciò garantisce che il compilatore crei una sola istanza della funzione come se fosse in linea, in modo da non ottenere un errore di collegamento per il simbolo definito più volte. Tuttavia non richiede che il compilatore lo inline effettivamente.

Ci sono bandiere GNU che ritengo sia in linea o simili, ma si tratta di un'estensione del linguaggio.


Answer #5

register non dice nemmeno che non puoi fare riferimento alla variabile indirettamente (almeno in C ++). Lo ha detto nel C originale, ma è stato eliminato.

Se cercare di ottimizzare il compilatore è una commissione sbagliata dipende dall'ottimizzazione. Non molti compilatori, ad esempio, convertiranno sin(x) * sin(x) + cos(x) * cos(x) in 1 .

Oggi molti compilatori ignorano il register e nessuno lo usa, perché i compilatori sono diventati bravi nell'assegnazione dei registri per fare un lavoro migliore di quello che si può fare con la register . In effetti, rispettare il register genere renderebbe il codice generato più lento. Questo non è il caso per inline o restrict : in entrambi i casi esistono tecniche, almeno teoricamente, che potrebbero portare al compilatore a fare un lavoro migliore di quello che è possibile. Tali tecniche non sono tuttavia diffuse e (almeno per quanto ne so), hanno un tempo di compilazione molto alto, con in alcuni casi tempi di compilazione che crescono esponenzialmente con le dimensioni del programma (il che li rende più o meno inutilizzabile sulla maggior parte dei programmi reali, i tempi di compilazione misurati in anni non sono accettabili).

Riguardo a dove tracciare la linea ... cambia nel tempo. Quando ho iniziato a programmare in C, il register fatto una differenza significativa ed è stato ampiamente utilizzato. Non oggi. Immagino che col tempo, lo stesso possa accadere con inline o con restrict - alcuni compilatori sperimentali sono già molto vicini a quelli in inline .





compiler-construction