2010-01-05 7 views
26

Kann mir jemand eine kurze und plausible Erklärung dafür geben, warum der Compiler Datenstrukturen auffüllen lässt, um seine Mitglieder auszurichten? Ich weiß, dass es getan ist, damit die CPU effizienter auf die Daten zugreifen kann, aber ich verstehe nicht, warum das so ist.Warum ist die Ausrichtung der Datenstruktur wichtig für die Leistung?

Und wenn dies nur CPU-bezogen ist, warum ist eine doppelte 4 Byte in Linux ausgerichtet und 8 Byte in Windows ausgerichtet?

+2

Es gibt zwei getrennte, aber miteinander verbundene Probleme: die Datenausrichtung und die Datenstruktur padding –

+0

gcc dobules auf 8 Bytes als auch auf x86-Rechner ‚Compiler jedoch gleiche wie Microsoft ausrichtet. – nos

+0

Warum werden Doppelbyte 8 Byte ausgerichtet, wenn die CPU Daten in Blöcken von 4 Bytes liest? Es ist dann egal, ob das Double 8 oder 4 Byte ausgerichtet ist, oder? – Mat

Antwort

15

Ausrichtung hilft die CPU auf eine effiziente Weise Daten aus dem Speicher holen: weniger Cache-Fehltreffer/flush, weniger Bustransaktionen usw.

einige Speichertypen (zB RDRAM, DRAM etc.) müssen in einem strukturierten zugegriffen werden soll, Weise (ausgerichtete "Wörter" und "Burst-Transaktionen", dh viele Wörter gleichzeitig), um effiziente Ergebnisse zu erzielen. Dies ist auf viele Dinge, unter welchen:

  1. Rüstzeit: Zeit, die für die Speichervorrichtungen die Speicherplätze zugreifen zu
  2. Bus Schiedskopf dh viele Geräte möglicherweise Zugriff auf die Speichereinrichtung wollen

"Padding" wird verwendet, um die Ausrichtung von Datenstrukturen zu korrigieren, um die Übertragungseffizienz zu optimieren.


Mit anderen Worten, der Zugriff auf eine "falsch ausgerichtete" Struktur führt zu einer niedrigeren Gesamtleistung. Ein gutes Beispiel für eine solche Fallstricke: Angenommen, eine Datenstruktur ist falsch ausgerichtet und erfordert, dass die CPU/Speichersteuerung 2 Bustransaktionen durchführt (anstelle von 1), um die genannte Struktur zu holen, ist die Leistung folglich folglich geringer.

+0

Also was genau passiert, wenn, sagen wir, ein Float ist 1byte ausgerichtet? – Mat

+0

@Mat: abhängig davon, "wo" die "float-Variable" im Speicher zugewiesen wird, variiert die Effizienz beim Zugriff auf diese "float-Variable". – jldupont

+0

aber verstehe ich richtig, dass die Leistung für den Zugriff auf eine schlecht ausgerichtete Float nicht schlechter als der Zugriff auf eine korrekt ausgerichtete Doppel ist? – Mat

12

Die CPU holt Daten aus dem Speicher in Gruppen von 4 Bytes (es hängt tatsächlich von der Hardware ihre 8 oder andere Werte für einige Arten von Hardware ab, aber bleiben Sie bei 4, um es einfach zu halten), ist alles gut Daten beginnen in einer Adresse, die durch 4 teilbar ist, die CPU geht zur Speicheradresse und lädt die Daten.

Nehmen wir jetzt an, die Daten beginnen in einer nicht durch 4 teilbare Adresse, aus Gründen der Einfachheit bei Adresse 1, die CPU muss Daten von Adresse 0 übernehmen und dann einen Algorithmus anwenden, um das Byte an der Adresse 0 zu dumpen Zugriff auf die eigentlichen Daten bei Byte 1. Das kostet Zeit und damit die Präformanz. Es ist also viel effizienter, alle Datenadressen auszurichten.

+1

nicht unbedingt in Gruppen von 4 Bytes: Dies hängt stark vom CPU-Typ ab. – jldupont

+1

Dies ist ein bisschen vereinfacht: Es ist in Ordnung, einen BYTE-Wert an einem Speicherplatz zu haben, der nicht durch 4 teilbar ist. Es ist auch in Ordnung, einen WORD-Wert an einem Speicherplatz mit 2 teilbar zu haben. – Niki

+3

Ich wollte einfach; -) – Alon

3

Neben jldupont Antwort haben einige Architekturen Lade- und Speicherbefehle (den verwendeten Schreib-/Lesezugriffe auf und aus dem Gedächtnis), dass nur arbeiten auf wordaligned Grenzen - so, ein nicht-ausgerichtete Wort zu laden aus dem Speicher würde zwei Ladeanweisungen, eine Schiebeanweisung und dann eine Maskenanweisung nehmen - viel weniger effizient!

+0

liest Lesen eines Typs, der kleiner als 4 Bytes ist (Bool, kurz, was auch immer) immer eine Maskierungsoperation und wenn es nicht 4byte ausgerichtet auch eine Shift-Anweisung? – Mat

+0

@Mat: nicht unbedingt eine "Shift-Anweisung": Auf der Schaltungsebene werden die Chip-Designer verwendet, um diese Art von Operation als etwas in der Art von "Byte-Swapper" zu bezeichnen. – jldupont

6

Eine Cache-Zeile ist eine Grundeinheit des Caching. Normalerweise sind es 16-64 Bytes oder mehr.

Pentium IV: 64 Bytes; Pentium Pro/II: 32 Bytes; Pentium I: 32 Bytes; 486: 16 Bytes.

myrandomreader: 
    ; ... 
    ; ten instructions to generate next pseudo-random 
    ; address in ESI from previous address 
    ; ... 
    MOV EAX, DS:[ESI] ; X 
    LOOP myrandomreader 

Für Speicher gelesen spreizt zwei Cache-Lines:

(für die L1-Cache-Miss) der Prozessor für die gesamte Cache-Zeile 1 zu lesenden von L2-> L1 in den Prozessor warten muß, bevor es kann die zweiten Cache-Zeile-Anfrage, einen kurzen Ausführungsströmungsabriß verursacht

(zur L2-Cache-Miss) muß der Prozessor warten, zwei Burst von L3-Cache (falls vorhanden) oder dem Hauptspeicher lesen eher zu vervollständigen als ein

Processor Stände

  • A random 4 Byte lesen wird eine Cache-Line-Grenze etwa 5% der Zeit für die 64-Byte-Cache-Lines, 10% für die 32-Byte-one und 20% für die 16-Byte-Einsen spreizen.

  • Bei einigen Anweisungen zu fehlausgerichteten Daten können zusätzliche Ausführungskosten anfallen, selbst wenn sie sich innerhalb einer Cache-Line befinden. Dies wird auf der Intel-Website für einige SSE-Anweisungen diskutiert.

  • Wenn Sie die Strukturen selbst definieren, kann es sinnvoll bei dem all < 32bit Datenfeldern zusammen in einem struct zu suchen, so dass padding-Overhead reduziert wird oder alternativ zu prüfen, ob es besser ist, auf oder auszuschalten Verpackung für eine bestimmte Struktur.

  • Auf MIPS und vielen anderen Plattformen erhalten Sie nicht die Wahl und müssen ausrichten - Kernel-Ausnahme, wenn Sie nicht !!

  • Ausrichtung kann für Sie auch besonders wichtig sein, wenn Sie E/A am Bus ausführen oder atomare Operationen wie atomare Inkremente/Dekrement verwenden oder wenn Sie Ihren Code auf Nicht-Intel portieren möchten.

  • Auf Intel nur (!) Code, ist eine gängige Praxis, eine Reihe von gepackten Strukturen für Netzwerk und Festplatte zu definieren, und eine andere gepolsterte Menge für In-Memory und Routinen zum Konvertieren von Daten zwischen diesen Formaten haben (auch in Betracht ziehen "endianness" für die Disk- und Netzwerkformate.

Verwandte Themen