Architektura Komputerów/Wykład 10: Redukcja opóźnień w procesorach

Plan wykładu





Opóźnienia w procesach superpotokowych i superskalarnych




O ile w procesorze jednopotokowym utrata pięciu cykli oznacza utratę czasu wykonania pięciu instrukcji, to w przypadku procesora superskalarnego opóźnienie wyrażone w „straconych” instrukcjach jest tyle razy większe, ile procesor ma potoków.


Duży proporcjonalny spadek wydajności procesorów superskalarnych wynikający z opóźnień powoduje konieczność minimalizacji tych opóźnień.



Opóźnienia skoków



  • Ze statystyki wynika, że skoki stanowią od 7 do 14% wszystkich wykonywanych instrukcji
    • dla dalszych rozważań przyjmiemy, że skoki stanowią 10% instrukcji
  • W bardziej złożonych architekturach opóźnienia skoków paraliżują działanie procesora
    • Niezbędne są mechanizmy umożliwiające znaczącą redukcję opóźnień

Skoki stanowią znaczny odsetek instrukcji zawartych w programach. Można przyjąć, że ok. 10% wszystkich instrukcji wykonywanych przez procesor stanowią skoki. Architektury superpotokowe i superskalarne charakteryzują się dużymi opóźnieniami skoków, sięgającymi nawet kilkunastu cykli. Mnożąc liczbę cykli procesora przez liczbę instrukcji wykonywanych w każdym cyklu otrzymujemy wartości opóźnień, które po przeliczeniu na procentową utratę wydajności oznaczają, że procesor pracuje z wydajnością nie przekraczającej ¼ tego, czego oczekuje użytkownik.


Aby wydajność procesora była bliska wydajności teoretycznej, niezbędna jest implementacja mechanizmów przyspieszających.



Spekulatywne wykonanie instrukcji



  • Współczesne procesory są wyposażone w mechanizmy spekulatywniego wykonania instrukcji
    • instrukcja jest wykonywana na podstawie przewidywania, że powinna być wykonana (np. po instrukcji skoku)
    • w przypadku błędnego przewidywania następuje anulowanie instrukcji i wszelkich efektów jej wykonania
    • procesory umożliwiają wielopoziomową spekulację - wykonanie instrukcji po kilku nierozstrzygniętych skokach
  • Jeśli w procesorze przebywa równocześnie 100 instrukcji, to wśród nich jest 10 skoków
    • oznacza to. że niemal przez cały czas wykonanie instrukcji ma charakter spekulatywny
    • bez spekulacji wydajność byłaby ok. 10-krotnie mniejsza

Mechanizmy redukcji opóźnień mają charakter heurystyczny. Oznacza to, że procesor próbuje „zgadywać” przebieg wykonania programu i na tej podstawie pobiera i wykonuje kolejne instrukcji. Procesor nie ma pewności, czy „zgadywanie” dało właściwy efekt aż do czasu wykonania instrukcji skoku. Wszystkie instrukcje wykonywane w tym czasie muszą być wykonywane w taki sposób, aby ich wyniki mogły zostać anulowane.

Taki sposób wykonania instrukcji nazywamy spekulatywnym. Współczesne procesory praktycznie przez cały czas pracują spekulatywnie.



Terminologia



  • Wykonanie skoku - wykonanie instrukcji skoku, które może, ale nie musi, powodować zmianę wartości PC Realizacja skoku - zmiana zawartości PC
  • Skok warunkowy przy niespełnionym warunku zostaje wykonany, ale nie zrealizowany
  • Adres skoku:
    • statyczny - zawarty w instrukcji w postaci stałej
    • dynamiczny- zmienna wartość w rejestrze lub pamięci (w tym na stosie - powroty z procedur)
  • Skoki z adresem statycznym:
    • względne - w obrazie instrukcji jest zapamiętane przemieszczenie względem nPC jako liczba ze znakiem (U2)
    • bezwzględne - w obrazie instrukcji jest zapamiętany adres docelowy, ładowany do PC


Przewidywanie skoków - zasady



  • Przewidywanie skoków polega na przewidzeniu wykonania instrukcji skoku przed faktycznym jej wykonaniem
  • Po przewidzeniu skoku procesor spekulatywnie wykonuje instrukcje po skoku (zrealizowanym lub niezrealizowanym)
  • Rzeczywiste wykonanie instrukcji skoku umożliwia sprawdzenie poprawności przewidywania
  • Jeśli przewidywanie było poprawne:
    • skok został wykonany bez opóźnień lub ze zmniejszonym opóźnieniem
    • instrukcje wykonane spekulatywnie mogą zostać zatwierdzone 
  • Jeśli przewidywanie było błędne:
    • instrukcje wykonane spekulatywnie są anulowane, a czas przeznaczony na ich wykonanie jest czasem opóźnienia skoku
    • następuje wykonanie właściwego strumienia instrukcji


Przewidywanie skoków - aspekty



  • Przewidywanie sposobu wykonania skoku warunkowego (zrealizowany/niezrealizowany)
  • Przewidywanie adresu docelowego skoku
  • Przewidywanie wystąpienia instrukcji skoku w strumieniu instrukcji

Przewidywanie skoków ma kilka aspektów. Możemy mówić o przewidywaniu wystąpienia instrukcji skoku w programie, przewidywaniu adresu docelowego skoku oraz o przewidywaniu, czy skok warunkowy będzie zrealizowany.


Możliwości przewidywania skoków



  • Aby wykonać skok, procesor musi:
    • wiedzieć, że występuje instrukcja skoku
    • znać adres docelowy
    • znać warunek wykonania dla skoków warunkowych 
  • Możliwość wykonania bez spekulacji:
    • skoki statyczne bezwarunkowe - natychmiast po stwierdzeniu wystąpienia instrukcji skoku
    • skoki statyczne warunkowe - po wyznaczeniu warunku
    • adres docelowy znany bezpośrednio po zdekodowaniu
    • skoki dynamiczne - po wyznaczeniu adresu docelowego 
  • Przewidywanie może obejmować:
    • dla wszystkich skoków - wystąpienie skoku, adres docelowy skoku
    • dla skoków warunkowych - dodatkowo sposób wykonania


Przewidywanie statyczne i dynamiczne



  • Statyczne - zachodzi po zdekodowaniu instrukcji skoku, nie wymaga gromadzenia wiedzy o historii programu
    • przewidywanie realizacji skoku warunkowego
  • Dynamiczne - na podstawie wiedzy o historii wykonania programu
    • przewidywanie wystąpienia instrukcji skoku przed jej pobraniem
    • przewidywanie adresu docelowego skoków statycznych i dynamicznych
    • przewidywanie realizacji skoku warunkowego

Wszystkie metody przewidywania skoków można podzielić na statyczne i dynamiczne. Metody statyczne, w przeciwieństwie do dynamicznych, nie wymagają gromadzenia przez procesor informacji o historii wykonania programu. Metody statyczne ograniczają się więc do przewidywania sposobu wykonania skoku warunkowego po jego zdekodowaniu przez procesor, kiedy już wiadomo, że instrukcja skoku wystąpiła oraz jaki jest adres docelowy skoku.


Metody dynamiczne polegają na kolekcjonowaniu przez procesor informacji o wykonaniu programu. Na tej podstawie można przewidywać wszystkie wymienione wcześniej aspekty skoków.



Statyczne przewidywanie realizacji skoku



  • Przewidywanie przez kompilator lub programistę
    • kompilator języka wysokiego poziomu lub programista piszący program w asemblerze zna prawdopodobieństwo wykonania skoku warunkowego
    • informacja ta może zostać umieszczona w programie, o ile model programowy uwzględni dwa warianty skoków warunkowych
      • skoki prawdopodobne
      • skoki nieprawdopodobne
    • przykłady: Alpha AXP, Intel Pentium 4
  • Przewidywanie przez procesor
    • z analizy własności programów wynika, że ponad 60% skoków warunkowych w tył jest realizowanych, a skoków w przód - nierealizowanych
    • skoki warunkowe są zapisywane jako względne - bit znaku przemieszczenia może zostać użyty przez procesor jako znacznik prawdopodobieństwa skoku

Przy przewidywaniu statycznym przez kompilator lub programistę, instrukcje skoków warunkowych zostają a priori oznaczone jako prawdopodobne lub nieprawdopodobne. Na tej podstawie procesor będzie podejmował decyzję o sposobie spekulatywnego wykonania skoku po jego zdekodowaniu, a przed właściwym deterministycznym wykonaniem. Taki sposób przewidywania wymaga odmiennego kodowania instrukcji skoków prawdopodobnych i nieprawdopodobnych. Mechanizm taki posiadały m.in. procesory rodziny Alpha AXP i Intel 960. Podobny mechanizm, korzystający z tzw. prefiksów instrukcji, wprowadzono w architekturze Pentium 4.


Przewidywanie przez procesor polega na określaniu prawdopodobieństwa na podstawie kierunku skoku. Korzysta się tu ze szczególnej własności programów.



Dynamiczne przewidywanie wystąpienia skoku



  • Wymaga wprowadzenia do struktury procesora dodatkowych bloków sprzętowych, gromadzących informację o historii wykonania programu
  • Bufor docelowy skoków (BTB - Branch Target Buffer)
    • gromadzi informację o pewnej liczbie ostatnio wykonywanych skoków
    • działa na zasadzie podobnej do kieszeni
    • często realizowany w powiązaniu z kieszenią kodu
  • Przy każdej realizacji skoku adres, pod którym jest położona instrukcja skoku jest odnotowywany w BTB
    • na ogół razem z adresem docelowym skoku
  • Przy każdym pobraniu instrukcji stopień pobrania sprawdza, czy adres pobrania został wcześniej zanotowany jako adres instrukcji skoku
    • jeśli tak - stopień pobrania może w następnym cyklu "wykonać" skok

Przewidywanie dynamiczne wymaga umieszczenia w procesorze bloków służących do gromadzenia informacji o wykonaniu skoków.


Główną strukturą jet tu bufor docelowy skoków, gromadzący informacje o wystąpieniach skoków i ich adresach docelowych.


Sam bufor docelowy służy do przewidywania wystąpienia skoku przed jego pobraniem i zdekodowaniem oraz do określenia adresu docelowego takiego skoku.



Bufor docelowy skoków - działanie




  • Przy wykonaniu skoku do adresu Y położonego pod adresem X w buforze docelowym zostaje zapamiętana para X,Y
  • Równocześnie z pobraniem każdej instrukcji następuje przeszukanie bufora z wartością scanPC jako kluczem
  • Jeśli w buforze znajduje się wartość X=scanPC, to na końcu cyklu następuje załadowanie Y do scanPC
    • w następnym cyklu stopień pobrania pobierze instrukcję spod przewidywanego adresu docelowego skoku
  • Opisany schemat działa poprawnie jeżeli:
    • pod adresem X nadal znajduje się ta sama instrukcja skoku
    • adres docelowy skoku pozostaje niezmienny
    • warunek wykonania (dla skoku warunkowego) ma tę samą wartość
  • Przy udanym przewidywaniu skok wykonuje się bez opóźnień

Bufor docelowy bez dodatkowych mechanizmów sprzętowych przewiduje skoki wyłącznie jako bezwarunkowe. Samo przewidywanie opiera się na spełnieniu przez wykonywany program kilku założeń. W przypadku niespełnienia wymienionych założeń, przewidywanie się nie powiedzie.


Bufor docelowy - ograniczenia



  • Bufor docelowy w podstawowej formie nie może przewidywać:
    • skoków dynamicznych
    • skoków warunkowych przy różnych wartościach warunku w kolejnych wykonaniach
  • Zastosowanie skoków dynamicznych:
    • powroty z procedur - b. częste, niezbędne
    • wskaźniki na funkcje, w tym metody wirtualne - zależne od przyjętego stylu programowania
    • jedna z możliwych realizacji konstrukcji typu switch - zależne od kompilatora
  • Skoki dynamiczne mogą być przewidywane w buforze docelowym jako statyczne
    • działa to dość dobrze dla wskaźników na funkcje
  • Potrzebny inny mechanizm przewidywania powrotów z procedur

Z przytoczonych wcześniej założeń wynika, że bufor docelowy nie może przewidywać skoków o zmiennym adresie docelowym, ani nie radzi sobie z przewidywaniem skoków warunkowych wykonywanych w różny sposób.


Ponieważ powroty z procedur są jednymi z najczęściej wykonywanych instrukcji, procesor powinien być wyposażony w inny mechanizm umożliwiający ich przewidywanie.



Przewidywanie  adresów powrotów z procedur




  • Szczególnie potrzebne w procesorach CISC
    • odczyt śladu ze stosu jest czasochłonny
  • Założenie: adres powrotu jest ostatnim zapamiętanym śladem
    • implikuje to wymaganie, że każdej instrukcji skoku ze śladem odpowiada instrukcja powrotu
    • przewidywanie adresu powrotu nie działa dla par: PUSH-RET, CALL-POP


Sprzętowy stos powrotów



  • Niewielki, niewidoczny programowo stos (bufor LIFO), umieszczony w procesorze
    • pojemność: 8..16 elementów
  • Wykonanie instrukcji skoku ze śladem powoduje umieszczenie na stosie powrotów wartość i śladu
    • dzieje się to niezależnie od przesłania śladu na stos w pamięci (CISC) lub do rejestru (RISC)
  • Napotkanie instrukcji powrotu powoduje zdjęcie śladu ze stosu
    • przy przewidywaniu dynamicznym akcja ta jest inicjowana przez BTB przy pobraniu instrukcji - powrót nie wnosi żadnych opóźnień
    • przy przewidywaniu statycznym spekulatywny powrót następuje po zdekodowaniu instrukcji powrotu
  • Wykonanie instrukcji powrotu weryfikuje poprawność spekulacji
    • jeśli adres ze stosu sprzętowego jest różny od adresu powrotu -następuje anulowanie całego ciągu instrukcji
    

Sprzętowy stos powrotów - efektywność



  • Stos powrotów działa skutecznie dla fragmentów programu, w których zagłębianie procedur nie przekracza pojemności stosu
  • Każda "niesparowana" instrukcja CALL lub RETURN powoduje de synchronizację stosu
    • wszystkie następne powroty będą przewidywane błędnie, aż do zapełnienia całego stosu nowymi śladami


Dynamiczne przewidywanie realizacji skoku



  • Sam bufor docelowy przewiduje skoki warunkowe jako bezwarunkowe
    • skok niezrealizowany po zrealizowanym jest przewidziany błędnie
  • Lepsze przewidywanie realizacji skoków wymaga implementacji predyktora, decydującego o tym, czy procesor ma traktować skok jako realizowany czy nierealizowany
  • Skróty i konwencje:
    • T (Taken) - skok realizowany
    • NT (Not Taken) - skok nierealizowany
    • PT (Predict Taken) - skok przewidywany jako realizowany (na grafach - kolor zielony)
    • PNT (Predict Not Taken) - skok przewidywany jako nierealizowany-na grafach kolor czerwony)



  • Automaty decydujące o sposobie spekulatywnego wykonania przewidywanego skoku warunkowego na podstawie historii wykonania programu
  • Działanie predyktorów
    • deterministyczne wykonanie (rozstrzygnięcie skoku) przez procesor powoduje modyfikację stanu predyktora
    • spekulatywne wykonanie skoku na podstawie stanu predyktora
  • Klasyfikacja predyktorów
    • wg konstrukcji elementarnego automatu predyktora
      • dwustanowe
      • czterostanowe
    • wg sposobu wiązania instrukcji skoku z automatem predyktora:
      • jednopoziomowe - prawdopodobieństwo bezwarunkowe
      • dwupoziomowe - prawdopodobieństwo warunkowe
      • trójpoziomowe - p. warunkowe z adaptacją schematu przewidywania


Predyktor dwustanowy - budowa



  • Stany: T, NT
  • Predyktor przydzielany do instrukcji skoku z chwilą pierwszej realizacji
  • Kolejne przewidywanie skoku jest takie samo, jak było ostanie wykonanie


Predyktor dwustanowy - charakterystyka



  • Błędne przewidywanie skoku zamykającego pętlę zagnieżdżoną przy pierwszym i ostatnim wykonaniu 
    • istotne przy pętlach iterowanych niewielką liczbę razy
  • Błędne przewidywanie po każdym odstępstwie od typowego wykonania
  • 100% błędne przewidywanie skoków realizowanych co drugi raz

Podczas ostatniej iteracji pętli zagnieżdżonej predyktor przewiduje zamknięcie pętli, i jest to oczywiście przewidywanie błędne. Przy wyjściu z pętli predyktor myli się. Po wyjściu z pętli predyktor dwustanowy ustawia przewidywanie skoku zamykającego pętle na NT(PNT). Przy powtórnym wejściu w pętlę predyktor przewiduje opuszczenie pętli, podczas, gdy pętla się zamyka. Po błędzie predyktora następuje zmiana stanu na T(PT). Predyktor myli się więc przy pierwszym i ostatnim obiegu pętli.


W zastosowaniu do skoku wykonywanego naprzemiennie predyktor myli się w każdym wykonaniu.



Predyktor czterostanowy




  • Stany:
    • ST - Strongly Taken 
    • WT - Weakly Taken
    • WNT - Weakly Not Taken
    • SNT - Strongly Not Taken
  • Pierwsza realizacja skoku wprowadza predyktor w stan ST
  • Zmiana przewidywania następuje po dwóch kolejnych takich samych wykonaniach

Predyktor czterostanowy działa jak dwubitowy licznik rewersyjny z nasyceniem. W stanach ST i WT mamy przewidywanie PT, a w stanach WNT i SNT PNT.


Predyktor czterostanowy - charakterystyka



  • Błędne przewidywanie skoku zamykającego pętle przy ostatnim wykonaniu 
    • istotne przy pętlach iterowanych niewielką liczbę razy
  • Poprawne przewidywanie po każdym jednokrotnym odstępstwie od typowego wykonania
  • Skoki realizowane co drugi raz przewidywane poprawnie w 50%

Po serii obiegów pętli predyktor skoku zamykającego pętlę jest w stanie SNT. Przy wyjściu z pętli predyktor błędnie przewiduje zamknięcie, po czym przechodzi w stan WT. Przy następnym wejściu w pętlę predyktor poprawnie przewiduje zamknięcie.


Skok naprzemienny jest przewidywany zawsze tak samo, czyli predyktor myli się w co drugim wykonaniu. Dzieje się tak dzięki temu, że początkowo predyktor ustawie się w stan ST (ew. SNT), po czym oscyluje pomiędzy ST i WT (lub SNT i WNT).



Ograniczenia predyktorów jednopoziomowych




  • Brak możliwości trafnego przewidzenia wszystkich iteracji skoku zamykającego pętlę
    • szczególnie istotne przy pętlach iterowanych niewielką liczbę razy. często spotykanych w oprogramowaniu
  • Brak możliwości trafnego przewidzenia skoków wykonywanych cyklicznie
    • np. dwa razy realizowany, następnie dwa razy nierealizowany


Predyktory dwupoziomowe



  • Przechowują informację o prawdopodobieństwie warunkowym
    • "jak wykona się skok, jeśli ostatnio raz był zrealizowany i trzy razy niezrealizowany"
  • Predyktor elementarny nie jest bezpośrednio związany z instrukcją skoku
    • wybór predyktora zachodzi na podstawie adresu instrukcji skoku i historii wykonań
  • Rejestr historii:
    • rejstr przesuwający o długości 8..16 bitów, zawierający historię wykonań (T/NT) dla ostatnich wykonań pojedynczego lub wielu skoków
  • Predyktory dwupoziomowe i trójpoziomowe są nazywane korelatorami skoków


Predyktor dwupoziomowy gLocal



W schemacie gLocal adres instrukcji skoku wybiera rejestr historii związany z danym skokiem. Zawartość rejestru historii i adres skoku są argumentami funkcji mieszającej, której wartość wybiera jeden z dwu- lub czterostanowych predyktorów.


Jako funkcji mieszającej najczęściej używa się operacji XOR.


Predyktor udziela odpowiedzi na pytanie o prawdopodobieństwo skoku na bazie poprzednich wykonań tego skoku.



Predyktor dwupoziomowy gShare



W schemacie gShare historia wykonań wszystkich skoków przechowywane jest w jednym rejestrze. Zawartość rejestru historii i adres skoku są argumentami funkcji mieszającej, której wartość wybiera jeden z dwu- lub czterostanowych predyktorów.


Predyktor udziela odpowiedzi na pytanie o prawdopodobieństwo skoku na bazie poprzednich wykonań wszystkich ostatnio wykonywanych skoków.



Predyktory dwupoziomowe - własności



  • Powolne „uczenie się" predyktora
    • trafne przewidywanie wymaga często wielu tysięcy wykonań skoku
  • Możliwość poprawnego przewidywania sekwencji wykonań o długości równej co najmniej długości rejestru historii
    • w tym możliwość poprawnego przewidywania zamknięć pętli iterowanych np. 8 razy
  • Niewrażliwość na pojedyncze nieregularności wykonania
  • Trafność przewidywania typowo około 95% lub więcej
  • Predyktor gLocal lepiej przewiduje pętle iterowane niewielką liczbę razy
  • Predyktor gShare lepiej przewiduje rozejścia zależne od wartości danych
    • o ile kilka kolejnych rozejść jest ze sobą powiązanych


Predyktory trójpoziomowe



  • Predyktor trójpoziomowy jest hybrydą dwóch predyktorów, zwykle gLocal i gShare
  • Dwa schematy przewidywania dla każdego skoku, z możliwością wyboru schematu
    • zmiana wyboru schematu dla danego skoku zachodzi, gdy dotychczas wybrany schemat przewidział skok błędnie, a alternatywny schemat-poprawnie
  • Trafność przewidywania typowo powyżej 96%


Redukcja opóźnienia danych z pamięci



  • W architekturach superskalarnych samo rozsunięcie instrukcji ładującej daną i instrukcji korzystającej z danej nie maskuje opóźnienia
  • Niezbędne pobieranie danych z pamięci do kieszeni lub buforów "na zapas" (Data Prefetch)
  • Realizacje:
    • pobieranie inicjowane przez oprogramowanie
    • automatyczne (spekulatywne) pobieranie przez procesor


Kieszenie a wydajność



  • Automatyczne umieszczenie danych w kieszeni nie zawsze przyspiesza wykonanie programu
    • Przykład - program przetwarzający dużą tablicę T1 przy użyciu danych z mniejszej tablicy T2
      • T2 mieści się w kieszeni L1
      • T1 mieści się w kieszeni L2, nie mieści się w kieszeni L2
      • odwołania do T1 powodują usuwanie elementów T2 z kieszeni L1 
      • efektywny współczynnik trafień L1 wynosi 0
    • Rozwiązanie: buforować T1 tylko w kieszeni L2, a T2 - również w L1
      • szybki dostęp do T1 (100% trafień w L1)
      • wolniejszy dostęp tylko do T1 (w kieszeni L2)
  • Potrzebna jest możliwość sterowania buforowaniem danych z poziomu aplikacji
    • wymaga to ujawnienia obecności kieszeni w modelu programowym
    • program powinien brać pod uwagę konfigurację komputera, na którym działa


Instrukcje pobierania danych



  • Instrukcje nie mają skutku w modelu programowym - nie modyfikują kontekstu procesora
  • Implementacja opcjonalna - prostsze procesory mogą je wykonywać jako puste
  • Instrukcje mają charakter sugestii (podpowiedzi), umożliwiających optymalizację działania procesora Jeden argument - adres pamięci
  • Wykonanie instrukcji polega na przesłaniu danych z pamięci do kieszeni lub bufora, ew. zmianie stanu linii kieszeni
    • instrukcja "pobrania do zapisu" tylko alokuje linie w kieszeni, które mają być całkowicie zapełnione nowymi danymi - bez późniejszego zapisu danych ich wartość będzie nieokreślona!
  • Czas i sposób wykonania mogą zależeć od bieżącego obciążenia interfejsu pamięci i kieszeni


Instrukcje PREFETCH - x86



  • PREFETCHNTA - pobranie danych z pominięciem kieszeni (do niewidocznego bufora danych w procesorze)
    • dane będą użyte jednorazowo i nie powinny powodować usunięcia innych obiektów z kieszeni
  • PREFETCHTO - pobranie danych do kieszeni poziomu 1. i ew. innych poziomów
  • PREFETCHT1 - pobranie danych do kieszeni poziomu 2 i ew. następnych
    • dane nie mieszczą się w L1 i nie powinny jej zajmować
  • PREFETCHT2 - pobranie danych do kieszeni poziomu 3. i ew. następnych
    • dane nie mieszczą się w L2 i nie powinny jej zajmować


Automatyczne pobieranie danych przez procesor



  • Odpowiedni moduł sprzętowy wykrywa wzorce odwołań do pamięci realizowanych przez program
  • Na podstawie rozpoznanego wzorca procesor samoczynnie wykonuje operację pobierania danych analogiczną do inicjowanej przez instrukcje PREFETCH
  • Mechanizm zaimplementowany min. w procesorach serii Pentium 4 i Core