Ottimizzare C++/Ciclo di vita dell’ottimizzazione: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Ramac (discussione | contributi)
m sistemato
Nessun oggetto della modifica
Riga 2:
# '''Progettazione''' (''design''). Dapprima si progettano gli algoritmi e le strutture dati in modo che abbiano senso per la logica applicativa, e che siano ragionevolmente efficienti, ma senza occuparsi di ottimizzarle. Dove si deve definire una struttura dati di ampio utilizzo e per la quale non è ovvio quale sia l’implementazione ottimale (per esempio, non si sa scegliere tra un array e una lista collegata), si definisce una struttura astratta, di cui si possa cambiare l’implementazione in fase di ottimizzazione.
# '''Codifica''' (''coding''). Poi si scrive il codice che implementa gli algoritmi progettati, seguendo linee-guida che permettano in linea di massima di evitare operazioni banalmente inefficienti e di incapsulare le operazioni che richiederanno ottimizzazioni.
# '''Collaudo funzionale''' (''functional testing''). Poi si collaudo il software prodotto, in modo da aumentare la probabilità che non abbia difetti rilevanti.
# '''Ottimizzazione''' (''tuning''). Dopo aver completato lo sviluppo di un’applicazione funzionante correttamente, si passa alla fase di ottimizzazione, costituita dalle seguenti sotto-fasi:
## '''Collaudo prestazionale''' (''performance testing''). Si valuta quali comandi hanno prestazioni inadeguate, cioè quali comandi, elaborando dei dati tipici, richiedono più memoria o più tempo di quelli massimi specificati nei requisiti.
## '''''Profiling'''''. Per ogni comando avente prestazioni inadeguate, si determina, usando un profiler, quali porzioni di codice costituiscono i cosiddetti “colli di bottiglia” per tale comando, che sono le porzioni di codice nelle quali, tra l’inizio del comando e il suo completamento, viene trascorso più tempo e viene allocata più memoria.
## '''Ottimizzazione algoritmica'''. Nei colli di bottiglia, si applicano tecniche di ottimizzazione sostanzialmente indipendenti dal linguaggio di programmazione, e totalmente indipendenti dalla piattaforma. Sono le tecniche che si trovano nei testi di teoria degli algoritmi. In pratica, si cerca di ridurre il numero di istruzioni eseguite, e in particolare il numero delle chiamate a routine costose, o a trasformare le chiamate costose in chiamate equivalenti ma meno costose. Per esempio, si sceglie di implementare l’[[Implementazione di algoritmi/Quicksort|algoritmol’algoritmo di ordinamento [[w:Quick_sort|''quicksort'']] invece dell’algoritmo [[w:Selection_sort|''selectionsort'' ]]. Se questa ottimizzazione rende il programma sufficientemente veloce, si termina l’ottimizzazione.
## '''Ottimizzazione indipendente dalla piattaforma'''. Nei colli di bottiglia, si adottano tecniche di ottimizzazione dipendenti dal linguaggio di programmazione e dalla sua libreria standard, ma indipendenti sia dalla piattaforma software che dalla piattaforma hardware. Per esempio, si usano operazioni intere invece di operazioni a virgola mobile, o si sceglie il tipo di contenitore più appropriato tra quelli disponibili nella libreria standard. Se questo rende il programma sufficientemente veloce, si termina l’ottimizzazione.
## '''Ottimizzazione dipendente dalla piattaforma software'''. Nei colli di bottiglia, si adottano tecniche di ottimizzazione dipendenti sia dal linguaggio di programmazione che dalla piattaforma software, ma indipendenti dalla piattaforma hardware. Per esempio sfruttando opzioni di compilazione, direttive pragma di compilazione, estensioni al linguaggio offerte da un particolare compilatore, chiamate a librerie non-standard, chiamate dirette al sistema operativo. Se questo rende il programma sufficientemente veloce, si termina l’ottimizzazione.
Riga 24:
Infine, per garantire che la nuova versione ottimizzata del software non sia peggiorata né per la correttezza né per le prestazioni complessive, dopo aver completato l’ottimizzazione si devono ripetere sia il collaudo funzionale che il collaudo prestazionale completo.
 
Questo testo approfondisce solo tre dei punti citati:
Questo testo approfondisce solo tre dei punti citati: alcune tecniche generali relative al punto 4.3 (nella sezione “Tecniche generali di ottimizzazione”) e, limitatamente all’uso del linguaggio C++, il punto 2 (nella sezione “Linee-guida per scrivere codice C++ efficiente e ottimizzabile”) e il punto 4.4 (nella sezione “Tecniche di ottimizzazione specifiche del linguaggio C++, ma indipendenti dalla piattaforma”).
* Limitatamente all’uso del linguaggio C++, il punto 2 (nel capitolo “Scrivere codice C++ efficiente”).
Notazioni terminologiche
* Alcune tecniche generali relative al punto 4.3 (nel capitolo “Tecniche generali di ottimizzazione”).
* Limitatamente all’uso del linguaggio C++, il punto 4.4 (nel capitolo “Ottimizzazione del codice C++”).
 
== Notazioni terminologiche ==
Per oggetto si intende una sequenza contigua di dati in memoria. In particolare, sia un dato associato a una variabile di un tipo fondamentale (come ''bool'', ''double'', o ''unsigned long'') che i dati associati a un'istanza di una classe sono oggetti. A ogni variabile è associato un oggetto, ma un oggetto potrebbe non avere una variabile associata; per esempio un puntatore può puntare a un oggetto senza nome.
 
Per '''oggetto''' si intende una sequenza contigua di dati in memoria. In particolare, sia un dato associato a una variabile di un tipo fondamentale (come ''bool'', ''double'', o ''unsigned long'', o un puntatore) cheè iun datioggetto, così come lo è la struttura associatiassociata a un'istanza di una classe sono oggetti. A ogni variabile è associato un oggetto, ma un oggetto potrebbe non avere una variabile associata; per esempio un puntatore può puntare a un oggetto senza nome.
Si dice che un oggetto possiede un altro oggetto se la deallocazione del primo oggetto comporta la deallocazione del secondo. Per esempio, un oggetto vector non vuoto tipicamente contiene un puntatore a un oggetto array contenente gli elementi; la distruzione del vector comporta la distruzione dell'array, e quindi diciamo che tale array è posseduto dal vector.
 
Si dice che un oggetto '''possiede''' un altro oggetto se la deallocazione del primo oggetto comporta la deallocazione del secondo. Per esempio, un oggetto vector non vuoto tipicamente contiene un puntatore a un oggetto array contenente gli elementi; la distruzione del vector comporta la distruzione dell'array, e quindi diciamo che tale array è posseduto dal vector.
== Sequenze di dati ==
 
Alcune ottimizzazioni risultano applicabili solo per brevi sequenze di dati, altre per sequenze più lunghe. In seguito, si userà la seguente classificazione per lale dimensioni degli oggetti:
* '''Piccolissimo''': Non oltre 8 byte. Sta in ununo o due registri a 32 bit o in un registro a 64 bit.
* '''Piccolo''': Oltre 8 byte, ma non oltre 64 byte. Non sta in un registro del processore, ma sta in una linea della cache dei dati del processore, e può essere interamente indirizzato da istruzioni macchina molto compatte come offset rispetto all'indirizzo iniziale.
* '''Medio''': Oltre 64 byte, ma non oltre 4096 byte. Non sta in una linea della cache dei dati del processore, e non può essere interamente indirizzato da istruzioni macchina compatte, ma sta nella cache dei dati di primo livello del processore, sta in una pagina di memoria virtuale, e sta in un cluster della memoria di massa.