W wersji 32-bitowej mieliśmy 8 rejestrów „ogólnego przeznaczenia”. W przypadku wersji 64-bitowej kwota podwaja się, ale wydaje się niezależna od samej zmiany 64-bitowej.
Skoro rejestry są tak szybkie (brak dostępu do pamięci), dlaczego nie ma ich więcej naturalnie? Czy konstruktorzy procesorów nie powinni umieszczać w procesorze jak największej liczby rejestrów? Jakie jest logiczne ograniczenie, dlaczego mamy tylko tyle, ile mamy?
88
Odpowiedzi:
Jest wiele powodów, dla których nie masz tylko dużej liczby rejestrów:
Obecnie naprawdę mamy dużo rejestrów - po prostu nie są one jawnie zaprogramowane. Mamy "zmianę nazwy rejestru". Chociaż masz dostęp tylko do małego zestawu (8-32 rejestrów), w rzeczywistości są one obsługiwane przez znacznie większy zestaw (np. 64-256). Następnie CPU śledzi widoczność każdego rejestru i przydziela je do zestawu o zmienionej nazwie. Na przykład, możesz ładować, modyfikować, a następnie przechowywać w rejestrze wiele razy z rzędu i mieć każdą z tych operacji faktycznie wykonywanych niezależnie w zależności od błędów pamięci podręcznej itp. W ARM:
Rdzenie Cortex A9 zmieniają nazwy rejestrów, więc pierwsze ładowanie do "r0" trafia do wirtualnego rejestru o zmienionej nazwie - nazwijmy go "v0". Ładowanie, zwiększanie i zapisywanie odbywa się na „v0”. W międzyczasie ponownie wykonujemy ładowanie / modyfikowanie / zapisywanie do r0, ale nazwa zostanie zmieniona na „v1”, ponieważ jest to całkowicie niezależna sekwencja wykorzystująca r0. Powiedzmy, że ładowanie ze wskaźnika w "r4" utknęło z powodu braku pamięci podręcznej. W porządku - nie musimy czekać, aż "r0" będzie gotowe. Ponieważ ma zmienioną nazwę, możemy uruchomić następną sekwencję z "v1" (również odwzorowanym na r0) - i być może jest to trafienie w pamięć podręczną i właśnie odnieśliśmy ogromną wygraną w wydajności.
Myślę, że x86 ma obecnie do gigantycznej liczby zmienionych nazw rejestrów (ballpark 256). Oznaczałoby to posiadanie 8 bitów razy 2 dla każdej instrukcji tylko po to, aby powiedzieć, jakie jest źródło i cel. Zwiększyłoby to znacznie liczbę przewodów potrzebnych w rdzeniu i jego rozmiar. Tak więc istnieje dobre miejsce w okolicach 16-32 rejestrów, na które zdecydowała się większość projektantów, aw przypadku niedziałających projektów procesorów zmiana nazwy rejestrów jest sposobem na złagodzenie tego problemu.
Edycja : znaczenie wykonywania poza kolejnością i zmiany nazwy rejestru w tym. Gdy masz OOO, liczba rejestrów nie ma tak dużego znaczenia, ponieważ są to tylko „tymczasowe znaczniki”, których nazwa zostaje zmieniona na znacznie większy zestaw rejestrów wirtualnych. Nie chcesz, aby liczba była zbyt mała, ponieważ pisanie małych sekwencji kodu jest trudne. Jest to problem dla x86-32, ponieważ ograniczone 8 rejestrów oznacza, że wiele danych tymczasowych przechodzi przez stos, a rdzeń potrzebuje dodatkowej logiki, aby przekazywać odczyty / zapisy do pamięci. Jeśli nie masz OOO, zwykle mówisz o małym rdzeniu, w którym to przypadku duży zestaw rejestrów to słaba korzyść koszt / wydajność.
Jest więc naturalny punkt optymalny dla rozmiaru banku rejestrów, który wynosi maksymalnie około 32 zaprojektowanych rejestrów dla większości klas procesorów. x86-32 ma 8 rejestrów i jest zdecydowanie za mały. ARM poszedł z 16 rejestrami i to dobry kompromis. 32 rejestry to trochę za dużo, jeśli w ogóle - w końcu nie potrzebujesz ostatnich 10 lub więcej.
Nic z tego nie dotyczy dodatkowych rejestrów, które otrzymujesz dla SSE i innych koprocesorów zmiennoprzecinkowych wektorów. Mają sens jako dodatkowy zestaw, ponieważ działają niezależnie od rdzenia typu integer i nie zwiększają wykładniczo złożoności procesora.
źródło
Mamy Czy ich więcej
Ponieważ prawie każda instrukcja musi wybierać 1, 2 lub 3 architektonicznie widoczne rejestry, zwiększenie ich liczby zwiększyłoby rozmiar kodu o kilka bitów w każdej instrukcji, a tym samym zmniejszyłoby gęstość kodu. Zwiększa również ilość kontekstu, który należy zapisać jako stan wątku i częściowo zapisać w rekordzie aktywacji funkcji . Operacje te występują często. Blokady rurociągów muszą sprawdzać tablicę wyników dla każdego rejestru, a to ma kwadratową złożoność czasową i przestrzenną. I chyba największym powodem jest po prostu zgodność z już zdefiniowanym zestawem instrukcji.
Ale okazuje się, dzięki przemianowanie rejestrów , tak naprawdę nie mają wiele dostępnych rejestrów, a my nawet nie trzeba, aby je zapisać. Procesor w rzeczywistości ma wiele zestawów rejestrów i automatycznie przełącza się między nimi w miarę wykonywania kodu. Robi to wyłącznie po to, aby uzyskać więcej rejestrów.
Przykład:
W architekturze, która ma tylko r0-r7, następujący kod może zostać automatycznie przepisany przez procesor jako coś takiego:
W tym przypadku r10 jest ukrytym rejestrem, który tymczasowo zastępuje r1. CPU może stwierdzić, że wartość r1 nigdy nie jest używana ponownie po pierwszym zapisie. Pozwala to na opóźnienie pierwszego obciążenia (nawet trafienie w pamięć podręczną na chipie zajmuje zwykle kilka cykli) bez konieczności opóźnienia drugiego obciążenia lub drugiego magazynu.
źródło
Dodają rejestry przez cały czas, ale często są powiązane z instrukcjami specjalnego przeznaczenia (np. SIMD, SSE2 itp.) Lub wymagają kompilacji do określonej architektury procesora, co zmniejsza przenośność. Istniejące instrukcje często działają na określonych rejestrach i nie mogą korzystać z innych rejestrów, gdyby były dostępne. Starszy zestaw instrukcji i wszystko.
źródło
Aby dodać tutaj trochę interesujących informacji, zauważysz, że posiadanie 8 rejestrów tej samej wielkości pozwala kodom operacyjnym zachować spójność z notacją szesnastkową. Na przykład instrukcja
push ax
to opcode 0x50 na x86 i dochodzi do 0x57 dla ostatniego rejestru di. Następnie instrukcjapop ax
zaczyna się od 0x58 i przechodzi w górę do 0x5F,pop di
aby ukończyć pierwszą podstawę-16. Spójność szesnastkowa jest utrzymywana przy 8 rejestrach na rozmiar.źródło