No, ma sì, ma forse, ma forse viceversa, ma no.
Come le persone hanno già sottolineato, (supponendo un linguaggio in cui l'addizione è associativa di sinistra, come C, C ++, C # o Java) l'espressione ((1 + 2) + 3)
è esattamente equivalente a1 + 2 + 3
. Sono modi diversi di scrivere qualcosa nel codice sorgente, che non avrebbero alcun effetto sul codice macchina o sul codice byte risultante.
In entrambi i casi il risultato sarà un'istruzione per esempio per aggiungere due registri e quindi aggiungere un terzo, o prendere due valori da uno stack, aggiungerlo, spingerlo indietro, quindi prenderlo e un altro e aggiungerli o aggiungere tre registri in una singola operazione o un altro modo per sommare tre numeri a seconda di ciò che è più sensato al livello successivo (il codice macchina o il codice byte). Nel caso del codice byte, ciò a sua volta subirà probabilmente una simile ristrutturazione in quella, ad esempio, l'equivalente IL di questo (che sarebbe una serie di carichi in uno stack e le coppie di pop-up da aggiungere e quindi respingere il risultato) non comporterebbe una copia diretta di tale logica a livello di codice macchina, ma qualcosa di più sensato per la macchina in questione.
Ma c'è qualcosa in più alla tua domanda.
Nel caso di qualsiasi compilatore C, C ++, Java o C # sano, mi aspetterei che il risultato di entrambe le istruzioni fornite fornisca esattamente gli stessi risultati di:
int a = 6;
Perché il codice risultante dovrebbe perdere tempo a fare matematica sui valori letterali? Nessuna modifica allo stato del programma fermerà il risultato 1 + 2 + 3
dell'essere 6
, quindi è quello che dovrebbe andare nel codice in esecuzione. Anzi, forse nemmeno quello (a seconda di cosa fai con quel 6, forse possiamo buttare via tutto; e anche C # con la sua filosofia di "non ottimizzare pesantemente, poiché il jitter ottimizzerà comunque questo" produrrà l'equivalente di int a = 6
o semplicemente buttare via tutto come non necessario).
Questo però ci porta a una possibile estensione della tua domanda. Considera quanto segue:
int a = (b - 2) / 2;
/* or */
int a = (b / 2)--;
e
int c;
if(d < 100)
c = 0;
else
c = d * 31;
/* or */
int c = d < 100 ? 0 : d * 32 - d
/* or */
int c = d < 100 && d * 32 - d;
/* or */
int c = (d < 100) * (d * 32 - d);
(Nota, questi ultimi due esempi non sono C # validi, mentre tutto il resto qui è e sono validi in C, C ++ e Java.)
Anche in questo caso abbiamo esattamente un codice equivalente in termini di output. Poiché non sono espressioni costanti, non verranno calcolate al momento della compilazione. È possibile che una forma sia più veloce di un'altra. Qual è più veloce? Ciò dipenderebbe dal processore e forse da alcune differenze piuttosto arbitrarie nello stato (soprattutto perché se uno è più veloce, non è probabile che sia molto più veloce).
E non sono del tutto estranei alla tua domanda, in quanto riguardano principalmente le differenze nell'ordine in cui qualcosa viene concettualmente fatto.
In ciascuno di essi, c'è un motivo per sospettare che uno potrebbe essere più veloce dell'altro. I singoli decrementi possono avere un'istruzione specializzata, quindi (b / 2)--
potrebbero essere più veloci di (b - 2) / 2
. d * 32
potrebbe forse essere prodotto più velocemente trasformandolo in d << 5
così da renderlo d * 32 - d
più veloce di d * 31
. Le differenze tra gli ultimi due sono particolarmente interessanti; uno consente in alcuni casi di saltare alcune elaborazioni, ma l'altro evita la possibilità di errori di previsione del ramo.
Quindi, questo ci lascia con due domande: 1. L'uno è effettivamente più veloce dell'altro? 2. Un compilatore converte il più lento nel più veloce?
E la risposta è 1. Dipende. 2. Forse.
O per espandersi, dipende perché dipende dal processore in questione. Certamente ci sono stati processori in cui l'equivalente in codice macchina ingenuo di uno sarebbe più veloce dell'equivalente in codice macchina ingenuo dell'altro. Nel corso della storia dell'informatica elettronica, non ce n'è mai stato uno che fosse sempre il più veloce (l'elemento di previsione errata del ramo in particolare non era rilevante per molti quando le CPU senza pipeline erano più comuni).
E forse, poiché ci sono un sacco di diverse ottimizzazioni che compileranno (e nervosismo e motori di script), e mentre alcuni possono essere obbligati in alcuni casi, in genere saremo in grado di trovare alcuni pezzi di codice logicamente equivalente che anche il compilatore più ingenuo ha esattamente gli stessi risultati e alcuni pezzi di codice logicamente equivalente in cui anche il più sofisticato produce un codice più veloce per l'uno che per l'altro (anche se dobbiamo scrivere qualcosa di totalmente patologico solo per dimostrare il nostro punto).
Potrebbe sembrare una minuscola preoccupazione per l'ottimizzazione,
No. Anche con differenze più complicate di quelle che do qui, sembra una preoccupazione assolutamente minima che non ha nulla a che fare con l'ottimizzazione. Semmai, è una questione di pessimizzazione poiché sospetti che la lettura più difficile ((1 + 2) + 3
potrebbe essere più lenta di quella più facile da leggere 1 + 2 + 3
.
ma la scelta di C ++ su C # / Java / ... significa ottimizzazioni (IMHO).
Se questo è davvero ciò che scegliere C ++ su C # o Java era "tutto" direi che le persone dovrebbero masterizzare la loro copia di Stroustrup e ISO / IEC 14882 e liberare lo spazio del loro compilatore C ++ per lasciare spazio ad altri MP3 o qualcosa del genere.
Queste lingue hanno diversi vantaggi l'una sull'altra.
Uno di questi è che C ++ è ancora generalmente più veloce e leggero nell'uso della memoria. Sì, ci sono esempi in cui C # e / o Java sono più veloci e / o hanno un migliore utilizzo della memoria per tutta la durata dell'applicazione e questi stanno diventando più comuni man mano che le tecnologie coinvolte migliorano, ma possiamo ancora aspettarci che il programma medio scritto in C ++ sia un eseguibile più piccolo che fa il suo lavoro più velocemente e utilizza meno memoria dell'equivalente in una di queste due lingue.
Questa non è ottimizzazione.
L'ottimizzazione è talvolta usata per significare "rendere le cose più veloci". È comprensibile, perché spesso quando parliamo davvero di "ottimizzazione", stiamo effettivamente parlando di far andare le cose più velocemente, e quindi uno è diventato una scorciatoia per l'altro e ammetterò di abusare della parola in quel modo.
La parola corretta per "rendere le cose più veloci" non è ottimizzazione . La parola corretta qui è miglioramento . Se apporti una modifica a un programma e l'unica differenza significativa è che ora è più veloce, non è ottimizzato in alcun modo, è solo meglio.
L'ottimizzazione è quando apportiamo un miglioramento per quanto riguarda un aspetto e / o un caso particolari. Esempi comuni sono:
- Ora è più veloce per un caso d'uso, ma più lento per un altro.
- Ora è più veloce, ma utilizza più memoria.
- Ora è più leggero in memoria, ma più lento.
- Ora è più veloce, ma più difficile da mantenere.
- Ora è più facile da mantenere, ma più lento.
Tali casi sarebbero giustificati se, ad esempio:
- Il caso d'uso più veloce è più comune o più gravemente ostacolato all'inizio.
- Il programma era inaccettabilmente lento e abbiamo molta RAM libera.
- Il programma si fermava perché utilizzava così tanta RAM da passare più tempo a scambiarsi che a eseguire la sua elaborazione superveloce.
- Il programma era inaccettabilmente lento e il codice più difficile da capire è ben documentato e relativamente stabile.
- Il programma è ancora accettabilmente veloce e la base di codice più comprensibile è più economica da mantenere e consente di apportare più facilmente altri miglioramenti.
Ma tali casi non sarebbero giustificati anche in altri scenari: il codice non è stato reso migliore da una misura assoluta infallibile di qualità, è stato reso migliore sotto un aspetto particolare che lo rende più adatto per un uso particolare; ottimizzato.
E la scelta della lingua ha un effetto qui, perché la velocità, l'uso della memoria e la leggibilità possono essere influenzati da essa, ma anche la compatibilità con altri sistemi, la disponibilità di librerie, la disponibilità di runtime, la maturità di quei runtime su un determinato sistema operativo (per i miei peccati ho in qualche modo finito con avere Linux e Android come i miei sistemi operativi preferiti e C # come la mia lingua preferita, e mentre Mono è fantastico, ma mi trovo ancora un po 'contro questo).
Dire "scegliere C ++ su C # / Java / ... significa ottimizzazioni" ha senso solo se pensi che C ++ faccia davvero schifo, perché l'ottimizzazione riguarda "meglio nonostante ..." non "meglio". Se pensi che C ++ sia migliore nonostante se stesso, l'ultima cosa di cui hai bisogno è preoccuparti di micro-opts così minuscoli. In effetti, probabilmente stai meglio abbandonandolo affatto; gli hacker felici sono anche una qualità da ottimizzare!
Se, tuttavia, sei propenso a dire "Adoro il C ++ e una delle cose che adoro è spremere cicli extra", allora è una questione diversa. È comunque un caso che le micro-opte valgano la pena solo se possono essere un'abitudine riflessiva (vale a dire, il modo in cui tendi a codificare naturalmente sarà più veloce di quanto non sia più lento). Altrimenti non sono nemmeno ottimizzazioni premature, sono pessimizzazioni premature che peggiorano le cose.