Newsgroup: it.comp.programmare.win32
Titolo: Ottimizzare il codice

Salve a tutti,
una domanda un po' tecnica: come si ottimizza il codice? o meglio: c'è
qualcosa in giro che mi dica se è più efficiente una sottrazione ed
un'uguaglianza oppure un "maggiore di"?? sto sviluppando un'applicazione che
deve essere il più efficiente possibile (ovviamente nei limiti) e vorrei
sapere se conoscete qualcosa, soprattutto orientato a delphi che già di suo
ottimizza il codice in compilazione.
Grazie a tutti


wrote:

Non esitono regole generali... per esempio "metti un Result:= True"...
perche dipende dal codice... dipende molto se scrivi "elegante" o
no...
cmq + "elegante" scrivi + e facile vedere le cose inutili percui
ottimizare...
Il Razziatore,
The Only Good Windows in an uninstalled Windows
-----------------------------------------------
E-MAILs : IlRazziat...@NOSPAMnetscape.net
WEB     : 
http://www.razziatore.dns2go.com/
ICQ     : 67552596

 wrote in
news:sEV78.24769$
nI1.7...@twister1.libero.it
: 

Prima di ottimizzare il codice devi trovare quali sono (se ci sono) i 
colli di bottiglia del tuo programma e dedicarti ad ottimizzare solo 
i punti che hanno davvero la necessità di essere ottimizzati.
Ci sono dei suggerimenti semplici che è sempre meglio considerare, ad 
esempio nel caso di if a condizione multipla mettere sempre prima il 
test che può rendere vera o falsa la condizione nella maggior parte 
dei casi (credo che anche deplhi interpreti le condizioni con lo 
"short circuit"), ma cercare di ottimizzare il codice mentre lo si 
scrive non è IMHO semplice e porta a scrivere codice complicato e 
poco manutenibile.
Senza considerare che un algoritmo sbagliato (anche implementato da 
un "guru") sarà sempre meno performante dell'algorimo migliore.
Inoltre non è solo la velocità di esecuzione delle istruzioni che 
conta (se fai dieci copie di una struttura da 1Mb per elaborarla in 
modo superveloce il programma rischia poi di essere penalizzato dallo 
swap e essere meno performante della versione che lavora sempre sullo 
stesso mega di dati).
Tempo fa avevo trovato on-line un bel testo di Michael Abrash ("Zen 
of code optimization" o qualcosa di simile), ma ho perso il link e 
sono quasi certo che sia ormai introvabile in versione cartacea.
-- 
Valter Minute
min...@inwind.it
 (the reply address of this message is invalid)

 sono quasi certo che sia ormai introvabile in versione cartacea.
bene, lo cerco.
Grazie

> una domanda un po' tecnica: come si ottimizza il codice?
Beh,  ora come  ora gli  ottimizzatori  dei compilatori  se la  cavano
meglio di  quasi tutti  gli umani a  livello di brevi  costrutti. Loro
infatti  "pensano" *solo* alla  cpu che  hanno sotto  e meccanicamente
considerano tutte le  regole (note) per far si che  il codice sia piu`
veloce.
VM> Prima di ottimizzare il codice devi trovare quali sono (se ci sono) i 
VM> colli di bottiglia del tuo programma e dedicarti ad ottimizzare solo 
VM> i punti che hanno davvero la necessità di essere ottimizzati.
Generalmente e` la parte di codice dove il programma rimane di piu` ad
esser  quella che ha  bisogno di  essere ottimizzata.  Ottimizzare una
parte poco usata puo` costare di piu` di quanto rende.
VM> Ci sono dei suggerimenti semplici che è sempre meglio considerare, ad 
VM> esempio nel caso di if a condizione multipla mettere sempre prima il 
VM> test che può rendere vera o falsa la condizione nella maggior parte 
VM> dei casi (credo che anche deplhi interpreti le condizioni con lo 
VM> "short circuit"), 
Evitare se possibile  i salti e` un'altra cosa. Un  salto non fa molto
bene alla pipeline della CPU, sopratutto se condizionato.
Usare "register". Vero  che su CPU Intel serve a  niente, ma non tutto
il mondo e` Intel per fortuma.
Puo` essere  utile usare  costrutti come il  Duff's device, ma  qui e`
meglio fare delle misure.  Indubbiamente riduce il numero di confronti
e salti,  ma a volte (i.e.  spostamento di blocchi  di memoria) meglio
sfruttare le routine di libreria  che si basano sulle istruzioni della
macchina...
Cerca  di tenere bassi  il numero  di accessi  in memoria  (ovvero non
farli se non  serve) e di cercare di  accedere alla memoria sfruttando
anche la cache line del processore... Questo si puo` ottenere giocando
sulla dichiarazione delle variabili.
VM> ma cercare di ottimizzare il codice mentre lo si 
VM> scrive non è IMHO semplice e porta a scrivere codice complicato e 
VM> poco manutenibile.
Ahem, esistono /* */ e //  :). Usando quelli si riesce a far diventare
comprensibile (non necessariamente  comprensibile) anche la magia piu`
nera.
VM> Senza considerare che un algoritmo sbagliato (anche implementato da 
VM> un "guru") sarà sempre meno performante dell'algorimo migliore.
Se parli di un algoritmo O(n^2) verso un O(n log n) O.K. Altrimenti...
VM> Inoltre non è solo la velocità di esecuzione delle istruzioni che 
VM> conta
Vero, ma non ci si deve preoccupare solo dello swap, anche l'uso della
cache ha  importanza. La  memoria e` spesso  il freno peggiore  per un
programma.
-- 
Gian  | main(){printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
Uberto|                        David Korn, 1987 ioccc best one liner 
Lauri |-------------------------------------------------------------------
(aka saint) >>Vedere X-Nospam nell'header - See X-Nospam field in Header<<

 macchina...
D'altronde e' cio' che afferma lo stesso Tom Duff nel suo
messaggio originale:
"Transformations like this can only be justified by 
 measuring the resulting code.  Be careful when you 
 use this thing that you don't unwind the loop so 
 much that you overflow your machine's instruction cache.  
 Don't try to be smarter than an over-clever C compiler 
 that recognizes loops that implement block move or block 
 clear and compiles them into machine idioms."
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 nera.
Esatto: i commenti servono a questo...

Anch'io ho problemi di ottimizzazione del codice in un algoritmo
estremamente ricorsivo.
Non ho ancora capito bene:
Per esempio:
č meglio questo:
if ((a < (dim1-dim2)) && (b < (dim1-dim2)) && (c < (dim1-dim2))) ....;
o questo:
register int dim12 = dim1-dim2;
If ((a < dim12) && (b < dim12) && (c < dim12)) ....;
E poi:
Io cerco di usare sempre le funzioni inline. Qual č lo svantaggio oltre ad
un eseguibile con maggiore occupazione di memoria (che non mi sembra grave)?
E poi, prendiamo questo ciclo:
for(int k=0; k<Arr.GetSize(); k++)
{
    int i;
    ...
}
Il GetSize() viene eseguito ad ogni ciclo del for? Se cosě fosse sarebbe
meglio appoggiarsi ad una variabile dichiarata prima del for
int size = Arr.GetSize();
for(int k=0; k<size; k++)
{
    int i;
    ...
}
L'intero i č megliodichiararlo fuori (prima) del for? (o č uguale?)

 un eseguibile con maggiore occupazione di memoria (che non mi sembra grave)?
Le funzioni inline di solito vanno bene per roba molto piccola (di solito
non piu di 4..5 righe di codice C senza ulteriori chiamate a funzione) 
usata all'interno di loop critici oppure di routine ricorsive molto utilizzate.
Se a forza di usare codice inline aumenti i cache miss rischi
di peggiorare le prestazioni complessive.
Assicurati prima che le ottimizzazioni a piu alto livello siano gia state
fatte e che siano corrette.
Mi e' capitato di scrivere "al primo colpo" codice "immune alle
ottimizzazioni del compilatore" nel senso che per l'organizzazione generale
del codice e per algoritmi utilizzati, le ottimizzazioni a livello di
compilazione del codice erano praticamente ininfluenti.
In altri casi, ritoccando in alcuni punti critici un programma grosso
(modifiche a meno dello 0.1% del codice complessivo) ho ottenuto un aumento
di prestazioni del 400% (ma la versione iniziale era stata scritta
in regime di rilascia-entro-la-data-prefissata-o-muori).
L. Micheletto


Ci sono vari livelli di ottimizzazione, le regole generali da seguire
sono:
0) Ottimizza solo le parti dell'applicazione che ne hanno veramente
   bisogno, ottimizza codice che viene eseguito molto spesso oppure 
   che quando viene eseguito introduce pause/ritardi sgradevoli per 
   l'utilizzatore. Se perdi tempo ad ottimizzare codice che e' gia 
   a livelli accettabili di efficienza oppure che non migliora in modo
   apprezzabile l'efficienza complessiva dell'applicazione stai sprecando
   tempo che potresti dedicare alle parti che ne hanno davvero bisogno.
1) Usa codice *gia ottimizzato* non reinventare la ruota se esiste gia
   e funziona bene (ci sono un sacco di librerie in giro sulla rete).
2) Assicurati che le ottimizzazioni indipendenti dal processore siano 
   gia state fatte e che siano valide.
   Non si tratta di scegliere "il miglior algoritmo", ma piuttosto
   "il miglior algoritmo per i tipi di dati (per dimensione
   ed organizzazione) su cui si opera e che produce un risultato
   accettabile". Bisogna anche valutare su quale tipo di computer
   viene fatta girare l'applicazione, non ha senso ottimizzare l'applicazione
   per un sistema che ha molta piu ram o molto meno ram di quello su cui
   va poi a girare ("l'algoritmo migliore" dipende anche da questo), se
   hai dei dubbi ottimizza prendendo in considerazione il sistema
   con i requisiti minimi (ma non auto-limitarti troppo).
3) Considera le dipendenze "generiche" dal processore e dal sistema
   (es: riesco a stare sulla cache il piu possibile, oppure
        "esco fuori troppo spesso" ?).
4) "Rilassa" i check sui range dei tipi di dato e sugli indici degli array
   fatti dal compilatore (questo se si usano linguaggi di programmazione
   che proteggono i programmatori dagli errori banali).
5) Infine applica le ottimizzazioni dipendenti dal set d'istruzioni
   della cpu (sono quelle soggette ad essere le meno durature, per questo
   vanno fatte solo quando necessario, specialmente se si deve
   far ricorso all'assembly).
I tool possono anche essere d'aiuto, ma per ottimizzare le parti dove 
non arriva il compilatore bisogna "acquisire la mentalita giusta" e questo 
lo si ottiene ragionando con calma su cosa si vuole ottenere e come 
si sta cercando di farlo.
Per esempio, non ha senso ottimizzare un confronto in una istruzione if
se poi ad avere il peso maggiore e' il salto condizionale che dipende
dal confronto.
L. Micheletto

    che proteggono i programmatori dagli errori banali).
Potresti fare un esempio?
Grazie, DANILO.

 Potresti fare un esempio?
Con C/C++ usando GCC puoi disattivare il controllo delle eccezioni
(ma il codice che usi non deve farne uso) ed usare -ffast-math in modo
da ridurre i check e la precisione sulle operazioni floating point.
Con VisualBasic puoi disabilitare il check sugli indici
delle matrici, il controllo di overflow sugli interi, quello sugli
errori in floating point.
Usando Ada95 (Gnat 3.14) puoi disabilitare selettivamente
i check su overflow degli interi e sui range di sottotipi ed indici
di array.
Opzioni simili esistono anche per Delphi (simili a quelle di Ada95).
Nota che C e C++ sono i linguaggi "meno protettivi" sotto questo punto
di vista e quindi hai meno roba disabilitabile, inoltre la disabilitazione
dei check va fatta su codice gia testato e (possibilmente) con meccanismi di
controllo/limitazione degli errori a cura del programmatore.
L. Micheletto

> nera.
La frase doveva sesse (non necessariamente _facilmente_ comprensibile)


 writes:
LM> 2) Assicurati che le ottimizzazioni indipendenti dal processore siano 
LM>    gia state fatte e che siano valide.
LM>    Non si tratta di scegliere "il miglior algoritmo", ma piuttosto
LM>    "il miglior algoritmo per i tipi di dati (per dimensione
LM>    ed organizzazione) su cui si opera e che produce un risultato
LM>    accettabile".
Guardacaso e` l'idea su cui si basa la FFTW (Fastest Fourier Transform
in the  West) che scova il miglior  modo per calcolare una  FFT di una
certa taglia su una certa macchina e si salva la "ricetta"...


 certa taglia su una certa macchina e si salva la "ricetta"...
Lo fanno pure alcune versioni del kernel Linux per identificare la routine 
di copia di memoria piu veloce.
Ma anche senza arrivare a tanto, basta un analisi preliminare sufficientemente
approfondita e qualche test per ottenere buoni risultati, l'approccio
con routine di autoconfigurazione va bene solo se il tempo impiegato
per mettere a punto il sistema di autoconfigurazione si ripaga a lungo termine
(per FFTW e Linux si ripaga eccome, ma in altri casi si traduce solo in piu
 codice da scrivere, mettere a punto e mantenere).
L. Micheletto