Scopo dell'allineamento della memoria


197

Certo, non capisco. Supponi di avere una memoria con una parola di memoria della lunghezza di 1 byte. Perché non riesci ad accedere a una variabile lunga 4 byte in un singolo accesso alla memoria su un indirizzo non allineato (cioè non divisibile per 4), come nel caso degli indirizzi allineati?


17
Dopo aver fatto un po 'di googling aggiuntivo, ho trovato questo ottimo link, che spiega molto bene il problema.
arca,


3
Link @ark interrotto
John Jiang,

2
@JohnJiang Penso di aver trovato il nuovo link qui: developer.ibm.com/technologies/systems/articles/pa-dalign
ejohnso49

Risposte:


63

È una limitazione di molti processori sottostanti. Di solito può essere aggirato eseguendo 4 inefficienti recuperi a byte singolo anziché un efficiente recupero di parole, ma molti specificatori di lingue hanno deciso che sarebbe stato più semplice metterli fuorilegge e forzare l'allineamento di tutto.

Ci sono molte più informazioni in questo link che l'OP ha scoperto.


311

Il sottosistema di memoria su un moderno processore è limitato all'accesso alla memoria per la granularità e l'allineamento della sua dimensione di parola; questo è il caso per una serie di motivi.

Velocità

I processori moderni hanno più livelli di memoria cache che i dati devono essere estratti; il supporto di letture a byte singolo renderebbe il throughput del sottosistema di memoria strettamente legato al throughput dell'unità di esecuzione (aka cpu-bound); questo ricorda tutto il modo in cui la modalità PIO è stata superata da DMA per molte delle stesse ragioni nei dischi rigidi.

La CPU legge sempre alla sua dimensione di parola (4 byte su un processore a 32 bit), quindi quando si fa un accesso di indirizzo non allineato - su un processore che lo supporta - il processore leggerà più parole. La CPU leggerà ogni parola di memoria su cui si trova il tuo indirizzo richiesto. Ciò provoca un'amplificazione fino a 2 volte il numero di transazioni di memoria richieste per accedere ai dati richiesti.

Per questo motivo, può essere molto più lento leggere due byte che quattro. Ad esempio, supponiamo di avere una struttura in memoria simile a questa:

struct mystruct {
    char c;  // one byte
    int i;   // four bytes
    short s; // two bytes
}

Su un processore a 32 bit sarebbe molto probabilmente allineato come mostrato qui:

Layout strutturale

Il processore può leggere ciascuno di questi membri in una transazione.

Supponiamo che tu abbia una versione impacchettata della struttura, magari dalla rete in cui è stata imballata per l'efficienza della trasmissione; potrebbe assomigliare a questo:

Struct imballato

La lettura del primo byte sarà la stessa.

Quando chiedi al processore di darti 16 bit da 0x0005, dovrà leggere una parola da 0x0004 e spostare a sinistra di 1 byte per inserirlo in un registro a 16 bit; del lavoro extra, ma la maggior parte può gestirlo in un ciclo.

Quando chiedi 32 bit da 0x0001 otterrai un'amplificazione 2X. Il processore leggerà da 0x0000 nel registro dei risultati e sposta a sinistra di 1 byte, quindi rileggerà da 0x0004 in un registro temporaneo, sposta a destra di 3 byte, quindi ORcon il registro dei risultati.

Gamma

Per un determinato spazio di indirizzi, se l'architettura può assumere che i 2 LSB siano sempre 0 (ad es. Macchine a 32 bit), può accedere a 4 volte più memoria (i 2 bit salvati possono rappresentare 4 stati distinti), o la stessa quantità di memoria con 2 bit per qualcosa come bandiere. Rimuovere i 2 LSB da un indirizzo ti darebbe un allineamento di 4 byte; indicato anche come un passo di 4 byte. Ogni volta che un indirizzo viene incrementato, aumenta effettivamente il bit 2, non il bit 0, ovvero gli ultimi 2 bit continueranno sempre ad essere 00.

Ciò può anche influire sulla progettazione fisica del sistema. Se il bus dell'indirizzo richiede 2 bit in meno, possono esserci 2 pin in meno sulla CPU e 2 tracce in meno sul circuito.

Atomicita

La CPU può operare atomicamente su una parola di memoria allineata, il che significa che nessun'altra istruzione può interrompere tale operazione. Ciò è fondamentale per il corretto funzionamento di molte strutture dati senza blocco e altri paradigmi di concorrenza .

Conclusione

Il sistema di memoria di un processore è un po 'più complesso e coinvolto di quanto descritto qui; una discussione su come un processore x86 si occupa effettivamente della memoria può aiutare (molti processori funzionano in modo simile).

Ci sono molti altri vantaggi nell'aderire all'allineamento della memoria che puoi leggere in questo articolo IBM .

L'uso principale di un computer è trasformare i dati. Le architetture e le tecnologie di memoria moderne sono state ottimizzate nel corso di decenni per facilitare l'ottenimento di più dati, in, out e tra unità di esecuzione più e più veloci, in modo altamente affidabile.

Bonus: cache

Un altro allineamento per prestazioni a cui ho accennato in precedenza è l'allineamento sulle linee della cache che sono (ad esempio, su alcune CPU) 64B.

Per ulteriori informazioni su quante prestazioni possono essere ottenute sfruttando le cache, dai un'occhiata alla Gallery of Processor Cache Effects ; da questa domanda sulle dimensioni della linea di cache

La comprensione delle linee della cache può essere importante per alcuni tipi di ottimizzazioni del programma. Ad esempio, l'allineamento dei dati può determinare se un'operazione tocca una o due righe della cache. Come abbiamo visto nell'esempio sopra, ciò può facilmente significare che in caso di disallineamento, l'operazione sarà due volte più lenta.


le seguenti strutture xyz hanno dimensioni diverse, poiché la regola di ciascun membro deve iniziare con l'indirizzo che è multiplo della sua dimensione e la strcut deve essere terminata con indirizzo che è multiplo della dimensione più grande del membro della struttura. struct x {short s; // 2 byte e 2 padding tytes int i; // 4 byte char c; // 1 byte e 3 byte di riempimento lunghi lunghi l; }; struct y {int i; // 4 byte char c; // 1 byte e 1 byte di riempimento brevi; // 2 byte}; struct z {int i; // 4 byte brevi s; // 2 byte char c; // 1 byte e 1 byte di riempimento};
Gavin,

1
Se ho capito bene, il motivo PERCHÉ un computer non può leggere una parola non allineata in un solo passaggio è perché gli additivi usano 30 bit e non 32 bit ??
GetFree

1
@chux Sì, è vero, gli assoluti non reggono mai. L'8088 è uno studio interessante dei compromessi tra velocità e costi, era fondamentalmente un 8086 a 16 bit (che aveva un bus esterno a 16 bit completo) ma con solo metà delle linee di autobus per risparmiare sui costi di produzione. Per questo motivo, l'8088 aveva bisogno di due volte il clock per accedere alla memoria rispetto all'8086 poiché doveva fare due letture per ottenere la parola completa a 16 bit. La parte interessante, l'8086 può eseguire una lettura allineata a 16 bit di una parola in un singolo ciclo, le letture non allineate prendono 2. Il fatto che l'8088 avesse un bus di mezza parola ha mascherato questo rallentamento.
joshperry,

2
@joshperry: leggera correzione: l'8086 può eseguire una lettura a 16 bit allineata a parola in quattro cicli, mentre le letture non allineate richiedono otto . A causa dell'interfaccia di memoria lenta, i tempi di esecuzione su macchine basate su 8088 sono generalmente dominati dai recuperi di istruzioni. Un'istruzione come "MOV AX, BX" è nominalmente un ciclo più veloce di "XCHG AX, BX", ma a meno che non sia preceduta o seguita da un'istruzione la cui esecuzione richiede più di quattro cicli per byte di codice, occorreranno quattro cicli in più per eseguire. Sull'8086, il recupero del codice a volte può tenere il passo con l'esecuzione, ma sull'8088 a meno che non si usi ...
supercat

1
Molto vero, @martin. Ho eluso quei byte di riempimento per focalizzare la discussione all'interno della struttura, ma forse sarebbe meglio includerli.
joshperry,

22

puoi farlo con alcuni processori ( nehalem può farlo ), ma in precedenza tutti gli accessi alla memoria erano allineati su una linea a 64 bit (o 32 bit), poiché il bus era largo 64 bit, dovevi recuperare 64 bit alla volta ed è stato significativamente più semplice recuperarli in "blocchi" allineati di 64 bit.

Quindi, se volevi ottenere un singolo byte, hai recuperato il blocco a 64 bit e poi mascherato i bit che non volevi. Facile e veloce se il tuo byte fosse all'estremità giusta, ma se fosse nel mezzo di quel blocco a 64 bit, dovresti mascherare i bit indesiderati e quindi spostare i dati nel posto giusto. Peggio ancora, se si voleva una variabile da 2 byte, ma che era divisa in 2 blocchi, ciò richiedeva il doppio degli accessi alla memoria richiesti.

Quindi, poiché tutti pensano che la memoria sia economica, hanno appena fatto in modo che il compilatore allinei i dati sulle dimensioni del blocco del processore in modo che il codice venga eseguito più velocemente e in modo più efficiente a scapito della memoria sprecata.


5

Fondamentalmente, la ragione è perché il bus di memoria ha una lunghezza specifica che è molto, molto più piccola della dimensione della memoria.

Quindi, la CPU legge dalla cache L1 su chip, che oggi è spesso 32 KB. Ma il bus di memoria che collega la cache L1 alla CPU avrà una larghezza notevolmente inferiore delle dimensioni della linea della cache. Questo sarà nell'ordine di 128 bit .

Così:

262,144 bits - size of memory
    128 bits - size of bus

Gli accessi non allineati a volte si sovrappongono a due righe della cache e ciò richiederà una lettura della cache completamente nuova per ottenere i dati. Potrebbe anche mancare fino alla DRAM.

Inoltre, una parte della CPU dovrà stare in piedi per mettere insieme un singolo oggetto da queste due diverse linee di cache che hanno ciascuna una parte dei dati. Su una riga, sarà nei bit di ordine molto elevato, nell'altra, i bit di ordine molto basso.

Ci sarà un hardware dedicato completamente integrato nella pipeline che gestisce lo spostamento di oggetti allineati sui bit necessari del bus dati della CPU, ma tale hardware potrebbe non essere disponibile per oggetti disallineati, perché probabilmente ha più senso usare quei transistor per accelerare correttamente ottimizzato programmi.

In ogni caso, la seconda lettura della memoria che a volte è necessaria rallenterebbe la pipeline, indipendentemente da quanto l'hardware per scopi speciali fosse (ipoteticamente e scioccamente) dedicato a correggere le operazioni di memoria disallineate.


5

@joshperry ha dato un'ottima risposta a questa domanda. Oltre alla sua risposta, ho alcuni numeri che mostrano graficamente gli effetti che sono stati descritti, in particolare l'amplificazione 2X. Ecco un link a un foglio di calcolo di Google che mostra l'effetto di diversi allineamenti di parole. Inoltre, ecco un link a una sintesi di Github con il codice per il test. Il codice del test è adattato dall'articolo scritto da Jonathan Rentzsch al quale fa riferimento @joshperry. I test sono stati eseguiti su un Macbook Pro con un processore Intel Core i7 a 64 bit quad-core a 2,8 GHz e 16 GB di RAM.

inserisci qui la descrizione dell'immagine


4
Cosa significano xe ycoordinano?
shuva,

1
Quale core i7 di generazione? (Grazie per la pubblicazione di link al codice!)
Nick Desaulniers il

2

Se un sistema con memoria indirizzabile in byte ha un bus di memoria a 32 bit, ciò significa che esistono effettivamente quattro sistemi di memoria a larghezza di byte che sono tutti cablati per leggere o scrivere lo stesso indirizzo. Una lettura allineata a 32 bit richiederà informazioni memorizzate nello stesso indirizzo in tutti e quattro i sistemi di memoria, quindi tutti i sistemi possono fornire dati contemporaneamente. Una lettura non allineata a 32 bit richiederebbe ad alcuni sistemi di memoria di restituire dati da un indirizzo e alcuni di restituire dati dal successivo indirizzo superiore. Sebbene ci siano alcuni sistemi di memoria che sono ottimizzati per essere in grado di soddisfare tali richieste (oltre al loro indirizzo, hanno effettivamente un segnale "più uno" che li induce a utilizzare un indirizzo uno superiore a quello specificato) tale funzione aggiunge costi considerevoli e complessità a un sistema di memoria;


2

Se si dispone di un bus dati a 32 bit, le linee di indirizzo del bus degli indirizzi collegate alla memoria inizieranno da A 2 , quindi è possibile accedere a indirizzi allineati a 32 bit in un singolo ciclo di bus.

Pertanto, se una parola supera un limite di allineamento dell'indirizzo, ovvero A 0 per dati a 16/32 bit o A 1 per dati a 32 bit non sono zero, sono necessari due cicli di bus per ottenere i dati.

Alcune architetture / set di istruzioni non supportano l'accesso non allineato e genereranno un'eccezione in tali tentativi, quindi il codice di accesso non allineato generato dal compilatore richiede non solo cicli di bus aggiuntivi, ma istruzioni aggiuntive, rendendolo ancora meno efficiente.


0

Su PowerPC è possibile caricare un numero intero da un indirizzo dispari senza problemi.

Sparc e I86 e (penso) Itatnium sollevano eccezioni hardware quando provi questo.

Un carico a 32 bit contro quattro carichi a 8 bit non farà molta differenza sulla maggior parte dei processori moderni. Se i dati sono già nella cache o meno avranno un effetto molto maggiore.


Su Sparc si è trattato di un "errore del bus", da cui il capitolo "Errore del bus, prendi il treno" in "Expert C Programming: Deep C Secrets" di Peter Van der Linden
jjg
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.