Cel wyrównania pamięci

196

Trzeba przyznać, że nie rozumiem. Załóżmy, że masz pamięć ze słowem o długości 1 bajta. Dlaczego nie można uzyskać dostępu do 4-bajtowej zmiennej w pojedynczym dostępie do pamięci na niezaangażowanym adresie (tzn. Nie można podzielić przez 4), jak ma to miejsce w przypadku wyrównanych adresów?

arka
źródło
17
Po zrobieniu dodatkowego Googlinga znalazłem ten świetny link, który naprawdę dobrze wyjaśnia problem.
arka
Sprawdź ten mały artykuł dla osób, które zaczynają się uczyć: blog.virtualmethodstudio.com/2017/03/memory-alignment-run-fools
darkgaze
3
@ark link uszkodzony
John Jiang
2
@JohnJiang Myślę, że znalazłem nowy link tutaj: developer.ibm.com/technologies/systems/articles/pa-dalign
ejohnso49

Odpowiedzi:

63

Jest to ograniczenie wielu podstawowych procesorów. Zwykle można to obejść, wykonując 4 nieefektywne jednobajtowe pobieranie zamiast jednego wydajnego pobierania słów, ale wielu specyfikatorów języków zdecydowało, że łatwiej byłoby po prostu je zakazać i wymusić wyrównanie wszystkiego.

Ten link zawiera znacznie więcej informacji, które odkrył OP.

Paul Tomblin
źródło
311

Podsystem pamięci w nowoczesnym procesorze jest ograniczony do dostępu do pamięci w stopniu szczegółowości i wyrównania wielkości słowa; dzieje się tak z wielu powodów.

Prędkość

Nowoczesne procesory mają wiele poziomów pamięci podręcznej, przez które dane muszą być pobierane; obsługa odczytów jednobajtowych spowodowałaby, że przepustowość podsystemu pamięci byłaby ściśle związana z przepustowością jednostki wykonawczej (zwanej także procesorem); wszystko to przypomina, w jaki sposób tryb PIO został wyprzedzony przez DMA z wielu tych samych powodów na dyskach twardych.

Procesor zawsze odczytuje rozmiar słowa (4 bajty na 32-bitowym procesorze), więc gdy wykonasz niezrównany dostęp do adresu - na procesorze, który go obsługuje - procesor odczyta wiele słów. Procesor odczyta każde słowo pamięci, które mieści się w żądanym adresie. Powoduje to zwiększenie do dwukrotności liczby transakcji pamięci wymaganych do uzyskania dostępu do żądanych danych.

Z tego powodu bardzo łatwo można odczytać dwa bajty niż cztery. Załóżmy na przykład, że masz strukturę pamięci, która wygląda następująco:

struct mystruct {
    char c;  // one byte
    int i;   // four bytes
    short s; // two bytes
}

W 32-bitowym procesorze najprawdopodobniej byłby wyrównany, jak pokazano tutaj:

Układ konstrukcyjny

Procesor może odczytać każdego z tych członków w jednej transakcji.

Załóżmy, że masz spakowaną wersję struktury, być może z sieci, w której została zapakowana w celu zwiększenia wydajności transmisji; może wyglądać mniej więcej tak:

Upakowana konstrukcja

Czytanie pierwszego bajtu będzie takie samo.

Gdy poprosisz procesor o podanie 16 bitów z 0x0005, będzie musiał odczytać słowo z 0x0004 i przesunąć w lewo o 1 bajt, aby umieścić go w rejestrze 16-bitowym; dodatkowe prace, ale większość może sobie z tym poradzić w jednym cyklu.

Gdy poprosisz o 32 bity od 0x0001, otrzymasz wzmocnienie 2X. Procesor wczyta z 0x0000 do rejestru wyników i przesunie w lewo o 1 bajt, a następnie wczyta ponownie z 0x0004 do rejestru tymczasowego, przesunie w prawo o 3 bajty, a następnie ORz rejestrem wyników.

Zasięg

W przypadku dowolnej przestrzeni adresowej, jeśli architektura może założyć, że 2 LSB mają zawsze wartość 0 (np. Maszyny 32-bitowe), może uzyskać dostęp do 4 razy większej pamięci (2 zapisane bity mogą reprezentować 4 różne stany) lub taką samą ilość pamięci z 2 bitami na coś w rodzaju flag. Usunięcie 2 LSB z adresu dałoby wyrównanie do 4 bajtów; nazywany także krokiem 4 bajtów. Za każdym razem, gdy adres jest zwiększany, efektywnie zwiększa bit 2, a nie bit 0, tzn. Ostatnie 2 bity zawsze będą nadal 00.

Może to nawet wpłynąć na fizyczną konstrukcję systemu. Jeśli magistrala adresowa potrzebuje 2 bitów mniej, procesor może mieć o 2 mniej pinów i o 2 mniej śladów na płytce drukowanej.

Atomowość

CPU może działać atomowo na wyrównanym słowie pamięci, co oznacza, że ​​żadna inna instrukcja nie może przerwać tej operacji. Ma to kluczowe znaczenie dla prawidłowego działania wielu struktur danych bez blokady i innych paradygmatów współbieżności .

Wniosek

System pamięci procesora jest nieco bardziej złożony i zaangażowany niż tutaj opisano; pomocna może być dyskusja na temat tego, w jaki sposób procesor x86 adresuje pamięć (wiele procesorów działa podobnie).

Istnieje wiele innych korzyści związanych z dostosowaniem pamięci, które można przeczytać w tym artykule IBM .

Podstawowym zastosowaniem komputera jest przekształcanie danych. Nowoczesne architektury i technologie pamięci zostały zoptymalizowane na przestrzeni dziesięcioleci, aby ułatwić uzyskiwanie większej ilości danych, wprowadzanie, wyprowadzanie oraz pomiędzy większą liczbą szybszych jednostek wykonawczych - w wysoce niezawodny sposób.

Bonus: Skrytki

Innym wyrównaniem do wydajności, o którym wspominałem wcześniej, jest wyrównanie w liniach pamięci podręcznej, które są (na przykład w niektórych procesorach) 64B.

Aby uzyskać więcej informacji o tym, ile wydajności można uzyskać dzięki wykorzystaniu pamięci podręcznej, zobacz Galerię efektów pamięci podręcznej procesora ; z tego pytania na temat rozmiarów linii pamięci podręcznej

Zrozumienie linii pamięci podręcznej może być ważne dla niektórych rodzajów optymalizacji programu. Na przykład wyrównanie danych może określać, czy operacja dotyka jednej, czy dwóch linii pamięci podręcznej. Jak widzieliśmy w powyższym przykładzie, może to łatwo oznaczać, że w przypadku niewspółosiowości operacja będzie dwa razy wolniejsza.

joshperry
źródło
następujące struktury xyz mają różne rozmiary, ponieważ reguła każdego elementu musi zaczynać się od adresu, który jest wielokrotnością jego rozmiaru, a łańcuch musi być zakończony adresem, który jest wielokrotnością największego rozmiaru elementu struktury. struct x {short s; // 2 bajty i 2 wypełniające tytes int i; // 4 bajty char c; // 1 bajt i 3 bajty wypełniające długi długi l; }; struct y {int i; // 4 bajty char c; // 1 bajt i 1 bajt dopełniający krótki s; // 2 bajty}; struct z {int i; // 4 bajty krótkie s; // 2 bajty char c; // 1 bajt i 1 bajt dopełniający};
Gavin
1
Jeśli dobrze rozumiem, powodem, dla którego komputer nie może odczytać niewyrównanego słowa w jednym kroku, jest to, że w dodatkach używa się 30 bitów, a nie 32 bitów?
GetFree
1
@chux Tak, to prawda, absoluty nigdy się nie utrzymują. 8088 jest ciekawym badaniem kompromisów między prędkością a kosztami, był to w zasadzie 16-bitowy 8086 (który miał pełną 16-bitową zewnętrzną magistralę), ale z tylko połową linii autobusowych, aby zaoszczędzić koszty produkcji. Z tego powodu 8088 potrzebował dwa razy więcej cykli zegara, aby uzyskać dostęp do pamięci niż 8086, ponieważ musiał wykonać dwa odczyty, aby uzyskać pełne 16-bitowe słowo. Co ciekawe, 8086 może wykonać 16-bitowy odczyt z wyrównaniem do słów w jednym cyklu, odczyty bez wyrównania zajmują 2. Fakt, że 8088 miał magistralę z pół słowami maskował to spowolnienie.
joshperry
2
@ joshperry: Niewielka korekta: 8086 może wykonać wyrównany do słów 16-bitowy odczyt w czterech cyklach, podczas gdy niezaangażowane odczyty zajmują osiem . Ze względu na powolny interfejs pamięci czas wykonywania na maszynach opartych na 8088 jest zwykle zdominowany przez pobieranie instrukcji. Instrukcja taka jak „MOV AX, BX” jest nominalnie o jeden cykl szybsza niż „XCHG AX, BX”, ale jeśli nie jest poprzedzona lub nie następuje instrukcja, której wykonanie zajmuje więcej niż cztery cykle na bajt kodu, zajmie to cztery cykle dłużej wykonać. W 8086 pobieranie kodu może czasem nadążyć za wykonaniem, ale w 8088 chyba, że ​​ktoś użyje ...
supercat
1
Bardzo prawda, @martin. Pomagałem tym bajtom dopełniającym, aby skupić się na wewnętrznej dyskusji, ale być może lepiej je uwzględnić.
joshperry
22

możesz z niektórymi procesorami ( nehalem może to zrobić ), ale wcześniej cały dostęp do pamięci był wyrównany na linii 64-bitowej (lub 32-bitowej), ponieważ szyna ma szerokość 64 bitów, trzeba było pobrać 64 bitów na raz , i znacznie łatwiej było pobrać je w wyrównanych „fragmentach” 64 bitów.

Więc jeśli chcesz uzyskać pojedynczy bajt, pobrałeś 64-bitowy fragment, a następnie zamaskowałeś bity, których nie chciałeś. Łatwo i szybko, jeśli twój bajt był na prawym końcu, ale jeśli był w środku tego 64-bitowego fragmentu, będziesz musiał maskować niechciane bity, a następnie przenieść dane we właściwe miejsce. Co gorsza, jeśli potrzebna była zmienna 2-bajtowa, ale została ona podzielona na 2 porcje, wymagało to podwójnego wymaganego dostępu do pamięci.

Ponieważ wszyscy uważają, że pamięć jest tania, po prostu sprawili, że kompilator wyrównał dane do wielkości porcji procesora, dzięki czemu kod działa szybciej i wydajniej kosztem zmarnowanej pamięci.

gbjbaanb
źródło
5

Zasadniczo powodem jest to, że magistrala pamięci ma określoną długość, która jest znacznie, znacznie mniejsza niż rozmiar pamięci.

Tak więc procesor odczytuje z wbudowanej pamięci podręcznej L1, która obecnie często wynosi 32 KB. Ale magistrala pamięci, która łączy pamięć podręczną L1 z procesorem, będzie miała znacznie mniejszą szerokość rozmiaru linii pamięci podręcznej. Będzie to rzędu 128 bitów .

Więc:

262,144 bits - size of memory
    128 bits - size of bus

Niewłaściwe dostępy będą czasami nakładać się na dwie linie pamięci podręcznej, a to będzie wymagało całkowicie nowego odczytu pamięci podręcznej w celu uzyskania danych. Może nawet przeoczyć drogę do DRAM.

Co więcej, część procesora będzie musiała stać na głowie, aby złożyć pojedynczy obiekt z tych dwóch różnych linii pamięci podręcznej, z których każda zawiera fragment danych. W jednej linii będą to bity bardzo wysokiego rzędu, w drugiej bity bardzo niskiego rzędu.

Zostanie dedykowany sprzęt w pełni zintegrowany z potokiem, który obsługuje przenoszenie wyrównanych obiektów na niezbędne bity szyny danych procesora, ale takiego sprzętu może brakować w przypadku niedopasowanych obiektów, ponieważ prawdopodobnie bardziej sensowne jest użycie tych tranzystorów do przyspieszenia poprawnie zoptymalizowanej programy.

W każdym razie drugi odczyt pamięci, który czasami jest konieczny, spowolniłby potok, bez względu na to, ile specjalnego sprzętu (hipotetycznie i głupio) było przeznaczone do łatania źle wyrównanych operacji pamięci.

DigitalRoss
źródło
5

@joshperry udzielił doskonałej odpowiedzi na to pytanie. Oprócz jego odpowiedzi mam kilka liczb, które pokazują graficznie opisane efekty, szczególnie wzmocnienie 2X. Oto link do arkusza kalkulacyjnego Google pokazującego, jak wyglądają efekty różnych dopasowań słów. Ponadto znajduje się link do listy Github z kodem testu. Kod testowy jest dostosowany z artykułu napisanego przez Jonathana Rentzscha, do którego odwołuje się @joshperry. Testy przeprowadzono na Macbooku Pro z czterordzeniowym procesorem Intel Core i7 2,8 GHz 2,8 GHz i 16 GB pamięci RAM.

wprowadź opis zdjęcia tutaj

adino
źródło
4
Co oznaczają xi ywspółrzędne?
shuva,
1
Jakiej generacji rdzeń i7? (Dzięki za opublikowanie linków do kodu!)
Nick Desaulniers
2

Jeśli system z pamięcią adresowalną bajtowo ma szynę pamięci o szerokości 32 bitów, oznacza to, że istnieją efektywnie cztery bajtowe systemy pamięci, z których wszystkie są podłączone do odczytu lub zapisu tego samego adresu. Wyrównany 32-bitowy odczyt będzie wymagał informacji przechowywanych pod tym samym adresem we wszystkich czterech systemach pamięci, więc wszystkie systemy mogą dostarczać dane jednocześnie. 32-bitowy odczyt bez wyrównania wymagałby, aby niektóre systemy pamięci zwracały dane z jednego adresu, a niektóre zwracały dane z następnego wyższego adresu. Chociaż istnieją pewne systemy pamięci zoptymalizowane pod kątem spełnienia takich żądań (oprócz adresu, mają one skutecznie sygnał „plus jeden”, co powoduje, że używają adresu o jeden wyższy niż określony), jednak taka funkcja powoduje znaczne koszty i złożoność systemu pamięci;

supercat
źródło
2

Jeśli masz szynę danych 32-bitowy adres autobusowe linie adresowe podłączone do pamięci rozpocznie się od A 2 , więc tylko 32bit wyrównane adresy można uzyskać w jednym cyklu magistrali.

Więc jeśli rozpiętości słowo adres wyrównanie brzegowe - czyli 0 do 16/32 danych bitowych lub 1 dla 32-bitowych danych nie są zerowe, dwa cykle magistrali są wymagane w celu uzyskania danych.

Niektóre zestawy architektur / instrukcji nie obsługują niezaangażowanego dostępu i generują wyjątek przy takich próbach, więc wygenerowany przez kompilator niepasowany kod dostępu wymaga nie tylko dodatkowych cykli magistrali, ale dodatkowych instrukcji, co czyni go jeszcze mniej wydajnym.

Clifford
źródło
0

Na PowerPC możesz bez problemu załadować liczbę całkowitą z nieparzystego adresu.

Sparc i I86 oraz (tak myślę) Itatnium podnoszą wyjątki sprzętowe, gdy spróbujesz tego.

Jedno obciążenie 32-bitowe w porównaniu do czterech obciążeń 8-bitowych nie zrobi dużej różnicy w większości nowoczesnych procesorów. To, czy dane są już w pamięci podręcznej, czy nie, będzie miało znacznie większy efekt.

James Anderson
źródło
Na Sparcu był to „błąd autobusu”, stąd rozdział „Błąd autobusu, weź pociąg” w „Expert C Programming: Deep C Secrets” Petera Van der Lindena
jjg