www.eprace.edu.pl » ia64 » Mechanizmy zastosowane w architekturze IA-64 » Spekulatywne wykonywanie kodu oraz spekulatywny dostęp do pamięci

Spekulatywne wykonywanie kodu oraz spekulatywny dostęp do pamięci

Są dwa typy spekulacji związane z mechanizmami procesora. Spekulatywne wykonanie kodu, oraz spekulatywny dostęp do danych. W obydwu, kompilator paczkuje instrukcje w taki sposób aby operacje opóźniające ścieżkę krytyczną danego problemu były wykonywane wcześniej. Kompilator będzie ustawiał instrukcje spekulatywne, jeżeli jest powód aby mieć nadzieję iż taka spekulacja będzie korzystna. Aby osiągnąć korzyść muszą byś spełnione dwa warunki: prawdopodobieństwo potrzeby poprawiania działania kompilatora jest znikomo małe, oraz wcześniejsze wykonanie jakiejś operacji umożliwia późniejszą optymalizację (ILP)

Spekulatywne wykonywanie kodu (Control speculation)

Idea spekulatywnego wykonywania kodu to optymalizacja wykonania programu przez kompilator która polega na wcześniejszym wykonaniu instrukcji lub grupy instrukcji, zanim przebieg programu dotrze do miejsca normalnego wykonania tych instrukcji. W przypadku zastosowania tego mechanizmu instrukcje są już wykonane w tym momencie gdy dopiero miały być załadowane. Technika to pozwala na zwiększenie efektów z równoległego wykonywania instrukcji (ILP), oraz zaoszczędzenie czasu przy wykonywaniu instrukcji które zajmują dużo mocy procesora. W efekcie całkowity czas wykonania programu skraca się. Istnieje możliwość nietrafnego wykonania instrukcji, spowodowana skokiem do innej części kodu, niż ta która została już wykonana. Jeśli dane nie zostały prawidłowo przewidziane to istnieje mechanizm który zapisuje takie zdarzenie. Sygnalizacja polega na wstawieniu specjalnego znacznika do docelowego rejestru, w którym znajdują się rezultaty wykonanych spekulatywnie instrukcji. Znaczniki te są inaczej reprezentowane w rejestrach ogólnego przeznaczenia a inaczej w rejestrach zmiennoprzecinkowych. Z rejestrami ogólnego przeznaczenia skojarzony jest specjalny bit NaT (Not a Thing). Jeśli w ten bit ma wartość 1 oznacza to, iż dane w odpowiednim rejestrze ogólnego przeznaczenia nie mogą być wykorzystane. W rejestrach zmiennoprzecinkowych wystąpienie nieprawidłowo załadowanych instrukcji które korzystały z tych rejestrów jest sygnalizowane przez specjalną wartość umieszczaną w tym rejestrze zwaną NaTVal (Not a Thing Value)

Aby lepiej zilustrować działanie spekulatywnego wykonania kodu można podać następujący przykład:

if(a>b)

instrukcja1;

else

instrukcja2;

Jeśli kompilator spodziewa się, że warunek (a>b) będzie prawdziwy to nakazuje wykonanie instrukcja1 wcześniej, zanim ten warunek będzie sprawdzany. W normalnym przypadku instrukcja1 była by wykonana dopiero kiedy warunek (a>b) będzie sprawdzony i okaże się prawdziwy, może się również zdarzyć taka sytuacja, że instukcja1 nie byłaby w ogóle wykonana. Jeśli kompilator użył techniki spekulatywnego wykonania kodu, pozostaje później jedynie czy warunek rzeczywiście został prawidłowo przewidziany

Wszystkie instrukcje zostały na dwie kategorie: spekulatywne (te które można używać spekulatywnie) oraz nie-spekulatywne (te które nie można). Nie-spekulatywne instrukcje nie powodują późniejszego sprawdzania czy naprawdę miały być wykonane (nie są później ustawiane wartości NaT). Stosowanie takich instrukcji wcześniej zanim jest znane miejsce ich wykonania jest niebezpieczne. Instrukcje spekulatywne są wyposażone w mechanizmy sprawdzania i można je bezpiecznie stosować zanim znane jest miejsce ich wykonania. Ładowanie do rejestrów ogólnego przeznaczenie, oraz zmiennoprzecinkowych jest możliwe za pomocą instrukcji spekulatywnych (ld.s, ldf.s, ldfp.s) jak i nie-spekulatywnych (ld, ldf, ldfp).

Sprawdzanie czy dane w rejestrach są poprawne wykonuje się za pomocą instrukcji chk.s. Instrukcja ta powinna być wykonana zanim zostanie osiągnięty punkt gdzie spekulatywnie wykonane instrukcje miały miejsce pierwotnie. Instrukcja ta testuje wystąpienie znacznika niepoprawności danych. Jeśli ten nie jest znaleziony to spekulatywne wykonanie zakończyło się sukcesem i dalej przebieg programu toczy się normalnie. Jeśli jednak znacznik taki jest to spekulatywne wykonanie zakończyło się niepowodzeniem i instrukcje muszą zostać powtórzone.

Spekulatywny dostęp do pamięci (Data speculation)

Spekulatywny dostęp do danych nazywany jest także zaawansowanym ładowaniem (advanced loads)

W dzisiejszych systemach procesor działa zwykle o wiele szybciej niż pamięć. Spodziewane jest tylko pogłębienie tej przepaści. W ten sposób opóźnienie w dostępie do danych jest poważnym problemem dla dzisiejszych systemów. Aby zredukować niekorzystny wpływ takiego wąskiego gardła tradycyjne architektury stosują pozwalają kompilatorowi oraz procesorowi na zaplanowanie wcześniejszego ładowania danych z pamięci, niż to jest rzeczywiście potrzebne. Niestety dla dzisiejszych systemów barierą która nie pozwala zawsze zastosować tą technikę są skoki. Spekulatywny dostęp do danych polega na przeprowadzeniu ładowania danych z pamięci zanim zostanie wykonane zapisanie do pamięci które rzeczywiście ma poprzedzać ładowanie. Jedynym warunkiem użycia tego mechanizmu jest to aby relacja między adresem pod który zapisujemy dane, a adresem skąd ładujemy dane była wyznaczona niedwuznacznie. Nie ma bariery skoków, dane mogą być ładowane do pamięci nawet przed skokiem za którym pamięć może być potrzebna. Za takim postępowaniem kryją się problemy. Jeżeli nie zostanie prawidłowo przewidziana pamięć do załadowania, zostaną załadowane dane które nie są poprawne. Trzeba jakoś poradzić sobie z takim problemem, gdyż w przeciwnym wypadku mogą wystąpić poważne błędy jeśli taki błąd zostanie zignorowany. Istniały do tej pory dwa podejścia do zaistniałej sytuacji. Dodanie specjalnego sprawdzania błędów, niestety taka alternatywa, niweluje dotychczasowe zyski szybkościowe, lub tzw. „work without net” czyli ryzykowanie wystąpienia katastrofalnych niewykrywalnych błędów, poprzez wyłączenie dodatkowej kontroli.

IA-64 wybiera tzw. złoty środek, oferując nowe rozwiązanie. Podstawową ideą tego rozwiązania jest to iż, do danych połączony jest bit NaT (Not a Thing) który informuje czy błąd został wygenerowany podczas ładowania danych czy nie. Ten bit wskazuje, ze dane przechodzące różne operacje np.: arytmetyczne, przemieszczenia, były sprawdzone przez tzw. instrukcję chk. (instrukcja ta została tak zaprojektowana, że nie zabiera dodatkowego czasu procesora.)

Taka taktyka sprawia, że kompilator może śmiało ładować spekulatywnie dane przed czasem, bez płacenia niepotrzebnej kary przy wyjątkowych sytuacjach. Jest to znakomita korzyść w porównaniu z dotychczasowymi rozwiązaniami, gdzie traci się albo na dodatkowym sprawdzaniu błędów, albo naraża się na ryzyko. Używanie bitu NaT oraz sprawdzania dodatkowego, bez użycia głównej mocy obliczeniowej procesora pozwala na używanie spekulatywnego dostępu do pamięci bardzo efektywnie. Dodatkową korzyścią z bitu NaT jest to, że może on być używany do sygnalizowania strukturalnych błędów jakie można znaleźć w językach C/C++. Ponieważ kompilator może zaplanować wykonanie instrukcji chk kiedy chce, możliwe jest bezpieczne odwlekanie wystąpienia błędów, aż do najlepszego momentu na obsłużenie ich. Taka technika wspierająca strukturalną obsługę błędów jest jedną z kluczowych cech IA-64 i gwarantuje stabilność systemu.

Mechanizm obsługi błędów polega na następującym algorytmie:

IA-64 trzyma ścieżki wszystkich ładowań, które są zaplanowane przed możliwymi konfliktami zapisu w specjalnej tablicy. Tablica ta jest nazwana ALAT (Advanced Load Addres Table – tablica adresów zaawansowanego ładowania). Kiedy napotkany jest zapis do pamięci który powoduje konflikt z danymi zapisanymi w tablicy, to ten zapis jest usuwany z tablicy. Natomiast sposób sprawdzania załadowanych danych polega na przeglądnięciu tablicy. Brak wpisu w tablicy sygnalizuje ze wartość należy ponownie załadować, aby osiągnąć poprawny rezultat. Metoda z tablicą ładowań jest unikalna i pozwala ładować dane na tyle wcześniej, aby opóźnienie w dostępie do pamięci zostało praktycznie w całości zniwelowane.

Podobnie jak przy spekulatywnym wykonywaniu kodu tak i przy spekulatywnym dostępie do pamięci instrukcje są podzielone na dwie kategorie. Pierwsza kategoria to ta które bezpiecznie można używać przy spekulatywnym dostępie do pamięci, natomiast druga to taka która nie zawiera mechanizmu sprawdzania poprawności załadowanych danych.

Korzyści ze spekulatywnego dostępu do danych są oczywiste, procesor nie musi czekać na dane, a trzeba zauważyć, że dostęp do fizycznej pamięci ram jest wolny w porównaniu z szybkością procesora.

Badania prowadzone nad tą technologią czyli nad spekulatywnym wykonywaniem kodu, oraz spekulatywnym dostępem do pamięci wykazały następujące wnioski.

W ogólnych zastosowaniach technika to poprawia osiągi procesora o 79% w porównaniu z systemami nie posiadającymi spekulatywnych mechanizmów.

Największe korzyści z zastosowania spekulatywnego wykonywania kodu, oraz spekulatywnego dostępu do pamięci można zauważyć dla programów które wymagają częstego dostępu do cache. Takie zachowanie jest typowe na przykład dla systemów operacyjnych czy wielkich baz danych.



komentarze

Copyright © 2008-2010 EPrace oraz autorzy prac.