Ottimizzazione dei loops.
-
29-09-2020 - |
Domanda
Disclaimer: non sono un esperto di compilatore. Sono semplicemente curioso e vieni alla ricerca dell'illuminazione.
Ho visto le persone affermano che - per efficienza - i loops for
dovrebbero generalmente utilizzare un confronto zero per la risoluzione. Così piuttosto che:
void blink1(int n) {
for (int i=0; i<n; i++) {
blink_led();
}
}
.
Dovresti scrivere:
void blink2(int n) {
for (int i=n; i>0; i--) {
blink_led();
}
}
.
Ho pensato che fosse un po 'sciocco: perché mettere il peso sull'umano se un compilatore potrebbe interpretare entrambi i casi come "blink_led () n volte"?
Ma usando Mr. Il compilatore di Godbolt Explorer , ora penso che mi sbaglio. Per tutti i compilatori ho provato, il "confronto contro lo zero" ha sempre prodotto un ciclo più breve. Ad esempio, X86-64 GCC 10.2 con -O3 ottimizzazione ha prodotto i seguenti anelli interni:
blink1:
...
.L3:
xor eax, eax
add ebx, 1
call blink_led
cmp ebp, ebx
jne .L3
.
vs
blink2:
...
.L12:
xor eax, eax
call blink_led
sub ebx, 1
jne .L12
.
Quindi ecco la domanda
Questo sembra un caso così comune.
Perché non è possibile (o perché non) il compilatore si nota che l'effetto del ciclo for
è semplicemente "fare questa cosa n volte" - se contare o contare giù - e ottimizzare per questo?
Soluzione
Quello che leggi è totale assurdità, ad eccezione del più primitivo dei compilatori.Innanzitutto, il confronto con un intero è veloce, forse anche più veloce del confronto con una costante.In secondo luogo, un buon compilatore ottimizzante prenderà un ciclo scritto usando un modello comune e trasformerlo nel miglior codice possibile;Potrebbe non riconoscere il tuo modello offuscato e produrre un codice meno buono per questo.
E infine, non dovresti sostituire leggibile con codice illeggibile a meno che non ci sia un bisogno effettivo per questo.Se trascorri un'ora di modifica, ha bisogno di produrre almeno 20 ore di tempo di CPU salvato.Quando sei a quel livello, è molto probabile che gli algoritmi migliori danno migliori risparmi.
Altri suggerimenti
Penso di essere d'accordo per lo più con @ Gnasher729, ma quelle persone di ciò che preoccuparsi di "efficienza" criticano usando il ciclo per il ciclo affatto - la variabile "I" non aggiunge nulla ...
Perché non:
void blink3(int n) {
while (n-- > 0) {
blink_led();
}
}
.
Ho aggiunto il "> 0" per un paio di motivi: 1) Nel caso in cui qualcuno richieda una quantità negativa di lampeggiare, non voglio rotolare all'indietro per 2 miliardi di volte.2) È un po 'più ovvio per l'unitario (forse).
A proposito, in Esempio intrecciato, la funzione "BLACK_LED ()" è strana e fishy - probabilmente aspettare per essere in grado di vederlo andare avanti, poi spento.Quindi "efficienza" è un po 'fuori dal tavolo.
Ma, in generale, per la maggior parte delle cose, l'efficienza è per lo più su quanto velocemente può essere codificato e anche (cosa più importante) quanto velocemente è compreso.