Speicherausrichtung: Wie verwende ich alignof / alignas?


81

Ich arbeite gerade mit Shared Memory.

Ich kann nicht verstehen alignofund alignas.

cppreference ist unklar: Gibtalignof "Ausrichtung" zurück, aber was ist "Ausrichtung"? Anzahl der hinzuzufügenden Bytes für den nächsten auszurichtenden Block? gepolsterte Größe? Stapelüberlauf / Blogeinträge sind ebenfalls unklar.

Kann jemand klar alignofund deutlich erklären alignas?


1
cppreference versucht, eher eine Referenz als ein Tutorial zu sein
Cubbi

1
@Cubbi: Sie können auch auf cplusplus.com überprüfen, es gibt Debatten, welche Website besser ist, für bestimmte Themen ist cplusplus besser, für andere cppreference ist besser, ich fand, dass beide Websites bestimmte Zeiten nicht eonugh sind
CoffeDeveloper

2
@DarioOO Ich habe nur geantwortet, warum cppreference das Konzept der Ausrichtung auf der alignofSeite nicht erklärt (jetzt auf der in Bearbeitung befindlichen Objektseite ). Ich sehe nicht, wie wichtig cplusplus.com ist.
Cubbi

Antworten:


81

Die Ausrichtung ist eine Einschränkung, an welchen Speicherpositionen das erste Byte eines Werts gespeichert werden kann. (Es ist erforderlich, um die Leistung auf Prozessoren zu verbessern und die Verwendung bestimmter Anweisungen zu ermöglichen, die nur für Daten mit bestimmter Ausrichtung funktionieren. Beispielsweise muss SSE auf 16 Byte und AVX auf 32 Byte ausgerichtet sein.)

Die Ausrichtung von 16 bedeutet, dass Speicheradressen, die ein Vielfaches von 16 sind, die einzigen gültigen Adressen sind.

alignas

Erzwinge die Ausrichtung auf die erforderliche Anzahl von Bytes. Sie können nur Potenzen von 2: 1, 2, 4, 8, 16, 32, 64, 128, ... ausrichten.

#include <cstdlib>
#include <iostream>

int main() {
    alignas(16) int a[4];
    alignas(1024) int b[4];
    printf("%p\n", a);
    printf("%p", b);
}

Beispielausgabe:

0xbfa493e0
0xbfa49000  // note how many more "zeros" now.
// binary equivalent
1011 1111 1010 0100 1001 0011 1110 0000
1011 1111 1010 0100 1001 0000 0000 0000 // every zero is just a extra power of 2

das andere Schlüsselwort

alignof

ist sehr praktisch, so etwas kann man nicht machen

int a[4];
assert(a % 16 == 0); // check if alignment is to 16 bytes: WRONG compiler error

aber du kannst es tun

assert(alignof(a) == 16);
assert(alignof(b) == 1024);

Beachten Sie, dass dies in Wirklichkeit strenger ist als eine einfache "%" -Operation (Modul). Tatsächlich wissen wir, dass etwas, das auf 1024 Bytes ausgerichtet ist, notwendigerweise auf 1, 2, 4, 8 Bytes ausgerichtet ist, aber

 assert(alignof(b) == 32); // fail.

Genauer gesagt gibt "alignof" die größte Potenz von 2 zurück, um etwas auszurichten.

Alignof ist auch eine gute Möglichkeit, die Mindestausrichtungsanforderungen für grundlegende Datentypen im Voraus zu kennen (wahrscheinlich wird 1 für Zeichen, 4 für Float usw. zurückgegeben).

Immer noch legal:

alignas(alignof(float)) float SqDistance;

Etwas mit einer Ausrichtung von 16 wird dann auf die nächste verfügbare Adresse gesetzt, die ein Vielfaches von 16 ist (es kann eine implizite Auffüllung von der zuletzt verwendeten Adresse geben).


10
Im Gegensatz zu sizeof, alignofkann nur auf ein angewendet werden type-id.
Neverhoodboy

wird alignof()(und das Gegenstück alignas()) zur Kompilierungszeit ausgewertet, also kein Laufzeitaufwand?
Nonsensation

Nein. Es ist nicht möglich, dass der Compiler dies in sehr wenigen Fällen als Optimierung durchführt, aber im Allgemeinen weiß er nicht, wie die Speicheradressen ausgerichtet sind, bevor die beiden Funktionen ausgewertet werden. Schauen Sie sich einfach die Assembly an, die durch mein Beispiel generiert wurde: goo.gl/ZbemBF
CoffeDeveloper

1
@Serthy Zur Verdeutlichung alignof ist eine Kompilierungszeitkonstante. alignasist nicht und muss durch Ihre Implementierung von new(Anforderung des Standards) oder durch einen benutzerdefinierten Standard- Allokator unterstützt werden .
Aidiakapi

Gute Antwort, aber es braucht eine Behandlung von structund Mitgliedern der Struktur, die sind static. alignasstellt sich als viel kniffliger heraus __attribute__((aligned)), besonders unter Compilern wie Clang.
JWW

11

Die Ausrichtung ist keine Polsterung (obwohl manchmal eine Polsterung eingeführt wird, um die Ausrichtungsanforderungen zu erfüllen). Es ist eine intrisische Eigenschaft eines C ++ - Typs. Um es in Standardese ( 3.11[basic.align]) zu setzen

Objekttypen haben Ausrichtungsanforderungen (3.9.1, 3.9.2), die die Adressen einschränken, an denen ein Objekt dieses Typs zugewiesen werden kann. Eine Ausrichtung ist ein implementierungsdefinierter ganzzahliger Wert, der die Anzahl der Bytes zwischen aufeinanderfolgenden Adressen darstellt, an denen ein bestimmtes Objekt zugewiesen werden kann. Ein Objekttyp stellt für jedes Objekt dieses Typs eine Ausrichtungsanforderung. Eine strengere Ausrichtung kann mit dem Ausrichtungsspezifizierer (7.6.2) angefordert werden.


1
Sehr interessant. Würde es Ihnen etwas ausmachen, einige Beispiele zu nennen? Ist alignof (struct X) == sizeof (struct X)? Warum nicht ?
Offirmo

1
@Offirmo nein, außer durch Zufall: struct X { char a; char b}hat Größe 2 und Ausrichtungsanforderung 1 auf vernünftigen Systemen (es kann an jeder Adresse zugewiesen werden, da ein Zeichen an jeder Adresse zugewiesen werden kann)
Cubbi

Ausrichtungsanforderung von 1 ???? Oh, ich verstehe: Ich dachte, dass die Ausrichtung immer an "natürlichen" 32-Bit / 64-Bit-Grenzen liegt, aber anscheinend nicht. Das erklärt die Dinge ... Auf normalen Maschinen beträgt das Ergebnis von alignof () immer maximal 4 (32 Bit) oder 8 (64 Bit). Habe ich recht?
Offirmo

@Offirmo "natürliche" Ausrichtung wird maximal sein alignof(std::max_align_t), was 16unter meinem Linux ist (unabhängig davon, ob -m32 oder -m64 kompiliert), aber Sie können es strenger machen mitalignas
Cubbi

7

Jeder Typ hat eine Ausrichtungsanforderung. Im Allgemeinen ist dies so, dass auf Variablen des Typs effizient zugegriffen werden kann, ohne dass die CPU mehr als einen Lese- / Schreibzugriff generieren muss, um ein bestimmtes Mitglied des Datentyps zu erreichen. Darüber hinaus wird ein effizientes Kopieren der gesamten Variablen sichergestellt. alignofgibt die Ausrichtungsanforderung für den angegebenen Typ zurück.

alignaswird verwendet, um eine Ausrichtung eines Datentyps zu erzwingen (sofern nicht weniger streng ist, alignofdass der Datentyp zurückgegeben wird)


3

Die Ausrichtung ist eine Eigenschaft, die sich auf die Speicheradresse bezieht. Wir können einfach sagen, dass wenn eine Adresse X auf Z ausgerichtet ist, x ein Vielfaches von Z ist, dh X = Zn + 0. Hier ist es wichtig, dass Z immer eine Potenz von 2 ist.

Die Ausrichtung ist eine Eigenschaft einer Speicheradresse, ausgedrückt als numerische Adresse modulo mit einer Potenz von 2. Beispielsweise ist die Adresse 0x0001103F modulo 4 3. Diese Adresse soll auf 4n + 3 ausgerichtet sein, wobei 4 die gewählte Potenz von angibt 2. Die Ausrichtung einer Adresse hängt von der gewählten Potenz von 2 ab. Das gleiche Adressmodulo 8 ist 7. Eine Adresse wird als auf X ausgerichtet bezeichnet, wenn ihre Ausrichtung Xn + 0 ist.

Die obige Anweisung befindet sich in der Microsoft C ++ - Referenz.

Wenn ein Datenelement mit einer Adresse im Speicher gespeichert ist, die auf seine Größe ausgerichtet ist, wird dieses Datenelement als natürlich ausgerichtet bezeichnet , andernfalls wird es falsch ausgerichtet. Zum Beispiel: Wenn eine ganzzahlige Variable mit einer Größe von 4 Bytes in einer Adresse gespeichert ist, die auf 4 ausgerichtet ist, können wir sagen, dass die Variable natürlich ausgerichtet ist, dh die Adresse der Variablen sollte ein Vielfaches von 4 sein.

Die Compiler versuchen immer, Fehlausrichtungen zu vermeiden. Für einfache Datentypen werden die Adressen so gewählt, dass sie ein Vielfaches der Größe der Variablen in Bytes sind. Der Complier ist auch bei Strukturen für die natürliche Ausrichtung und den natürlichen Zugriff geeignet. Hier wird die Struktur auf das Maximum der Größe verschiedener Datenelemente in der Struktur ausgerichtet.

    struct abc
   {
        int a;
        char b;
   };

Hier ist die Struktur abc auf 4 ausgerichtet, was der Größe des int-Elements entspricht, die offensichtlich größer als 1 Byte ist (Größe des char-Elements).

alignas

Dieser Bezeichner wird verwendet, um benutzerdefinierte Typen wie Struktur, Klasse usw. an einem bestimmten Wert auszurichten, der eine Potenz von 2 ist.

ausrichten

Dies ist eine Art Operator, um den Wert abzurufen, an dem die Struktur oder der Klassentyp ausgerichtet ist. z.B:

#include <iostream>
struct alignas(16) Bar
{
    int i; // 4 bytes
    int n; // 4 bytes
    short s; // 2 bytes
};
int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.