align (C++)

W programie Visual Studio 2015 lub nowszym użyj alignas specyfikatora (C++11), aby kontrolować wyrównanie. Aby uzyskać więcej informacji, zobacz Wyrównanie.

Specyficzne dla firmy Microsoft

Służy __declspec(align(#)) do precyzyjnego kontrolowania wyrównania danych zdefiniowanych przez użytkownika (na przykład statycznych alokacji lub automatycznych danych w funkcji).

Składnia

__declspec( align(#) )deklarator

Uwagi

Pisanie aplikacji korzystających z najnowszych instrukcji procesora wprowadza pewne nowe ograniczenia i problemy. Wiele nowych instrukcji wymaga dopasowania danych do granic 16 bajtów. Wyrównanie często używanych danych do rozmiaru wiersza pamięci podręcznej procesora zwiększa wydajność pamięci podręcznej. Jeśli na przykład zdefiniujesz strukturę, której rozmiar jest mniejszy niż 32 bajty, możesz chcieć wyrównać 32 bajty, aby upewnić się, że obiekty tego typu struktury są efektywnie buforowane.

# to wartość wyrównania. Prawidłowe wpisy są liczbami całkowitymi dwóch z zakresu od 1 do 8192 (bajtów), takich jak 2, 4, 8, 16, 32 lub 64. declarator to dane, które deklarujesz zgodnie z ich dopasowaniem.

Aby uzyskać informacje na temat zwracania wartości typu size_t , która jest wymaganiem wyrównania typu, zobacz alignof. Aby uzyskać informacje na temat deklarowania nieprzygotowanych wskaźników podczas określania wartości docelowej procesorów 64-bitowych, zobacz __unaligned.

Można użyć __declspec(align(#)) podczas definiowania zmiennej struct, unionlub class, lub podczas deklarowania zmiennej.

Kompilator nie gwarantuje ani nie próbuje zachować atrybutu wyrównania danych podczas operacji kopiowania lub przekształcania danych. Na przykład memcpy można skopiować strukturę zadeklarowaną z dowolną lokalizacją __declspec(align(#)) . Zwykłe alokatory (na przykład malloc, C++ operator newi alokatory Win32) zwykle zwracają pamięć, która nie jest wyrównana do __declspec(align(#)) struktur ani tablic struktur. Aby zagwarantować, że miejsce docelowe operacji kopiowania lub przekształcania danych jest poprawnie wyrównane, użyj polecenia _aligned_malloc. Możesz też napisać własny alokator.

Nie można określić wyrównania parametrów funkcji. Po przekazaniu danych, które mają atrybut wyrównania według wartości na stosie, konwencja wywołująca kontroluje jego wyrównanie. Jeśli wyrównanie danych jest ważne w wywoływanej funkcji, skopiuj parametr do poprawnie wyrównanej pamięci przed użyciem.

Bez __declspec(align(#)), kompilator zwykle wyrównuje dane na granicach naturalnych na podstawie procesora docelowego i rozmiaru danych, do 4-bajtowych granic na 32-bitowych procesorach i 8-bajtowych granic procesorach 64-bitowych. Dane w klasach lub strukturach są wyrównane do klasy lub struktury co najmniej jej naturalnego wyrównania i bieżącego ustawienia pakowania (z #pragma pack lub opcji kompilatora /Zp ).

W tym przykładzie pokazano użycie elementu __declspec(align(#)):

__declspec(align(32)) struct Str1{
   int a, b, c, d, e;
};

Ten typ ma teraz atrybut wyrównania 32 bajtów. Oznacza to, że wszystkie wystąpienia statyczne i automatyczne są uruchamiane na granicy 32-bajtowej. Inne typy struktury zadeklarowane z tym typem jako element członkowski zachowują atrybut wyrównania tego typu. Oznacza to, że każda struktura z Str1 elementem ma atrybut wyrównania co najmniej 32.

sizeof(struct Str1) W tym miejscu wartość jest równa 32. Oznacza to, że jeśli zostanie utworzona tablica Str1 obiektów, a podstawa tablicy jest wyrównana do 32 bajtów, każdy element członkowski tablicy jest również wyrównany 32-bajtowy. Aby utworzyć tablicę, której podstawa jest poprawnie wyrównana do pamięci dynamicznej, użyj polecenia _aligned_malloc. Możesz też napisać własny alokator.

Wartość sizeof dowolnej struktury jest przesunięciem końcowego elementu członkowskiego oraz rozmiarem tego elementu członkowskiego zaokrąglonym do najbliższej wielokrotności największej wartości wyrównania elementu członkowskiego lub wartości wyrównania całej struktury, w zależności od tego, która wartość jest większa.

Kompilator używa tych reguł do wyrównania struktury:

  • O ile element nie zostanie zastąpiony wartością __declspec(align(#)), wyrównanie elementu członkowskiego struktury skalarnej jest minimalnym rozmiarem i bieżącym pakowaniem.

  • O ile nie zostanie zastąpiony elementem __declspec(align(#)), wyrównanie struktury jest maksymalną wartością wyrównania poszczególnych elementów członkowskich.

  • Element członkowski struktury jest umieszczany z przesunięciem od początku jego struktury nadrzędnej, która jest najmniejszą wielokrotną jego wyrównaniem większą lub równą przesunięciem końca poprzedniego elementu członkowskiego.

  • Rozmiar struktury jest najmniejszą wielokrotną jego wyrównaniem większą lub równą przesunięciem końca ostatniego elementu członkowskiego.

__declspec(align(#)) może tylko zwiększyć ograniczenia wyrównania.

Aby uzyskać więcej informacji, zobacz:

align Przykłady

W poniższych przykładach pokazano, jak __declspec(align(#)) wpływa na rozmiar i wyrównanie struktur danych. W przykładach założono następujące definicje:

#define CACHE_LINE  32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))

W tym przykładzie struktura jest definiowana S1 przy użyciu polecenia __declspec(align(32)). Wszystkie zastosowania definicji zmiennej S1 lub w innych deklaracjach typów są wyrównane do 32 bajtów. sizeof(struct S1) Zwraca wartość 32 i S1 ma 16 bajtów wypełnienia po 16 bajtach wymaganych do przechowywania czterech liczb całkowitych. Każdy int element członkowski wymaga wyrównania 4 bajtów, ale wyrównanie samej struktury jest deklarowane jako 32. Następnie ogólne wyrównanie wynosi 32.

struct CACHE_ALIGN S1 { // cache align all instances of S1
   int a, b, c, d;
};
struct S1 s1;   // s1 is 32-byte cache aligned

W tym przykładzie sizeof(struct S2) zwraca wartość 16, która jest dokładnie sumą rozmiarów elementów członkowskich, ponieważ jest to wielokrotność największego wymagania wyrównania (wielokrotność 8).

__declspec(align(8)) struct S2 {
   int a, b, c, d;
};

W poniższym przykładzie sizeof(struct S3) zwraca wartość 64.

struct S3 {
   struct S1 s1;   // S3 inherits cache alignment requirement
                  // from S1 declaration
   int a;         // a is now cache aligned because of s1
                  // 28 bytes of trailing padding
};

W tym przykładzie zwróć uwagę, że a ma wyrównanie typu naturalnego, w tym przypadku 4 bajty. S1 Jednak musi być wyrównana 32 bajty. 28 bajtów dopełnienia następuje po a, więc s1 zaczyna się od przesunięcia 32. S4 następnie dziedziczy wymaganie S1wyrównania , ponieważ jest to największe wymaganie wyrównania w strukturze. sizeof(struct S4) zwraca wartość 64.

struct S4 {
   int a;
   // 28 bytes padding
   struct S1 s1;      // S4 inherits cache alignment requirement of S1
};

Następujące trzy deklaracje zmiennych używają również metody __declspec(align(#)). W każdym przypadku zmienna musi być wyrównana do 32 bajtów. W tablicy adres podstawowy tablicy, a nie każdy element członkowski tablicy, jest wyrównany do 32 bajtów. Wartość sizeof dla każdego elementu członkowskiego tablicy nie ma wpływu na użycie elementu __declspec(align(#)).

CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;

Aby wyrównać każdy element członkowski tablicy, należy użyć kodu takiego jak:

typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];

W tym przykładzie zwróć uwagę, że wyrównanie samej struktury i wyrównanie pierwszego elementu ma taki sam efekt:

CACHE_ALIGN struct S6 {
   int a;
   int b;
};

struct S7 {
   CACHE_ALIGN int a;
               int b;
};

S6 mają S7 identyczne cechy wyrównania, alokacji i rozmiaru.

W tym przykładzie wyrównanie adresów początkowych a, b, ci d są odpowiednio 4, 1, 4 i 1.

void fn() {
   int a;
   char b;
   long c;
   char d[10]
}

Wyrównanie, gdy pamięć jest przydzielana na stercie, zależy od wywoływanej funkcji alokacji. Jeśli na przykład używasz mallocmetody , wynik zależy od rozmiaru operandu. Jeśli arg> = 8, zwracana pamięć jest wyrównana 8 bajtów. Jeśli arg< 8, wyrównanie zwracanej pamięci jest pierwszą mocą 2 mniejszą niż arg. Jeśli na przykład używasz malloc(7)metody , wyrównanie wynosi 4 bajty.

Definiowanie nowych typów za pomocą polecenia __declspec(align(#))

Typ można zdefiniować z właściwością wyrównania.

Można na przykład zdefiniować obiekt struct z wartością wyrównania w następujący sposób:

struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;

aType Teraz i bType mają ten sam rozmiar (8 bajtów), ale zmienne typu bType są wyrównane do 32 bajtów.

Wyrównywanie danych w magazynie lokalnym wątku

Statyczny magazyn lokalny wątków (TLS) utworzony za pomocą atrybutu __declspec(thread) i umieszczony w sekcji TLS na obrazie działa w celu wyrównania dokładnie tak jak normalne dane statyczne. Aby utworzyć dane tls, system operacyjny przydziela pamięć rozmiaru sekcji TLS i uwzględnia atrybut wyrównania sekcji TLS.

W tym przykładzie pokazano różne sposoby umieszczania dopasowanych danych do magazynu lokalnego wątku.

// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;

// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;

// create an aligned structure
struct CACHE_ALIGN S9 {
   int a;
   int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;

Jak align działa z pakowaniem danych

Opcja /Zp kompilatora pack i pragma mają wpływ na pakowanie danych dla elementów członkowskich struktury i unii. W tym przykładzie pokazano, jak /Zp i __declspec(align(#)) współdziałać ze sobą:

struct S {
   char a;
   short b;
   double c;
   CACHE_ALIGN double d;
   char e;
   double f;
};

W poniższej tabeli wymieniono przesunięcie każdego elementu członkowskiego w różnych /Zp wartościach (lub #pragma pack), pokazując, jak te dwa elementy wchodzą w interakcje.

Zmienna /Zp1 /Zp2 /Zp4 /Zp8
a 0 0 0 0
b 1 2 2 2
c 3 4 4 8
d 32 32 32 32
e 40 40 40 40
f 41 42 44 48
sizeof(S) 64 64 64 64

Aby uzyskać więcej informacji, zobacz /Zp (Wyrównanie składowe struktury).

Przesunięcie obiektu zależy od przesunięcia poprzedniego obiektu i bieżącego ustawienia pakowania, chyba że obiekt ma __declspec(align(#)) atrybut, w którym to przypadku wyrównanie jest oparte na przesunięcie poprzedniego obiektu i __declspec(align(#)) wartość obiektu.

END Microsoft Specific

Zobacz też

__declspec
Przegląd konwencji ABI ARM
Konwencje kodowania x64