Dlaczego procesor ma 32 rejestry?

52

Zawsze zastanawiałem się, dlaczego procesory zatrzymały się przy 32 rejestrach. To zdecydowanie najszybszy element maszyny, dlaczego nie stworzyć większych procesorów z większą liczbą rejestrów? Czy nie oznaczałoby to mniejszego korzystania z pamięci RAM?

Matt Capone
źródło
2
Sądzę, że poza pewnym punktem wszystkie zmienne lokalne mieszczą się w rejestrach. Rzeczywiste dane, z którymi pracujesz, są prawdopodobnie zbyt duże
Niklas B.,
14
Malejące zwroty. Najwyraźniej rejestry są „droższe” (pod różnymi względami) niż pamięć RAM, albo mielibyśmy tylko 8 GB rejestrów.
David Richerby
5
Jednym z powodów, dla których jest tak szybki, jest to, że nie ma ich wiele.
stackErr
5
Istnieje różnica między liczbą rejestrów w jednostce centralnej a liczbą rejestrów, których można użyć jednocześnie.
Thorbjørn Ravn Andersen
Procesory i procesory graficzne ukrywają opóźnienia głównie przez pamięci podręczne i wielowątkowość. Tak więc procesory mają niewiele rejestrów, podczas gdy procesory graficzne mają dziesiątki tysięcy rejestrów. Zobacz mój artykuł ankietowy na temat pliku rejestru GPU, który omawia wszystkie te kompromisy i czynniki.
user984260

Odpowiedzi:

82

Po pierwsze, nie wszystkie architektury procesorów zatrzymały się przy 32 rejestrach. Prawie wszystkie architektury RISC, które mają 32 rejestry ujawnione w zestawie instrukcji, faktycznie mają 32 rejestry liczb całkowitych i 32 więcej rejestrów zmiennoprzecinkowych (czyli 64). (Zmienny punkt „dodaj” używa innych rejestrów niż liczba całkowita „dodaj”.) Architektura SPARC ma okna rejestrów. Na SPARC można uzyskać dostęp tylko do 32 rejestrów całkowitych naraz, ale rejestry działają jak stos i można przesuwać i wyskakiwać nowe rejestry 16 jednocześnie. Architektura Itanium HP / Intel miała 128 liczb całkowitych i 128 rejestrów zmiennoprzecinkowych ujawnionych w zestawie instrukcji. Nowoczesne procesory graficzne NVidia, AMD, Intel, ARM i Imagination Technologies ujawniają ogromną liczbę rejestrów w swoich plikach rejestrów. (Wiem, że tak jest w przypadku architektur NVidia i Intel, nie znam się dobrze na zestawach instrukcji AMD, ARM i Imagination, ale myślę, że pliki rejestrów też są tam duże).

Po drugie, większość współczesnych mikroprocesorów implementuje zmianę nazw rejestrów, aby wyeliminować niepotrzebną serializację spowodowaną koniecznością ponownego wykorzystania zasobów, więc podstawowe pliki rejestrów fizycznych mogą być większe (96, 128 lub 192 rejestrów na niektórych komputerach). To (i dynamiczne planowanie) eliminuje niektóre z potrzeba, aby kompilator wygenerował tak wiele unikalnych nazw rejestrów, jednocześnie zapewniając większy harmonogram dla harmonogramu.

Istnieją dwa powody, dla których może być trudno zwiększyć liczbę rejestrów ujawnionych w zestawie instrukcji. Po pierwsze, musisz być w stanie określić identyfikatory rejestru w każdej instrukcji. 32 rejestry wymagają 5-bitowego specyfikatora rejestru, więc instrukcje 3-adresowe (wspólne w architekturach RISC) wydają 15 z 32 bitów instrukcji tylko na określenie rejestrów. Jeśli zwiększysz to do 6 lub 7 bitów, będziesz miał mniej miejsca na określenie kodów i stałych. Procesory graficzne i Itanium mają znacznie większe instrukcje. Większe instrukcje mają swoją cenę: musisz użyć więcej pamięci instrukcji, więc zachowanie pamięci podręcznej instrukcji jest mniej idealne.

Drugim powodem jest czas dostępu. Im większa jest pamięć, tym wolniejszy jest dostęp do danych z niej. (Po prostu z podstaw fizyki: dane są przechowywane w przestrzeni dwuwymiarowej, więc jeśli przechowujesz bitów, średnia odległość do określonego bitu wynosi .) Plik rejestru jest tylko mała pamięć wieloportowa, a jednym z ograniczeń zwiększania jej jest to, że w końcu trzeba będzie zacząć wolniej taktować maszynę, aby pomieścić większy plik rejestru. Zwykle pod względem całkowitej wydajności jest to strata. nO(n)

Wędrująca logika
źródło
1
Wspomniałbym o 256 FPR SPARC64 VIIIfx i 32 dodatkowych GPR niepartych w oknie, osiągniętych przez dodanie instrukcji Set XAR, która zapewnia 13 bitów dla następnej jednej lub dwóch instrukcji. Był ukierunkowany na HPC, więc liczba rejestrów jest bardziej zrozumiała. Kusiłbym również, by wyjaśnić niektóre kompromisy i techniki związane z większą liczbą rejestrów; ale wykazałeś się mądrością, aby uniknąć bardziej wyczerpującej (a nawet nie wyczerpującej) odpowiedzi.
Paul A. Clayton
2
Warto dodać trochę malejącej korzyści z większej liczby rejestrów dla kodu „ogólnego przeznaczenia”, chociaż znalezienie znaczących pomiarów nie jest łatwe. Myślę, że Mitch Alsup wspomniał na comp.arch, że rozszerzenie x86 do 32 rejestrów zamiast 16 zyskałoby około 3% wydajności w porównaniu z (ISTR) 10-15% dla wybranego rozszerzenia rejestru 8 do 16. Nawet w przypadku ISA przechowującego dane przechodzenie do 64 prawdopodobnie nie przyniesie korzyści (przynajmniej w przypadku obecnego kodu GP). (Przy okazji, GPU często rejestry udziały na nici: na przykład, jeden z gwintem 250 zostawiając 16 Wszystkie inne prywatne nici).
Paul A. Clayton
Ciekawe, że zarządzanie środowiskiem (stąd konwersja alfa), często kojarzone z językami wysokiego poziomu, jest faktycznie używane na poziomie rejestru.
babou
@ PaulA.Clayton Zawsze myślałem, że IA-64 to architektura, która ma największą liczbę rejestrów ISA
phuclv
@ LưuVĩnhPhúc SPARC64 VIIIfx był specyficzny dla HPC. Do twojej wiadomości, Am29k (wprowadzony około 1987-8 ) miał 64 globalne i 128 okienkowe GPR, co oznacza więcej GPR niż Itanium (który ma 8 rejestrów gałęzi i rejestr zliczania pętli, którego funkcja byłaby w GPR w niektórych innych ISA).
Paul A. Clayton
16

Jeszcze tylko dwa powody ograniczenia liczby rejestrów:

  • Niewielkiego zysku można się spodziewać: procesor, taki jak obecne modele Intel / AMD x64, ma 32 kB i więcej pamięci podręcznej L1-D, a dostęp do pamięci podręcznej L1 zwykle zajmuje tylko jeden cykl zegara (w porównaniu do około stu cykli zegara dla pełnej pojedynczej pamięci RAM dostęp). Tak więc niewiele można zyskać z posiadania większej ilości danych w rejestrach w porównaniu z posiadaniem danych w pamięci podręcznej L1
  • Dodatkowe koszty obliczeniowe: posiadanie większej liczby rejestrów powoduje narzut, który może spowolnić komputer:
    • W środowiskach wielozadaniowych przełącznik zadań zwykle musi zapisywać zawartość wszystkich rejestrów procesu pozostawionych w pamięci i musi załadować te z procesu, który ma zostać wprowadzony. Im więcej masz rejestrów, tym dłużej to trwa.
    • Podobnie w architekturach bez okien rejestrów kaskadowe wywołania funkcji używają tego samego zestawu rejestrów. Tak więc funkcja A wywołująca funkcję B używa tego samego zestawu rejestrów co sama B. Dlatego B musi zapisać zawartość wszystkich używanych rejestrów (które nadal przechowują wartości A) i musi je zapisać przed powrotem (w niektórych konwencjach wywoływania zadaniem A jest zapisanie zawartości rejestru przed wywołaniem B, ale koszty ogólne są podobne). Im więcej rejestrów masz, tym dłużej trwa to oszczędzanie, a tym samym droższe staje się wywołanie funkcji.
Robert Buchholz
źródło
Jak działa pamięć podręczna L1, abyśmy nie mieli tego samego problemu co w przypadku rejestrów?
babou
4
Na procesorach o wysokiej wydajności opóźnienie L1 Dcache wynosi zazwyczaj 3 lub 4 cykle (w tym generowanie adresu), np. Haswell Intela ma opóźnienie 4 cykli (brak opóźnienia rejestru rejestru zależności danych jest łatwiejszy do ukrycia w potoku). Dcache ma również tendencję do obsługi mniejszej liczby dostępów na cykl (np. 2 odczyt, 1 zapis dla Haswell) niż plik rejestru (np. 4 odczyt, 6 zapis dla Alpha 21264, który zreplikował plik, 2 pliki z 4 odczytami są szybsze niż 1 z 8).
Paul A. Clayton
@ PaulA.Clayton: Jeśli pamięć podręczna L1 ma opóźnienie 3-4 cyklów, sugerowałoby to, że może być pewna korzyść z posiadania np. Kilku zestawów 64 słów pamięci jednocyklowej z własną 64-wyrazową przestrzenią adresową i dedykowane instrukcje „ładuj / przechowuj bezpośrednio”, zwłaszcza jeśli istniał sposób na wypchnięcie wszystkich niezerowych wartości, a następnie słowo z informacją, które słowa były niezerowe, a następnie sposób na ich cofnięcie (zerowanie rejestrów, które nie zostały wyskakujące) . Wiele metod zawiera od 16 do 60 słów zmiennych lokalnych, więc skrócenie czasu dostępu dla tych z 3-4 cykli do jednego wydaje się pomocne.
supercat
@ superuper Różne pomysły na pamięć podręczną na stos (i globalny / TLS [np. Knapsack]) zostały zaprezentowane w artykułach naukowych, a także mechanizmy takie jak bufor podpisów ( PDF ) Rzeczywiste wykorzystanie, nie tyle (wydaje się). Robi się rozmownie (więc prawdopodobnie powinno się skończyć lub iść gdzie indziej).
Paul A. Clayton,
4

Dużo kodu ma wiele dostępów do pamięci (30% to typowa liczba). Poza tym zwykle około 2/3 dostępu to odczyt, a 1/3 dostępu to dostęp do zapisu. Nie dzieje się tak z powodu wyczerpania rejestrów, ale dostępu do tablic, dostępu do zmiennych elementów obiektu itp.

Trzeba to zrobić w pamięci (lub w pamięci podręcznej danych) ze względu na sposób tworzenia C / C ++ (wszystko, co można uzyskać, musi mieć adres, który musi być potencjalnie przechowywany w pamięci). Jeśli kompilator zgadnie, że nie będziesz chciał pisać do zmiennych za pomocą szalonych sztuczek pośrednich, umieści je w rejestrach, i działa to świetnie w przypadku zmiennych funkcyjnych, ale nie w przypadku globalnie dostępnych (ogólnie wszystko, co pochodzi z malloc ()), ponieważ nie można zgadnąć, jak zmieni się stan globalny.

Z tego powodu nie jest powszechne, że kompilator będzie w stanie zrobić wszystko z ponad 16 rejestrami ogólnego zastosowania. Dlatego wszyscy popularni architekci mają ich tak wiele (ARM ma 16).

MIPS i inne RISC mają zwykle 32, ponieważ nie jest tak trudno mieć tyle rejestrów - koszt jest wystarczająco niski, więc jest to trochę „dlaczego nie?”. Ponad 32 jest w większości bezużyteczne i ma wadę polegającą na tym, że dostęp do pliku rejestru jest dłuższy (każdy podwojenie liczby rejestrów potencjalnie dodaje dodatkową warstwę multiplekserów, co dodaje nieco więcej opóźnienia ...). Średnio wydłuża również instrukcje - co oznacza, że ​​podczas uruchamiania programów zależnych od przepustowości pamięci instrukcji dodatkowe rejestry spowalniają cię!

Jeśli twój procesor jest w porządku i nie zmienia nazwy rejestru i próbujesz wykonać wiele operacji na cykl (więcej niż 3), to teoretycznie potrzebujesz więcej rejestrów, gdy liczba operacji na cykl rośnie. Właśnie dlatego Itanium ma tak wiele rejestrów! Ale w praktyce, oprócz kodu zmiennoprzecinkowego lub kodu zorientowanego na SIMD (w którym Itanium był naprawdę dobry), większość kodów będzie miała wiele odczytów / zapisów i skoków pamięci, co uniemożliwi realizację tego marzenia o ponad 3 operacjach na cykl (szczególnie w oprogramowaniu serwerowym, takim jak bazy danych, kompilatory, wykonywanie języka wysokiego poziomu, takie jak javascript, emulacja itp.). Właśnie to zatopiło Itanium.

Wszystko sprowadza się do różnicy między obliczeniami a wykonywaniem!

Hubert Lamontagne
źródło
2

Kto ci mówi, że procesor ma zawsze 32 rejestry? x86 ma 8, ARM 32-bit i x86_64 ma 16, IA-64 ma 128 i wiele innych liczb. Możesz zajrzeć tutaj . Nawet MIPS, PPC lub dowolna architektura, która ma 32 rejestry ogólnego przeznaczenia w zestawie instrukcji, liczba ta jest znacznie większa niż 32, ponieważ zawsze istnieją rejestry flag (jeśli istnieją), rejestry kontrolne ... bez rejestrów o zmienionej nazwie i rejestrów sprzętowych

Wszystko ma swoją cenę. Im większa liczba rejestrów, tym więcej pracy wykonujesz podczas przełączania zadań, tym więcej miejsca potrzebujesz na kodowanie instrukcji. Jeśli masz mniej rejestrów, nie musisz dużo zapisywać i przywracać podczas wywoływania i powracania z funkcji lub przełączania zadań z kompromisem braku rejestrów w niektórych kodach obszernych obliczeniowo

Co więcej, im większy plik rejestru, tym będzie on droższy i bardziej złożony. SRAM to najszybsza i najdroższa pamięć RAM, więc jest używana tylko w pamięci podręcznej procesora. Ale wciąż jest znacznie tańszy i zajmuje mniej miejsca niż plik rejestru o tej samej pojemności.

phuclv
źródło
2

Na przykład typowy procesor Intel ma „oficjalnie” 16 rejestrów liczb całkowitych i 16 wektorów. Ale w rzeczywistości jest o wiele więcej: procesor używa „zmiany nazwy rejestru”. Jeśli masz instrukcję reg3 = reg1 + reg2, miałbyś problem, gdyby inna instrukcja korzystająca z reg3 jeszcze się nie zakończyła - nie możesz wykonać nowej instrukcji, jeśli zastąpi ona reg3, zanim zostanie odczytana przez poprzednią instrukcję.

Dlatego istnieje około 160 rzeczywistych rejestrów. Tak więc powyższa prosta instrukcja została zmieniona na „regX = reg1 + reg2 i pamiętaj, że regX zawiera reg3”. Bez rejestrów zmian nazw, wykonywanie poza kolejnością byłoby absolutnie martwe w wodzie.

gnasher729
źródło
1

Nie jestem inżynierem elektrykiem, ale myślę, że inną przyczyną z powodu ograniczenia liczby rejestrów jest routing. Liczba jednostek arytmetycznych jest ograniczona i muszą one być w stanie pobierać dane z każdego rejestru i wyprowadzać dane do każdego rejestru. Jest to szczególnie prawdziwe, gdy masz programy potokowe, które mogą wykonywać wiele instrukcji na cykl.

Prosta wersja tego miałaby złożoność, uniemożliwiając skalowanie rejestrów, lub w inny sposób wymagałaby przeprojektowania routingu do czegoś o wiele bardziej skomplikowanego, aby trasować wszystko z większą złożonością.O(n2)

Pomysł na tę odpowiedź przyszedł mi po obejrzeniu niektórych wypowiedzi Iwana Godarda na temat procesora Mill. Częścią innowacji Mill CPU jest to, że nie można wyprowadzać danych do dowolnych rejestrów - wszystkie wyjścia są wypychane na stos rejestru lub „pas”, co zmniejsza problemy z routingiem, ponieważ zawsze wiadomo, dokąd pójdzie wyjście. Zauważ, że nadal mają problem z routingiem w celu uzyskania rejestrów wejściowych do jednostek arytmetycznych.

Zobacz The Mill CPU Architecture - the Belt (2 of 9), aby znaleźć opis problemu i rozwiązanie Mill.

Realz Slaw
źródło
„Muszą być w stanie pobierać dane z każdego rejestru i wysyłać dane do każdego rejestru”. - Spodziewam się, że jest to zwykle realizowane za pomocą magistrali, nie musi być osobnego połączenia z ALU dla każdego rejestru.
user253751,
1
@immibis: Jeśli chcesz przenosić dane w 300 pikosekund, autobus tego nie zrobi. A jeśli chcesz przenosić wiele danych (na przykład wykonać trzy instrukcje z dwoma operandami i po jednym wyniku w tym samym cyklu), magistrala absolutnie, absolutnie nie będzie działać.
gnasher729
0

Jeśli chodzi o MIPS ISA, Hennessy and Patterson, Computer Organisation and Design 4th edition str. 176, odpowiada bezpośrednio na to konkretne pytanie:

Mniejszy jest szybszy. Dążenie do szybkości powoduje, że MIPS ma 32 rejestry, a nie wiele więcej.

Olsonista
źródło