Ottimizzare C++/Scrivere codice C++ efficiente/Costrutti che migliorano le prestazioni: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Pietrodn (discussione | contributi)
Nessun oggetto della modifica
Riga 1:
{{Ottimizzare C++}}
 
Alcuni costrutti del linguaggio C++, se usati opportunamente, permettono di migliorareaumentare la velocità del software risultante.
 
In questa sezione si presentano le linee-guida per approfittare di tali costrutti.
Riga 13:
=== Funzioni <code>qsort</code> e <code>bsearch</code> ===
 
'''Invece delle funzioni della libreria standard del C <code>qsort</code> e ''<code>bsearch''</code>, usa le funzioni della libreria standard del C++ <code>std:sort</code> e <code>std:lower_bound</code>.'''
 
Le prime due funzioni richiedono necessariamente un puntatore a funzione, mentre le seconde due possono usare un oggetto-funzione (o, usando [[w:C++0x|C++0x]], un'espressione lambda).
I puntatori a funzione spesso non sono espansi ''inline'' e sono quindi meno efficienti degli oggetti-funzione.
 
=== Funzioni membro <code>const</code> ===
Riga 25:
Indicare <code>const</code> una funzione membro equivale a specificare tale argomento implicito come puntatore a <code>const</code>.
 
La linea-guida "Passaggio di argomenti alle funzioni" della sezione 3.3.5 consiglia di usare lo specificatore <code>const</code> negliper gli argomenti di funzione di tipo puntatore, se l'oggetto puntato non deve essere modificato dalla funzione.
 
=== Rappresentazione di simboli ===
Riga 33:
Un enumerato è implementato come un intero.
Tipicamente, rispetto ad un intero, una stringa occupa più spazio, ed è più lenta da copiare e da confrontare.
Inoltre, tale pratica è più sicura, in quanto se si sbaglia a scrivere la costante nel codice, il compilatore si lamenterà di unaun variabileidentificatore non definitadefinito, mentre se si passa una stringa e si sbaglia a scrivereerrata si crea un errore potenzialmente difficile da rintracciare.
 
=== Istruzioni <code>if</code> e <code>switch</code> ===
Riga 39:
Se devi confrontare un valore intero con una serie di valori costanti, invece di una serie di istruzioni <code>if</code>, usa un'istruzione <code>switch</code>.
 
I compilatori possono sfruttare la regolarità di tale istruzione per applicare alcune ottimizzazioni, in particolare se viene applicata la linea-guida 3.1.11"Valori dei casi di istruzioni <code>switch</code>" di questa sezione.
 
=== Collezioni incapsulate ===
Riga 46:
 
In fase di progettazione è difficile decidere quale struttura dati avrà prestazioni ottimali nell'uso effettivo dell'applicazione.
In fase di ottimizzazione, ci si può accorgere che cambiando il tipo di un contenitore, per esempio passando da <code>std::vector</code> a <code>std::list</code>, si ottengono prestazioni migliori.
Tuttavia, tale modificacambio di implementazione comporta la modifica di gran parte del codice sorgente che utilizza direttamente il contenitore che ha cambiato di tipo.
 
Se una collezione è accessibile da una sola unità di compilazione, tale modifica avrà impatto solamente sul codice sorgente contenuto in tale file, e quindi non è necessario incapsulare tale collezione.
Se invece la collezione è accessibile direttamente da altre unità di compilazione, in futuro ci potrà essere una quantità enorme di codice che dovrà essere modificata a fronte di un cambio di contenitore, e quindi, per rendere fattibile in tempi ragionevoli tale ottimizzazione, si deve incapsulare il contenitore in una classe la cicui interfaccia non cambia al cambiare dell'implementazione del contenitore.
 
I contenitori STL attuano già parzialmente questo principio, ma alcune operazioni sono disponibili solo per alcuni contenitori, e altre hanno comportamento diverso al variare del contenitore.
Riga 58:
'''Nell'uso dei contenitori STL, a parità di prestazioni, fa' in modo di rendere intercambiabile il contenitore.'''
 
Per esempio, chiama <code>a.empty()</code> invece di <code>a.size() == 0</code>, e chiama <code>iter != a.end()</code> invece di <code>iter < a.end()</code>, e chiama <code>distance(iter1, iter2)</code> invece di <code>iter2 - iter1</code>, in quanto le prime espressioni sono valide per ogni tipo di contenitore, mentre le seconde solo per alcuni tipi.
 
Purtroppo, non è sempre possibile scrivere del codice egualmente efficientevalido eed validoefficiente per ogni tipo di contenitore.
Tuttavia, riducendo il numero di istruzioni dipendenti dal tipo di contenitore, se in fase di ottimizzazione si dovesse sostituire il contenitore con un altro, si dovrà modificare meno codice.
 
=== Espressioni lambda ===
 
'''Invece di scrivere un ciclo <code>for</code> su un contenitore STL, usa un algoritmo di STL con un'espressione lambda (usando la libreria [[http://www.boost.org/ Boost]] o C++0x).'''
 
Gli algoritmi di STL sono già dei cicli ottimizzati per gli specifici contenitori, ed evitano il rischio di introdurre erroneamente operazioni inefficienti.
 
Le espressioni lambda sono implementate come oggetti-funzione espansi ''inline'', e quindi sono altrettanto efficienti quanto il codice scritto sul posto.
 
Senza avere a disposizione la funzionalità lambda, nella maggiorin parte deimolti casi risulta così scomodo usare gli algoritmi STL che non ne vale la pena, a meno che l'algoritmo sia di una certa complessità, come, per esempio, nel caso di <code>std::sort</code>.
 
=== Scelta del contenitore di default ===
Riga 81:
Per insiemi più grandi, altri contenitori possono diventare gradualmente più efficienti a seconda delle operazioni, ma il <code>vector</code> rimane quello che ha minore occupazione di memoria (purché non ci sia capacità in eccesso), minor tempo di scansione completa, e maggiore località di riferimento.
 
=== Funzioni espanse <code>''inline</code>'' ===
 
'''Se usi compilatori che consentono l'ottimizzazione dell'intero programma e l'espansione <code>''inline</code>'' di ogni funzione che il compilatore ritenga opportuno, usa tali opzioni e non dichiarare <code>inline</code> nessuna funzione. Se tali funzionalità del compilatore non fossero disponibili, dichiara </code>inline</code> nei file di intestazione solo le funzioni che contengono non più di tre righe di codice, tra le quali non ci sono cicli.'''
 
Le funzioni espanse </code>''inline</code>'' non hanno il costo della chiamata di funzione, che è tanto più grande quanti più sono gli argomenti della funzione. Inoltre, dato che il codice è consecutivo con quello del chiamante, hanno migliore località di riferimento. del codice.
Infine, dato che il codice intermedio delle funzioni espanse </code>''inline</code>'' si fonde con quello del chiamante, tale codice può essere facilmente ottimizzato dal compilatore.
 
Le funzioni molto piccole, cioè un semplice assegnamento o una semplice istruzione <code>return</code>, quando espanse <code>''inline</code>'', comportano addirittura una riduzione del codice generatocomplessivamente complessivogenerato.
 
Tuttavia, ogni volta che viene espansa <code>inline</code> una routine che contiene una quantità notevole di codice, siviene duplicaespansa ''inline'', tale codice viene duplicato, e quindi si ingrandisce la dimensione complessiva del programma aumenta, con conseguente rallentamento a causa della minore località di riferimento del codice.
 
Tra le routine non piccolissime, solo quelle critiche per la velocità verranno resedichiarate ''<code>inline''</code> in fase di ottimizzazione.
<!--revisione giunta fino a qui-->
 
=== Valori dei casi di istruzioni <code>switch</code> ===