2012-06-19 2 views
8

In seguito alla discussione da this post, ho capito che il motivo principale per l'allineamento dei membri della struttura è la prestazione (e alcune restrizioni di architettura).Doppio allineamento

Se studieremo Microsoft (Visual C++), Borland/CodeGear (C++ - Builder), Digital Mars (DMC) e GNU (GCC) quando si compila per 32-bit x86: L'allineamento per int è di 4 byte e se int non è allineato, può succedere che vengano letti 2 file di banchi di memoria.

La mia domanda è: perché non fare allineare anche double con 4 byte? 4 byte allineati double anche causeranno 2 file di banchi di memoria di lettura ....

Ad esempio nel seguente esempio, poiché double è 8-allineati, la dimensione effettiva della struttura sarà sizeof(char) + (alignment for double padding) + sizeof(int) = 20 bytes.

typedef struct structc_tag{ 
    char  c; 
    double  d; 
    int   s; 
} structc_t; 

Grazie

+2

L'allineamento è definito dall'implementazione, quindi non ha molto senso fare questa domanda in modo così generale. Dovresti scegliere un compilatore/piattaforma specifico per informazioni. –

+0

In realtà ottengo 24 byte ... –

+1

C'è anche un riempimento di 4 byte all'estremità per garantire che il doppio sia ancora allineato negli elementi di una matrice di queste strutture. Generalmente. –

risposta

9

un commento estesa:

Secondo la documentazione di GCC su -malign-double:

Allineamento double variabili su una parola due boundary produce codice che gira leggermente più veloce su un Pentium a spese di più memoria.

Su x86-64, -malign-double è abilitato per impostazione predefinita.

Attenzione: se si utilizza lo switch -malign-double, le strutture contenenti i tipi precedenti sono allineate in modo diverso rispetto alle specifiche dell'interfaccia binaria dell'applicazione pubblicata per il 386 e non sono compatibili binario con le strutture nel codice compilato senza tale opzione.

Una parola qui significa parola i386 che è a 32 bit.

Windows utilizza l'allineamento a 64 bit dei valori double anche in modalità a 32 bit, mentre SysV i386 conforme conforme ABI utilizza l'allineamento a 32 bit. L'API di Windows a 32 bit, Win32, proviene da Windows NT 3.1, che, a differenza delle attuali versioni di Windows di generazione, aveva come target Intel i386, Alpha, MIPS e persino l'oscura Intel i860. Poiché i sistemi RISC nativi come Alpha e MIPS richiedono che i valori double siano allineati a 64 bit (altrimenti si verifica un errore hardware), la portabilità potrebbe essere stata la logica dietro l'allineamento a 64 bit nell'ABI i386 Win32.

sistemi x86 a 64 bit, noto anche come AMD64 o x86-64, o x64, richiedono double valori da 64 bit allineati altrimenti si verifica un guasto disallineamento e l'hardware fa un costoso "fix-up", che rallenta considreably giù l'accesso alla memoria. Ecco perché i valori double sono allineati a 64 bit in tutti i moderni ABI x86-64 (SysV e Win32).

0

La domanda è altamente specifica piattaforma in termini di architettura della CPU. Ad esempio, in architetture che danno penalità per operare su indirizzi non a 4 byte allineati, l'allineamento delle variabili (ad esempio i loro indirizzi) a 4 byte può evitare tale penalità.

I compilatori sono abbastanza buoni con cose come questa, specialmente quando fornite loro l'architettura della CPU di destinazione per la quale ottimizzare in modo che possano fare la maggior parte di ciò per voi, e anche molte altre ottimizzazioni. Dai un'occhiata al flag GCC -march, ad esempio, che ti consente di scegliere come target le architetture della CPU.

+0

Sarò più specifico :) - Voglio capire il motivo di progettazione di tali piattaforme per rendere 8 byte doppio allineamento – Yakov

6

La maggior parte dei compilatori allinea automaticamente i valori dei dati alla dimensione della parola della piattaforma o alle dimensioni del tipo di dati, a seconda di quale di esse sia più piccola. La stragrande maggioranza dei processori consumer e aziendali utilizza una dimensione word di 32 bit. (Anche i sistemi a 64 bit usano solitamente 32 bit come dimensione nativa di una parola)

Come tale, l'ordinamento dei membri nella tua struttura potrebbe sprecare un po 'di memoria. Nel tuo caso specifico, stai bene. Aggiungerò nei commenti l'impronta reale di memoria utilizzata:

typedef struct structc_tag{ 
      char  c; // 1 byte 
         // 3 bytes (padding) 
      double  d; // 8 bytes 
      int   s; // 4 bytes 
} structc_t;    // total: 16 bytes 

Questa regola si applica alle strutture troppo, quindi, anche se li riorganizzate in modo che il campo più piccolo era ultima, si avrebbe ancora una struttura delle stesse dimensioni (16 byte).

typedef struct structc_tag{ 
      double  d; // 8 bytes 
      int   s; // 4 bytes 
      char  c; // 1 byte 
         // 3 bytes (padding) 
} structc_t;    // total: 16 bytes 

Se si dovesse dichiarare più campi che erano più piccola di 4 byte, si poteva vedere alcune riduzioni di memoria se li raggruppate in base alle dimensioni. Per esempio:

typedef struct structc_tag{ 
      double  d1; // 8 bytes 
      double  d2; // 8 bytes 
      double  d3; // 8 bytes 
      int   s1; // 4 bytes 
      int   s2; // 4 bytes 
      int   s3; // 4 bytes 
      short  s4; // 2 bytes 
      short  s5; // 2 bytes 
      short  s6; // 2 bytes 
      char  c1; // 1 byte 
      char  c2; // 1 byte 
      char  c3; // 1 byte 
          // 3 bytes (padding) 
} structc_t;    // total: 48 bytes 

Dichiarazione di una struct stupido potrebbe perdere un sacco di memoria, a meno che il compilatore riordina gli elementi (che, in generale, non sarà così, senza essere esplicitamente detto di)

typedef struct structc_tag{ 
      int   s1; // 4 bytes 
      char  c1; // 1 byte 
          // 3 bytes (padding) 
      int   s2; // 4 bytes 
      char  c2; // 1 byte 
          // 3 bytes (padding) 
      int   s3; // 4 bytes 
      char  c3; // 1 byte 
          // 3 bytes (padding) 
} structc_t;    // total: 24 bytes 
          // (9 bytes wasted, or 38%) 
          // (optimal size: 16 bytes (1 byte wasted)) 

Le doppie sono più grandi di 32 bit e, pertanto, secondo la regola della prima sezione, sono allineate a 32 bit. Qualcuno ha menzionato un'opzione del compilatore che modifica l'allineamento e che l'opzione del compilatore di default è diversa tra i sistemi a 32 e 64 bit, anche questo è valido. Quindi la vera risposta al doppio è che dipende dalla piattaforma e dal compilatore.

Le prestazioni della memoria sono regolate da parole: il caricamento dalla memoria avviene in fasi che dipendono dal posizionamento dei dati. Se i dati coprono una parola (i.e. è allineata alla parola), solo quella parola deve essere caricata. Se non è allineato correttamente (cioè un int a 0x2), il processore deve caricare 2 parole per leggere correttamente il suo valore. Lo stesso vale per i doppi, che normalmente occupano 2 parole, ma se disallineati, occupano 3. Nei sistemi a 64 bit in cui è possibile il caricamento nativo di quantità a 64 bit, si comportano come 32 bit inte su sistemi a 32 bit in quanto se allineati correttamente , possono essere recuperati con un carico, ma per il resto, richiederanno 2.

+0

Nonostante gli esempi siano belli - la tua risposta non risponde alla mia domanda: sto cercando il motivo per cui i progettisti donano t allign double di 4 byte – Yakov

+0

Modificherò la mia risposta per riflettere questo. – Wug

2

Prima di tutto è l'architettura che impone il requisito di allineamento, alcuni tollerano gli accessi di memoria non allineati, altri no.

lascia dare piattaforma x86-32bit finestre come esempio, in questa piattaforma il requisito di allineamento per int e long è 4 bytes e 8 bytes rispettivamente.

È chiaro il motivo per cui il requisito di allineamento int è 4 bytes, semplicemente in modo che la cpu possa leggerlo tutto solo con un accesso.

Il motivo per cui il requisito di allineamento per doulbe è 8 bytes e non 4 bytes, è perché se fosse 4 bytes poi pensare a cosa accadrà se questa doppia era situato all'indirizzo 60 e la dimensione linea di cache era 64bits, in questo caso il processore ha bisogno di caricare 2 linee di cache dalla memoria alla cache, ma se questo double è allineato, ciò non accadrà, poiché in questo caso lo double sarà sempre parte di una linea di cache e non diviso tra due.

...58 59|60 61 62 63 64 65 66 67|68 69 70 71... 
    - - - - - - - - - - - - - - - - - 
----------+ + + + . . + + + +-------------- 
      |   . .   | 
----------+ + + + . . + + + +-------------- 
         . .   
     Cache Line 1  . . Cache Line 2 
    - - - - - - - - - - - - - - - - -