Czy procesory mogą być jeszcze wydajniejsze? Poznaj ograniczenia architektury, a także zobacz co szykuje przyszłość.
Jeszcze kilka lat temu większość dostępnych w sklepach procesorów było układami jednordzeniowymi. Obecnie trudno znaleźć modele, które miałyby mniej niż dwa rdzenie. Architektura współczesnych układów wielordzeniowych powoli zbliża się jednak do swoich konstrukcyjnych ograniczeń, uniemożliwiających dalszy rozwój, i to pomimo ciągłego schodzenia z procesu produkcyjnego. Dlatego inżynierowie już dziś intensywnie pracują nad różnymi koncepcjami wielordzeniowej architektury, która nie miałaby ograniczeń obecnych układów.
Od czasów procesora Intel 8080, a więc od połowy lat 70. ubiegłego wieku, podstawowym sposobem zwiększania wydajności układów kolejnych generacji (nie tylko tych zgodnych z architekturą x86) było zwiększanie częstotliwości taktowania procesorów oraz zwiększania stopnia zrównoleglenia wykonywanych instrukcji. Istotnymi elementami zwiększającymi szybkość pracy współczesnych procesorów są też mechanizmy przewidywania rozgałęzień programu (ang. branch prediction) i szybka wielopoziomowa pamięć podręczna cache. W tym miejscu warto dodać, że procesor, w którym zwielokrotniono liczbę jednostek wykonawczych nosi nazwę superskalarnego.
Tradycyjne sposoby zwiększania wydajności
Aby wprowadzić w życie koncepcję superskalarności trzeba zwiększyć szerokość szyny danych (ang. data bus) tak, aby była ona większa niż długość słowa dla pojedynczej instrukcji. W wypadku współczesnych procesorów zgodnych z architekturą x86, słowa mają postać 8-, 16-, 32- i 64-bitowych rozkazów (czasami w wypadku instrukcji multimedialnych są to słowa 128-bitowe), a szerokość szyny danych to zwykle od 128 do 256 bitów (np. w procesorach Core i3, i5 i i7 jest ona 192-bitowa), więc przesłanie w jednym takcie kilku instrukcji nie stanowi problemu.
Uproszczony schemat architektury komputera – komunikacja za pomocą szyn systemowych
Z superskalarnością związana jest też idea potokowego (ang. pipelining) przetwarzania danych, wywodząca się z procesorów typu RISC (Reduced Instruction Set Computer), czyli układów o zredukowanej (i uproszczonej) liście rozkazów. Kiedyś procesory zgodne z architekturą x86 były układami typu CISC (Complex Instruction Set Computer), które wykorzystywały listę złożonych rozkazów, co z kolei - ze względu na strukturę instrukcji (jeden cykl – jeden rozkaz) - uniemożliwiało zastosowanie mechanizmów architektury potokowej i superskalarnej. Aby ominąć to ograniczenie i przyśpieszyć działanie jednostki centralnej, wszystkie pecetowe procesory Intela i AMD, począwszy od układów z serii Pentium, konstruowane są jako układy RISC ze zintegrowanym dekoderem instrukcji x86, tłumaczącym tradycyjny CISC-owy kod x86 na wewnętrzny mikrokod procesora.
Intel i960 z 1988 roku był pierwszym na świecie seryjnie produkowanym procesorem superskalarnym.
Intel i960 to układ RISC stosowany do budowy mikrokontrolerów
Wróćmy jednak do przetwarzania potokowego, które jest jednym ze sposobów sekwencyjnego przetwarzania danych i polega na podzieleniu cyklu przetwarzania na odrębne, połączone ze sobą szeregowo - jeden za drugim - bloki. Innymi słowy, w przetwarzaniu potokowym dane po przejściu przez jeden blok trafiają do następnego, aż osiągną ostatni. Co ważne, dane w poszczególnych blokach mogą być przetwarzane niezależnie, pod warunkiem że dane wejściowe dla bloku nie zależą od danych wyjściowych z poprzedniego. Jest to co prawda trudne do osiągnięcia w wypadku pojedynczej instrukcji, ale niezależność bloków wykonawczych pozwala „wpuścić” w potok wykonawczy naraz kilka rozkazów o różnym stopniu przetworzenia, np. jedna instrukcja może być przetwarzana przez końcowy fragment potoku, druga przez środkowe bloki, a trzecia może znajdować się na samym początku potoku wykonawczego.
Wieloetapowy potok w procesorach Pentium III (10-etapowy) i Pentium 4 (20-etapowy)
Potok wykonawczy w architekturze AMD K7 (Athlon)
Oczywiście, przetwarzanie potokowe wymaga takiego zarządzania danymi, aby poszczególne moduły w poszczególnych potokach wykonawczych (we współczesnych procesorach potoków wykonawczych jest ich zawsze kilka) były maksymalnie wykorzystane w czasie przetwarzania ciągu informacji. Dzięki temu m.in. nie ma pustych przebiegów, a procesor w jednym cyklu zegara jest w stanie wykonać kilka instrukcji. W tym celu stosuje się mechanizmy wykonywania rozkazów poza kolejnością (ang. out of order execution), które pozwalają na wykonywanie instrukcji w innej kolejności niż ta w wykonywanym przez procesor programie.
Wykonywanie rozkazów poza kolejnością wymaga stosowania procedur śledzenia kodu we wszystkich potokach wykonawczych pod kątem zależności między danymi i niedopuszczenia do sytuacji, w której dwa moduły wykonują jednocześnie to samo zadanie. Istotne jest też zaimplementowanie mechanizmów przemianowywania rejestrów, które pozwalają uelastycznić proces jednoczesnego wykonywania rozkazów. A to dopiero początek.