Ogólnie rzecz biorąc, wszystkie procesory, mikroprocesory jednoukładowe lub wieloukładowe, uruchamiają programy, wykonując następujące czynności:
- Przeczytaj instrukcję i rozszyfruj ją.
- Znajdź wszelkie powiązane dane, które są potrzebne do przetworzenia instrukcji.
- Wykonać polecenie.
- Wypisz wyniki.
Komplikacją tej prosto wyglądającej serii kroków jest fakt, że hierarchia pamięci, która obejmuje buforowanie, pamięć główną i nieulotną pamięć masową, taką jak dyski twarde, (gdzie znajdują się instrukcje programu i dane) zawsze była wolniejsza niż sam procesor. Krok (2) często wprowadza opóźnienie (w terminologii CPU często nazywane "przeciągnięciem"), podczas gdy dane docierają przez magistralę komputera. Wiele badań poświęcono konstrukcjom, które w jak największym stopniu unikają tych opóźnień. Przez lata głównym celem projektantów było równoległe wykonywanie większej liczby instrukcji, zwiększając w ten sposób efektywną szybkość wykonywania programu. Te wysiłki wprowadziły skomplikowane struktury logiczne i układowe. W przeszłości takie techniki mogły być implementowane tylko na drogich komputerach typu mainframe lub superkomputerach ze względu na ilość potrzebnych do tego obwodów. Wraz z postępem w produkcji półprzewodników, coraz więcej z tych technik można było zaimplementować na pojedynczym chipie półprzewodnikowym.
Poniżej znajduje się przegląd technik mikroarchitektonicznych, które są powszechnie stosowane w nowoczesnych procesorach.
Wybór zestawu instrukcji
Wybór architektury zestawu instrukcji w znacznym stopniu wpływa na złożoność implementacji urządzeń o wysokiej wydajności. Przez lata projektanci komputerów robili wszystko, aby uprościć zestawy instrukcji, aby umożliwić implementacje o wyższej wydajności, oszczędzając wysiłek i czas projektantów na funkcje poprawiające wydajność, zamiast marnować go na złożoność zestawu instrukcji.
Projektowanie zestawów instrukcji rozwinęło się od typów CISC, RISC, VLIW, EPIC. Architektury, które zajmują się paralelizmem danych to SIMD i Vectors.
Łączenie instrukcji w potoki
Jedną z pierwszych i najpotężniejszych technik zwiększania wydajności jest wykorzystanie potoku instrukcji. Wczesne konstrukcje procesorów wykonywały wszystkie powyższe kroki na jednej instrukcji przed przejściem do następnej. Duża część obwodów procesora pozostawała bezczynna na każdym etapie; na przykład, obwody dekodujące instrukcje były bezczynne podczas wykonywania instrukcji i tak dalej.
Linie potokowe zwiększają wydajność, pozwalając wielu instrukcjom pracować w procesorze w tym samym czasie. W tym samym podstawowym przykładzie procesor zacząłby dekodować (krok 1) nową instrukcję, podczas gdy poprzednia czekała na wyniki. Dzięki temu w jednym czasie mogą być "w locie" nawet cztery instrukcje, co sprawia, że procesor działa cztery razy szybciej. Chociaż wykonanie każdej instrukcji trwa tak samo długo (nadal są cztery kroki), procesor jako całość "wycofuje" instrukcje znacznie szybciej i może pracować z dużo wyższą prędkością zegara.
Cache
Udoskonalenia w produkcji układów scalonych pozwoliły na umieszczenie większej ilości obwodów na jednym chipie, a projektanci zaczęli szukać sposobów na ich wykorzystanie. Jednym z najczęstszych sposobów było dodawanie coraz większej ilości pamięci cache na chipie. Pamięć podręczna jest bardzo szybką pamięcią, do której dostęp można uzyskać w ciągu kilku cykli w porównaniu do tego, co jest potrzebne do rozmowy z pamięcią główną. Procesor zawiera kontroler pamięci podręcznej, który automatyzuje odczyt i zapis z pamięci podręcznej, jeśli dane są już w pamięci podręcznej, to po prostu "pojawia się", podczas gdy jeśli nie jest, procesor jest "zatrzymany", podczas gdy kontroler pamięci podręcznej czyta go w.
W konstrukcjach RISC zaczęto dodawać pamięć podręczną w połowie lub pod koniec lat 80-tych, często o łącznej wielkości zaledwie 4 KB. Liczba ta rosła z czasem i obecnie typowe procesory mają około 512 KB, podczas gdy bardziej wydajne procesory mają 1 lub 2, a nawet 4, 6, 8 lub 12 MB, zorganizowane na wielu poziomach hierarchii pamięci. Ogólnie rzecz biorąc, więcej pamięci podręcznej oznacza większą szybkość.
Cache i potoki idealnie do siebie pasowały. Wcześniej nie było sensu budować potoku, który mógłby działać szybciej niż opóźnienie dostępu do pamięci podręcznej poza chipem. Użycie zamiast tego pamięci cache on-chip oznaczało, że potok mógł działać z prędkością opóźnienia dostępu do pamięci cache, czyli przez znacznie mniejszy okres czasu. Pozwoliło to na zwiększenie częstotliwości pracy procesorów w znacznie szybszym tempie niż w przypadku pamięci off-chip.
Przewidywanie rozgałęzień i wykonywanie spekulacyjne
Spiętrzenia w potoku i przepłukiwanie spowodowane rozgałęzieniami to dwie główne rzeczy uniemożliwiające osiągnięcie wyższej wydajności poprzez równoległość na poziomie instrukcji. Od czasu, gdy dekoder instrukcji procesora stwierdzi, że napotkał instrukcję warunkowego rozgałęzienia do czasu, gdy decydująca wartość rejestru skoków może zostać odczytana, potok może utknąć w martwym punkcie na kilka cykli. Średnio, co piąta wykonywana instrukcja jest rozgałęzieniem, więc jest to duża ilość przestojów. Jeśli rozgałęzienie zostanie wykonane, jest jeszcze gorzej, ponieważ wtedy wszystkie kolejne instrukcje, które były w potoku muszą zostać przepłukane.
Techniki takie jak przewidywanie rozgałęzień i wykonywanie spekulatywne są używane do redukcji tych kar za rozgałęzienia. Przewidywanie rozgałęzień polega na tym, że sprzęt zgaduje, czy dane rozgałęzienie zostanie wykonane. Domysły te pozwalają sprzętowi na prefetchowanie instrukcji bez czekania na odczyt rejestru. Wykonywanie spekulatywne jest kolejnym ulepszeniem, w którym kod wzdłuż przewidywanej ścieżki jest wykonywany zanim wiadomo, czy rozgałęzienie powinno zostać wykonane czy nie.
Wykonanie poza kolejnością
Dodanie pamięci podręcznej zmniejsza częstotliwość lub czas trwania przestojów spowodowanych oczekiwaniem na dane, które mają być pobrane z głównej hierarchii pamięci, ale nie eliminuje całkowicie tych przestojów. We wczesnych projektach pominięcie pamięci podręcznej zmuszało kontroler pamięci podręcznej do zatrzymania procesora i czekania. Oczywiście w programie może być jakaś inna instrukcja, której dane są dostępne w pamięci podręcznej w tym momencie. Wykonywanie poza kolejnością pozwala tej gotowej instrukcji być przetwarzaną, podczas gdy starsza instrukcja czeka w pamięci podręcznej, a następnie zmienia kolejność wyników, aby sprawić wrażenie, że wszystko wydarzyło się w zaprogramowanej kolejności.
Superskalar
Nawet przy całej tej dodatkowej złożoności i bramkach potrzebnych do obsługi opisanych powyżej koncepcji, usprawnienia w produkcji półprzewodników wkrótce pozwoliły na zastosowanie jeszcze większej liczby bramek logicznych.
W powyższym schemacie procesor przetwarza części pojedynczej instrukcji na raz. Programy komputerowe mogłyby być wykonywane szybciej, gdyby wiele instrukcji było przetwarzanych jednocześnie. To właśnie osiągają procesory superskalarne, poprzez replikację jednostek funkcjonalnych, takich jak ALU. Replikacja jednostek funkcjonalnych stała się możliwa dopiero wtedy, gdy powierzchnia układu scalonego (zwanego czasem "matrycą") procesora jednoprocesorowego przestała przekraczać granice tego, co można było wyprodukować w sposób niezawodny. Pod koniec lat 80. na rynku zaczęły pojawiać się konstrukcje superskalarne.
W nowoczesnych projektach często spotyka się dwie jednostki ładujące, jedną przechowującą (wiele instrukcji nie ma wyników do przechowywania), dwie lub więcej jednostek obliczeniowych dla liczb całkowitych, dwie lub więcej jednostek zmiennoprzecinkowych i często jednostkę SIMD jakiegoś rodzaju. Logika wydawania instrukcji staje się coraz bardziej złożona poprzez wczytywanie ogromnej listy instrukcji z pamięci i przekazywanie ich do różnych jednostek wykonawczych, które są w tym momencie bezczynne. Wyniki są następnie zbierane i ponownie porządkowane na końcu.
Zmiana nazwy rejestru
Zmiana nazwy rejestru odnosi się do techniki używanej w celu uniknięcia niepotrzebnego szeregowego wykonywania instrukcji programu z powodu ponownego użycia tych samych rejestrów przez te instrukcje. Załóżmy, że mamy dwie grupy instrukcji, które będą używać tego samego rejestru, jeden zestaw instrukcji jest wykonywany jako pierwszy, aby pozostawić rejestr drugiemu zestawowi, ale jeśli drugi zestaw jest przypisany do innego podobnego rejestru, oba zestawy instrukcji mogą być wykonywane równolegle.
Wieloprocesowość i wielowątkowość
Ze względu na rosnącą różnicę pomiędzy częstotliwością pracy procesora a czasem dostępu do pamięci DRAM, żadna z technik zwiększających równoległość na poziomie instrukcji (ILP) w ramach jednego programu nie była w stanie przezwyciężyć długich przestojów (opóźnień), które występowały, gdy dane musiały być pobierane z pamięci głównej. Dodatkowo, duża liczba tranzystorów i wysokie częstotliwości pracy wymagane przez bardziej zaawansowane techniki ILP wymagały rozpraszania energii na poziomie, którego nie dało się już tanio schłodzić. Z tych powodów, nowsze generacje komputerów zaczęły wykorzystywać wyższe poziomy równoległości, które istnieją poza pojedynczym programem lub wątkiem programu.
Ten trend jest czasami znany jako "throughput computing". Idea ta wywodzi się z rynku mainframe, gdzie przetwarzanie transakcji online kładło nacisk nie tylko na szybkość wykonania jednej transakcji, ale także na zdolność do obsługi dużej liczby transakcji w tym samym czasie. W ostatniej dekadzie, gdy znacznie wzrosła liczba aplikacji opartych na transakcjach, takich jak routing sieciowy i obsługa stron internetowych, przemysł komputerowy ponownie położył nacisk na kwestie wydajności i przepustowości.
Jedną z technik osiągania tej równoległości są systemy wieloprocesorowe, czyli systemy komputerowe z wieloma procesorami. W przeszłości było to zarezerwowane dla high-endowych mainframe'ów, ale teraz małe serwery (2-8) wieloprocesorowe stały się powszechne na rynku małych firm. Dla dużych korporacji powszechne są wieloprocesory o dużej skali (16-256). Od lat 90. pojawiły się nawet komputery osobiste z wieloma procesorami.
Postęp w technologii półprzewodnikowej zmniejszył rozmiar tranzystorów; pojawiły się wielordzeniowe procesory, w których wiele procesorów jest zaimplementowanych na tym samym układzie krzemowym. Początkowo były stosowane w układach przeznaczonych na rynki wbudowane, gdzie prostsze i mniejsze procesory pozwalały na umieszczenie wielu instancji na jednym kawałku krzemu. Do 2005 r. technologia półprzewodnikowa umożliwiła masową produkcję układów CMP z dwoma procesorami CMP dla komputerów stacjonarnych klasy high-end. Niektóre konstrukcje, takie jak UltraSPARC T1, wykorzystywały prostsze (skalarne, in-order) projekty, aby zmieścić więcej procesorów na jednym kawałku krzemu.
Ostatnio inną techniką, która stała się bardziej popularna, jest wielowątkowość. W wielowątkowości, gdy procesor musi pobrać dane z wolnej pamięci systemowej, zamiast czekać na ich przybycie, procesor przełącza się do innego programu lub wątku programu, który jest gotowy do wykonania. Chociaż nie przyspiesza to konkretnego programu/wątku, zwiększa ogólną przepustowość systemu poprzez skrócenie czasu bezczynności procesora.
Koncepcyjnie wielowątkowość jest równoważna przełącznikowi kontekstu na poziomie systemu operacyjnego. Różnica polega na tym, że wielowątkowy procesor może wykonać przełączenie wątku w jednym cyklu procesora zamiast setek lub tysięcy cykli procesora, których normalnie wymaga przełączenie kontekstu. Osiąga się to poprzez replikację sprzętu stanowego (takiego jak plik rejestrów i licznik programu) dla każdego aktywnego wątku.
Kolejnym usprawnieniem jest wielowątkowość symultaniczna. Technika ta pozwala procesorom superskalarnym na jednoczesne wykonywanie instrukcji z różnych programów/wątków w tym samym cyklu.