2016-08-10 21 views
15

Nie mogłem znaleźć przyzwoitego dokumentu wyjaśniającego, jak działa system wyrównania i dlaczego niektóre typy są bardziej wyrównane niż inne.Co to jest wyrównanie danych? Dlaczego i kiedy powinienem się martwić, gdy wskaźniki literowania w C?

+10

Pamiętaj, że C i C++, chociaż są syntaktycznie podobne w niektórych częściach, są naprawdę dwoma różnymi językami z różnymi semantykami i regułami. Więc nie spamuj tagów niezwiązanych z językiem, o który naprawdę pytasz. –

+2

Cóż, standard powinien być dość pouczający. – Downvoter

+0

dla C++, [tutaj] (http://eel.is/c++draft/basic.align) jest tym, co standard o nim mówi – Carousel

Odpowiedz

11

Spróbuję wyjaśnić w skrócie.

Co to jest wyrównanie danych?

Architektura komputera składa się z procesora i pamięci. Pamięć jest organizowana w komórkach, więc:

0x00 | data | 
0x01 | ... | 
0x02 | ... | 

Każda komórka pamięci ma określony rozmiar, ilość bitów może przechowywać. Jest to zależne od architektury.

Po zdefiniowaniu zmiennej w swoim programie C/C++, jeden program zajmuje jedną komórkę: lub więcej.

Na przykład

int variable = 12; 

Załóżmy, każda komórka zawiera 32 bitów i wielkość int typu wynosi 32 bitów, to w gdzieś w pamięci:

variable: | 0 0 0 c | // c is hexadecimal of 12. 

Kiedy Twój procesor ma pracować na tym zmienna, musi ją wprowadzić do rejestru. Procesor może pobrać "1 zegar" małą ilość bitów z pamięci, rozmiar ten jest zwykle nazywany WORD. Ten wymiar zależy również od architektury.

Teraz załóżmy, że masz zmienną, która jest przechowywana, z powodu jakiegoś przesunięcia, w dwóch komórkach.

Na przykład mam dwie różne części do przechowywania danych (mam zamiar użyć „reprezentację ciąg do bardziej jasne”):

data1: "ab" 
data2: "cdef" 

Więc pamięć zostanie skomponowany w taki sposób (2): różne komórki

|a b c d|  |e f 0 0| 

Oznacza to, data1 zajmuje tylko połowę komórki, tak data2 zajmuje pozostałą część i część drugą komórkę.

Załóżmy teraz, że procesor chce odczytać data2. Procesor potrzebuje 2 zegarów w celu uzyskania dostępu do danych, ponieważ w ciągu jednego zegara odczytuje pierwszą komórkę, a w drugim zegarze odczytuje pozostałą część w drugiej komórce.

Jeśli możemy wyrównaćdata2 zgodnie z niniejszym pamięci-przykład, możemy wprowadzenie pewnego rodzaju wyściółką i przesunięcie data2 wszystkim w drugiej komórce.

|a b 0 0|  |c d e f| 
    --- 
    padding 

W ten sposób procesor będzie tylko stracić „1 zegar” w celu dostępu do data2.

Co robi system align

System align właśnie wprowadza że wyściółka aby wyrównać dane z pamięci systemu, należy pamiętać, zgodnie z architekturą. Gdy dane są wyrównane w pamięci, nie marnujesz cykli procesora w celu uzyskania dostępu do danych.

Wykonuje się to ze względów związanych z wydajnością (99% razy).

+0

'Robi się to ze względów związanych z wydajnością (99% razy). 'IMO natychmiast powinno nastąpić zrzeczenie się, że 99% statystyk jest wytwarzanych na miejscu, a następnie dokładne wyjaśnienie możliwych przyczyn, które powodują inne' 1 % ". Podobnie jak niewyrównane dostępy powodujące pułapki, wyjątki sprzętowe itp. Mówienie o tym głównie o prędkości bez wyjaśnienia innych, często ważniejszych powodów, jest prawie wcale lepsze niż stwierdzenie, że chodzi wyłącznie o szybkość, która byłaby fałszywa. –

13

Jest to "definicja implementacji", tj. Wymagania dotyczące wyrównania nie są częścią specyfikacji języka.

Różne procesory mają różne wymagania dotyczące wyrównania. Niektórzy nie mogli adresować wartości 16-bitowej na nieparzystym adresie, niektórzy mogli. Niektóre z nich nie mogły zaadresować wartości zmiennoprzecinkowej, chyba że zostały wyrównane do adresu podzielnego przez jego rozmiar, inne mogłyby. I tak dalej. Niektórzy mieliby dostęp do źle wyrównanych obiektów danych wolniej niż prawidłowo wyrównane, inni mogliby przejść przez niewyrównany dostęp.

Dlatego standard językowy nie zawiera informacji o tym, który typ musi być wyrównany w określony sposób (ponieważ nie mógł), ale pozostawił go w "implementacji" - w tym przypadku w backendie kompilatora .

Jeśli wskażesz typ krojów pisma, możesz zmusić kod do adresowania danego obiektu pod adresem, do którego nie można go adresować. Należy upewnić się, że wymagania dotyczące wyrównania typu "starego" są następujące: co najmniej tak ścisłe, jak w przypadku "nowego" typu.

W języku C++ (C++ 11 w górę), operator alignof informuje o wymaganiach dotyczących wyrównania dla danego typu. Otrzymasz również operator alignas, aby wymusić bardziej ścisłe wyrównanie na danym typie lub obiekcie.

W C (C11 w górę), masz _Alignof i _Alignas operatorów, w <stdalign.h> Chusta do alignof/alignasconvenience macros. (Dzięki, Lundin - C11 nie jest moją mocną stroną.)

+4

Zgodnie z C11, istnieje "_Alignof" w C. – Lundin

+0

To jest lepsza odpowiedź na wspomnienie, że reguły dopasowania zależą od architektury istnieją dla prędkości lub konieczności - wspominając obie - podczas gdy każda inna odpowiedź skupia się tylko na jednym z dwóch do tego stopnia, że ​​bagatelizują znaczenie drugiego czynnika niemal do zera. –

7

Niektóre systemy mogą uzyskać dostęp do pamięci w częściach, powiedzmy, słów 32-bitowych (4 bajty). To ograniczenie sprzętowe. Oznacza to, że rzeczywisty adres wchodzący do kontrolera pamięci powinien być podzielny przez cztery (ponieważ nadal adresuje bajty). Więc kiedy spróbujesz znaleźć słowo pod adresem, który nie jest podzielny przez cztery, istnieją dwie opcje - albo kompilator spróbuje wygenerować jakiś wymyślny kod, żeby skomponować słowo z dwóch dostępów do pamięci, ale nie zawsze tak jest. . Czasami po prostu wygeneruje kod dostępu do 4 bajtów z podanego adresu. A następnie procesor zakończy się niepowodzeniem z błędem wyrównania danych.

Co prowadzi do ograniczenia języka narzucającego się.

Rozważmy kod (zły):

uint8_t a[] = {1,2,3,4,5,6}; 
uint32_t b = *(uint32_t*)&a[1]; 

i zakładamy a jest dostosowana do podzielna przez cztery granice. Następnie druga linia próbuje odczytać słowo z adresu jego elementu sekund, tj. Adres nie podzielny przez cztery. Doprowadzi to do błędu wyrównania. Ale w C jest to po prostu zabronione przez ścisłą regułę aliasingu .

+0

Kompilator gcc ma opcję '-Wcast-align', aby konkretnie ostrzegać przed takimi błędami. – hlovdal

+0

'albo kompilator spróbuje wygenerować jakiś fantazyjny kod, aby skomponować słowo z dwóch dostępów do pamięci, ale zwykle tak nie jest." Proszę zdefiniować "zwykle". x86 może obsłużyć nieprzydzielone dostępy tylko dla podstawowych instrukcji ładowania/przechowywania przez agregowanie odczytów/zapisów, [np. link] (http://stackoverflow.com/questions/12491578/whats-the-actual-effect-efuc-uccessigned-unaligned-accesses-on-x86). I czy x86 nie jest naszym standardowym punktem odniesienia dla takich terminów jak "zwykły"? O jakich architekturach mówisz? –

+0

@underscore_d Zamienię "zwykle" na "nie zawsze tak jest". Jestem przyzwyczajony do ARM i procesorów MCU niższej klasy, więc są dla mnie zwyczajne. –

Powiązane problemy