L'assemblaggio è ampiamente evitato. È più difficile scrivere rispetto ai linguaggi di alto livello e non premia questa difficoltà con prestazioni notevolmente migliori. Sì, i migliori programmatori di assiemi potrebbero essere in grado di scrivere assembly migliori di un compilatore ottimizzatore, ma il compilatore è molto meglio della stragrande maggioranza dei programmatori.
La difficoltà di scrivere assemblaggi veramente veloci è che ciò richiede una comprensione molto dettagliata della famiglia di processori che si sta prendendo di mira. Non solo a livello di "architettura x86-64", ma a livello di "microarchitettura Skylake". Il costo di un'istruzione di assemblaggio dipende anche da quali istruzioni vengono eseguite attorno ad esso. Ottimizzare i compilatori dispone di modelli di costo appropriati e può fare buon uso del pipelining del processore.
Questo lascia due casi in cui la scrittura dell'assemblaggio può essere appropriata:
-
Per le sezioni estremamente critiche in cui non è possibile fare affidamento sulle ottimizzazioni del compilatore.
L'argomento qui non è che il compilatore non è abbastanza buono, ma che non si vuole rischiare che versioni future del compilatore emettano un assemblaggio peggiore. Un altro motivo potrebbe essere che si dispone di informazioni che non è possibile rendere disponibili per il compilatore, ad es. ciò ti consentirebbe di elidere la contabilità che sarebbe normalmente richiesta dalla convenzione di chiamata.
-
Utilizzare le funzioni dell'insieme di istruzioni che non sono disponibili nella tua lingua.
I set di istruzioni hanno molte estensioni specifiche del processore. È importante sottolineare che le istruzioni SIMD consentono di vettorizzare il codice, che può migliorare drasticamente il throughput dei dati per determinati casi d'uso come la transcodifica multimediale, la crittografia o la numerics. Un compilatore potrebbe non essere in grado di utilizzare queste estensioni a meno che non le abiliti esplicitamente per il compilatore, potrebbe non essere a conoscenza delle nuove estensioni o potrebbe non essere in grado di usarle perché violerebbero la semantica della tua lingua.
Quando un'applicazione si basa sulla vettorizzazione, è tuttavia spesso preferibile utilizzare un coprocessore dedicato invece di una CPU generica. Le GPU sono esattamente tali coprocessori e sono quindi frequentemente utilizzate per la grafica o ML (che è per lo più solo un sacco di moltiplicazioni di matrice che sono facili da vettorializzare in istruzioni di moltiplicazione con fusibile). Invece di utilizzare l'assembly, è possibile che si ottengano maggiori benefici dalla scrittura di shader o dall'uso di tecnologie come OpenCL o CUDA.
Infine, scrivere assembly o utilizzare la vettorizzazione potrebbe non rendere il codice più veloce. Hai ancora bisogno di essere a conoscenza della microarchitettura. Per esempio. l'uso delle istruzioni SIMD può anche rallentare il funzionamento del tuo computer, perché più core potrebbero condividere un'unità vettoriale, trasformando quindi queste istruzioni in un collo di bottiglia.
Come per qualsiasi lavoro sul rendimento, non "ottimizzare" ciecamente:
- Determina i requisiti di rendimento effettivi.
- Misura le prestazioni. Profilo del software per i colli di bottiglia.
- Prova un'ottimizzazione per questi colli di bottiglia. Misuralo. Se non aiuta, scartalo.
- Ripeti fino a quando non vengono soddisfatti i requisiti di rendimento.
Le ottimizzazioni sono più facili quando il software è scritto in uno stile facile da capire. Le maggiori vittorie non derivano dal fare la stessa cosa più velocemente, ma dalla ricerca di modi per evitare il lavoro non necessario. L'assemblaggio della scrittura funziona in modo contrario a questo obiettivo perché rende il software molto più difficile da comprendere.