Sì, sia l'allineamento che la disposizione dei dati possono fare una grande differenza in termini di prestazioni, non solo del pochi percento ma da poche a molte centinaia di percento.
Prendi questo loop, due istruzioni contano se esegui abbastanza loop.
.globl ASMDELAY
ASMDELAY:
subs r0,r0,#1
bne ASMDELAY
bx lr
Con e senza cache, e con l'allineamento con e senza lancio della cache nella previsione del ramo e puoi variare le prestazioni di queste due istruzioni di un importo significativo (timer tick):
min max difference
00016DDE 003E025D 003C947F
Un test delle prestazioni che puoi fare facilmente da solo. aggiungere o rimuovere nops attorno al codice in prova ed eseguire un accurato processo di temporizzazione, spostare le istruzioni in prova lungo un intervallo sufficientemente ampio di indirizzi da toccare i bordi delle linee della cache, ecc.
Lo stesso tipo di cose con gli accessi ai dati. Alcune architetture lamentano accessi non allineati (ad esempio eseguendo una lettura a 32 bit all'indirizzo 0x1001), fornendo un errore di dati. Alcuni di quelli che è possibile disabilitare l'errore e subiscono il colpo di prestazione. Altri che consentono accessi non allineati ti danno solo il successo delle prestazioni.
A volte sono "istruzioni" ma il più delle volte sono cicli di clock / bus.
Guarda le implementazioni memcpy in gcc per vari target. Supponiamo che tu stia copiando una struttura che è 0x43 byte, potresti trovare un'implementazione che copia un byte lasciando 0x42 quindi copia 0x40 byte in grandi blocchi efficienti quindi l'ultimo 0x2 può fare come due byte singoli o come trasferimento a 16 bit. Allineamento e destinazione entrano in gioco se gli indirizzi di origine e destinazione sono sullo stesso allineamento, diciamo 0x1003 e 0x2003, quindi potresti fare un byte, quindi 0x40 in grossi pezzi quindi 0x2, ma se uno è 0x1002 e l'altro 0x1003, allora si ottiene veramente brutto e molto lento.
Il più delle volte sono cicli di bus. O peggio il numero di trasferimenti. Prendi un processore con un bus dati a 64 bit, come ARM, ed esegui un trasferimento di quattro parole (lettura o scrittura, LDM o STM) all'indirizzo 0x1004, che è un indirizzo allineato a parola, e perfettamente legale, ma se il bus è 64 bit di larghezza è probabile che la singola istruzione si trasformi in tre trasferimenti in questo caso un 32 bit a 0x1004, un 64 bit a 0x1008 e un 32 bit a 0x100A. Ma se avessi le stesse istruzioni ma all'indirizzo 0x1008, potresti fare un unico trasferimento di quattro parole all'indirizzo 0x1008. Ad ogni trasferimento è associato un tempo di installazione. Quindi la differenza di indirizzo da 0x1004 a 0x1008 da sola può essere parecchie volte più veloce, anche / esp quando si utilizza una cache e tutti sono hit della cache.
Parlando di, anche se si fa una lettura di due parole all'indirizzo 0x1000 vs 0x0FFC, lo 0x0FFC con errori cache causerà due letture della riga cache dove 0x1000 è una riga cache, si ha comunque la penalità di una riga cache per un caso casuale accesso (lettura di più dati rispetto all'utilizzo) ma poi raddoppia. Come sono allineate le strutture o i dati in generale e la frequenza di accesso a tali dati, ecc., Può causare il blocco della cache.
Puoi finire con lo striping dei tuoi dati in modo tale che mentre elabori i dati puoi creare sfratti, potresti essere sfortunato e finire usando solo una frazione della tua cache e mentre salti attraverso di essa il successivo BLOB di dati si scontra con un BLOB precedente . Mescolando i dati o riorganizzando le funzioni nel codice sorgente, ecc., È possibile creare o rimuovere le collisioni, poiché non tutte le cache sono create uguali, il compilatore non ti aiuterà qui, è su di te. Anche il rilevamento del miglioramento delle prestazioni o del miglioramento dipende da te.
Tutto ciò che abbiamo aggiunto per migliorare le prestazioni, bus di dati più ampi, pipeline, cache, previsione di diramazioni, unità / percorsi di esecuzione multipli, ecc. Molto spesso aiuterà, ma hanno tutti punti deboli, che possono essere sfruttati intenzionalmente o accidentalmente. Il compilatore o le librerie possono fare molto poco, se sei interessato alle prestazioni devi mettere a punto e uno dei maggiori fattori di ottimizzazione è l'allineamento del codice e dei dati, non solo allineato su 32, 64, 128, 256 limiti di bit, ma anche dove le cose sono relative l'una all'altra, si desidera che i loop pesantemente usati o i dati riutilizzati non arrivino allo stesso modo cache, ognuno di loro vuole il proprio. I compilatori possono aiutare, ad esempio, a ordinare le istruzioni per un'architettura super scalare, riordinando le istruzioni l'una rispetto all'altra, non importa,
La più grande svista è l'ipotesi che il processore sia il collo di bottiglia. Non è vero per un decennio o più, l'alimentazione del processore è il problema ed è qui che entrano in gioco problemi come hit delle prestazioni di allineamento, blocco della cache, ecc. Con un po 'di lavoro anche a livello di codice sorgente, la riorganizzazione dei dati in una struttura, l'ordinamento delle dichiarazioni variabili / struttura, l'ordinamento delle funzioni all'interno del codice sorgente e un po' di codice aggiuntivo per allineare i dati, possono migliorare le prestazioni più volte o Di Più.