Questa è la natura compilatori. Supponendo che prenderanno il percorso più veloce o migliore, è abbastanza falso. Chiunque sottintenda che tu non debba fare nulla per il tuo codice per ottimizzarlo perché i "compilatori moderni" riempiono lo spazio vuoto, fanno il lavoro migliore, fanno il codice più veloce, ecc. In realtà ho visto gcc peggiorare da 3.x a 4. x sul braccio almeno. 4.x potrebbe aver raggiunto il 3.x da questo punto, ma presto ha prodotto un codice più lento. Con la pratica puoi imparare come scrivere il tuo codice in modo che il compilatore non debba lavorare così duramente e di conseguenza produrre risultati più coerenti e attesi.
Il bug qui è le vostre aspettative su ciò che verrà prodotto, non su ciò che è stato effettivamente prodotto. Se vuoi che il compilatore generi lo stesso output, alimentalo con lo stesso input.Non è matematicamente uguale, non è lo stesso, ma in realtà è lo stesso, non ci sono percorsi diversi, nessuna condivisione o distribuzione di operazioni da una versione all'altra. Questo è un buon esercizio per capire come scrivere il codice e vedere cosa fanno i compilatori con esso. Non commettere l'errore di presupporre che poiché una versione di gcc per un target del processore un giorno ha prodotto un determinato risultato che è una regola per tutti i compilatori e tutto il codice. Devi usare molti compilatori e molti obiettivi per avere un'idea di cosa sta succedendo.
gcc è piuttosto brutto, vi invito a guardare dietro le quinte, a guardare il coraggio di gcc, provare ad aggiungere un obiettivo o modificare qualcosa da soli. E 'a mala pena tenuto insieme da nastro adesivo e cavo di sicurezza. Una riga aggiuntiva di codice aggiunta o rimossa in punti critici e si sgretola. Il fatto che abbia prodotto codice utilizzabile è qualcosa di cui essere contenti, invece di preoccuparsi del perché non ha soddisfatto altre aspettative.
hai visto quali diverse versioni di gcc producono? 3.xe 4.x in particolare 4.5 vs 4.6 vs 4.7, ecc.? e per diversi processori di destinazione, x86, arm, mips, ecc o diversi tipi di x86 se questo è il compilatore nativo che usi, 32 bit vs 64 bit, ecc.? E poi llvm (clang) per obiettivi diversi?
Mystical ha svolto un lavoro eccellente nel processo di pensiero necessario per risolvere il problema dell'analisi/ottimizzazione del codice, aspettandosi che un compilatore possa venire a conoscenza di ciò che è, beh, non previsto da alcun "compilatore moderno".
Senza entrare nelle proprietà di matematica, il codice di questa forma
if (exponent < 0) {
r = mantissa << -exponent; /* diff */
} else {
r = mantissa >> exponent; /* diff */
}
return (r^-sign) + sign; /* diff */
sta per portare al compilatore di A: la sua attuazione in quella forma, effettuare l'if-then-else poi convergere su codice comune per finire e tornare. oppure B: salva un ramo poiché questa è la fine della coda della funzione. Inoltre non preoccuparti di usare o salvare r.
if (exponent < 0) {
return((mantissa << -exponent)^-sign)+sign;
} else {
return((mantissa << -exponent)^-sign)+sign;
}
Poi si può entrare come Mistico ha sottolineato la variabile segno scompare tutti insieme per il codice come scritto. Non mi aspetterei che il compilatore vedesse la variabile del segno andare via, quindi avresti dovuto farlo tu stesso e non costringere il compilatore a cercare di capirlo.
Questa è un'opportunità perfetta per scavare nel codice sorgente di gcc. Sembra che tu abbia trovato un caso in cui l'ottimizzatore ha visto una cosa in un caso e un'altra in un altro caso. Quindi fai il passo successivo e vedi se non riesci a ottenere gcc per vedere quel caso. Ogni ottimizzazione è lì perché alcuni individui o gruppi hanno riconosciuto l'ottimizzazione e intenzionalmente l'hanno messa lì. Per questa ottimizzazione essere lì e lavorare ogni volta che qualcuno deve metterlo lì (e poi testarlo, e poi mantenerlo nel futuro).
Sicuramente non dare per scontato che meno codice è più veloce e più codice è più lento, è molto facile da creare e trovare esempi di ciò che non è vero. Potrebbe accadere più spesso che meno codice sia più veloce di più codice. Come ho dimostrato dall'inizio, è possibile creare più codice per salvare la ramificazione in quel caso o il ciclo, ecc. E avere il risultato netto un codice più veloce.
La linea di fondo si alimenta con una fonte diversa dal compilatore e si aspettava gli stessi risultati. Il problema non è l'output del compilatore ma le aspettative dell'utente. È abbastanza facile dimostrare per un particolare compilatore e processore, l'aggiunta di una riga di codice che rende l'intera funzione notevolmente più lenta. Ad esempio perché cambia a = b + 2; a a = b + c + 2; causa _fill_in_the_blank_compiler_name_ genera un codice radicalmente diverso e più lento? Ovviamente la risposta è che il compilatore ha ricevuto un codice diverso sull'input, quindi è perfettamente valido per il compilatore generare output diversi.(ancora meglio quando si scambiano due righe di codice non correlate e l'output cambia radicalmente) Non esiste una relazione attesa tra la complessità e la dimensione dell'input per la complessità e la dimensione dell'output. Feed qualcosa di simile in clang:
for(ra=0;ra<20;ra++) dummy(ra);
Ha prodotto da qualche parte tra 60-100 linee di assemblatore. Ha srotolato il ciclo. Non ho contato le righe, se ci pensate, deve aggiungere, copiare il risultato nell'input alla chiamata di funzione, effettuare la chiamata di funzione, minimo tre operazioni. quindi a seconda dell'obiettivo che è probabilmente 60 istruzioni almeno, 80 se quattro per ciclo, 100 se cinque per ciclo, ecc.
Per scopi di test, ho creato un gist [qui] (https://gist.github.com/2430364) in cui è possibile copiare/incollare facilmente l'origine e vedere se è possibile riprodurre il bug su altri sistemi/versioni di GCC . – orlp
Metti i casi di test in una directory a parte loro. Compilili con '-S -O3 -da -fdump-tree-all'. Questo creerà molte istantanee della rappresentazione intermedia. Cammina attraverso di loro (sono numerati) fianco a fianco e dovresti essere in grado di trovare l'ottimizzazione mancante nel primo caso. – zwol
Mi azzardo a indovinare e dico che hai quasi risposto alla tua stessa domanda con il tuo flag gcc -O3. Prova a impostarlo su -0 e poi noterai che sono più simili. Quindi è a causa dell'ottimizzazione. Non posso rispondere a come funzionano le specifiche, ma ricordo che quando abbiamo scritto un ottimizzatore per il nostro compilatore all'università abbiamo usato trucchi molto pazzi per ottimizzare il nostro output. – Mads