PAMIĘĆ CACHE 1. Cel stosowania pamięci cache w procesorach Aby określić cel stosowania pamięci podręcznej cache, należy w skrócie omówić zasadę działania mikroprocesora. Jest on układem cyfrowym taktowanym przez sygnał zegarowy, który realizuje zadany program - ciąg rozkazów umieszczony w pamięci operacyjnej. Program ma zwykle zadanie przetworzenia określonych danych pobranych z pamięci (lub urządzeń zewnętrznych), oraz zapisanie wyników ich przetwarzania, też w pamięci (lub przekazanie do urządzeniach zewnętrznych). Schemat blokowy mikroprocesora przedstawiono na rysunku 1.1. Na schemacie znajduje się też blok pamięci operacyjnej, który fizycznie stanowi oddzielny układ. Rys. 1.1 Schemat blokowy mikroprocesora. OZNACZENIA RAM (Random Acces Memory) pamięć operacyjna BU (Bus Unit) - układ zarządzający magistralami AU (Addresing Unit) - układ obliczania adresu połączony z MMU (Memory Management Unit) układem zarządzania pamięcią IU (Instruction Unit) - dekoder instrukcji EU (Execution Unit) -moduł wykonawczy zawiera ALU (Aritmetic-Logic Unit) jednostkę arytmetyczno-logiczną FPU (Floating Point Unit) jednostkę zmiennoprzecinkową Połączenie mikroprocesora z pamięcią operacyjną realizuje się za pomocą dwóch magistral adresowej i danych. Do sterowania ruchem na tych magistralach wykorzystuje się zbiór sygnałów sterujących. Realizacja programu przez taki układ przebiega następująco: Kod rozkazów jest odczytywany z pamięci RAM i umieszczany w kolejce instrukcji. Poszczególne instrukcje (słowa binarne) trafiają z kolejki do dekodera IU, który rozkodowuje rozkazy przeznaczone do wykonania. Wykorzystuje przy tym informacje o sposobie kodowania rozkazów zawarte w pamięci stałej ROM umieszczonej w strukturze procesora. Zależnie od rozkazu dekoder często musi wydzielić z kodu instrukcji zawarte w niej argumenty, lub adresy danych, które procesor będzie wykorzystywał. Są one przesyłane odpowiednio do bloku wykonującego instrukcje EU i bloku adresowego AU. Blok adresowy AU i zarządzania pamięcią MMU wypracowuje na tej podstawie odpowiednie sygnały i dane przekazywane do bloku zarządzania magistralami BU. Po wystawieniu adresu na magistrali adresowej generowane są sygnały sterujące, które powodują odczyt danych z pamięci - układ pamięci wystawia dane na magistrali danych. Dane są przekazywane do bloku wykonującego instrukcje lub (jeśli są kodami następnych rozkazów programu do kolejki instrukcji). Blok wykonujący instrukcje EU zawiera jednostkę arytmetyczno-logiczną ALU, która dokonuje porównań lub działań matematycznych na argumentach stałoprzecinkowych. Argumenty zmienno-przecinkowe są przetwarzane w jednostce do tego celu wyspecjalizowanej FPU. Wyniki przetwarzania trzeba często zapisać w pamięci pod określonym adresem - blok wykonujący instrukcje przekazuje odpowiednie dane do bloku zarządzania pamięcią i bloku sterującego magistralami. Wszystko musi być odpowiednio synchronizowane w czasie, dlatego układ wykonujący instrukcje EU wypracowuje też różne sygnały sterujące, przez które ma możliwość wpływu na pracę innych bloków. Układ wykonujący instrukcje często też przechowuje wyniki pośrednie przetwarzania danych we własnych rejestrach. Jednak aby nie zaciemniać schematu na rys. 1.1 nie przedstawiano szczegółowej budowy wyróżnionych bloków. 1 Szybkość wykonywania programu zależy w znacznej mierze od czasu dostępu procesora do układu pamięci operacyjnej. Nie bez znaczenia jest także pojemność pamięci (ile danych można w niej zapisać). Stosowane we współczesnych komputerach wielozadaniowe systemy operacyjne umożliwiają uruchamianie wielu programów jednocześnie. Dobrze jest więc gdy procesor ma do dyspozycji dużą pamięć operacyjną RAM. Ważnym czynnikiem pozostaje też koszt zastosowanego układu pamięci. Naturalnie w przypadku praktycznego systemu musi on być jak najniższy. Istnieją wzajemne zależności pomiędzy wszystkimi opisywanymi wyżej parametrami. mniejszy czas dostępu - większy koszt większa pojemność - większy czas dostępu Z tego wynika, że nie jest możliwe wyprodukowanie idealnej pamięci o maksymalnie dużej pojemności, a przy tym małym czasie dostępu i minimalnym koszcie. Możliwe jest budowanie szybkich układów, ale stosunkowo drogich i o małej pojemności. Istnieją też duże pamięci o małych kosztach w przeliczeniu na bajt, ale cechujące się mniejszą efektywnością w zakresie czasu dostępu. We współczesnych procesorach stosuje się rozwiązanie kompromisowe, polegające na zastosowaniu pamięci wewnętrznej dwupoziomowej. Mikroprocesor wyposaża się we względnie dużą i wolniejszą pamięć główną, oraz w mniejszą ale szybszą pamięć podręczną cache. Ilustruje to schemat blokowy przedstawiony na rysunku 1.2. Takie rozwiązanie pozwala na korzystanie z pamięci o dużej pojemności, jednocześnie możliwe jest umieszczenie najpotrzebniejszych danych, w szybkiej pamięci podręcznej. Pamięć podręczna zawiera kopię części zawartości pamięci głównej. Gdy procesor zamierza odczytać słowo z pamięci, najpierw następuje sprawdzenie, czy słowo to nie znajduje się w pamięci podręcznej. Jeśli tak, to słowo to jest szybko dostarczane do procesora. Jeśli nie, to blok pamięci głównej RAM zawierający określoną liczbę kolejnych słów jest wczytywany do pamięci podręcznej, a następnie potrzebne słowo (zawarte w tym bloku) jest dostarczane do procesora. Następne odwołania do tego samego słowa i sąsiednich zawartych w przepisanym bloku będą realizowane już znacznie szybciej. Organizacja współpracy procesora z takimi pamięciami wymaga zastosowania dodatkowego układu - kontrolera cache, który steruje tym procesem. Rys 1.2 Podłączenie cache do procesora 2 Rys 1.3 Algorytm dostępu procesora do pamięci Efektywność stosowania cache zależy w znacznej mierze od sposobu ułożenia kodu programów i danych pobieranych z pamięci przez mikroprocesory. Zwykle kod i dane nie są "porozrzucane" przypadkowo po całej dostępnej przestrzeni adresowej w pamięci RAM. Większość odwołań do pamięci w trakcie wykonywania programu odbywa się przez pewien czas pracy mikroprocesora w wąskim obszarze. Zjawisko to jest określane mianem lokalności odniesień. Lokalność odniesień można uzasadnić intuicyjnie w następujący sposób: Z wyjątkiem rozkazów skoku i wywołania procedury, realizacja programów ma charakter sekwencyjny. Tak więc, w większości przypadków następny rozkaz przewidziany do pobrania z pamięci następuje bezpośrednio po ostatnio pobranym rozkazie. Rzadkością jest występowanie w programach długich, nieprzerwanych sekwencji wywołań procedury (procedura wywołuje procedurę itd.), a potem długiej sekwencji powrotów z procedur. Wymagałoby to ciągłego odwoływania się do oddalonych od siebie obszarów pamięci głównej, zawierających kody poszczególnych procedur. Większość pętli (konstrukcji bardzo często występujących w programach) składa się z małej liczby wielokrotnie powtarzanych rozkazów. Podczas iteracji następuje kolejne powtarzanie zwartej części programu. W wielu programach znaczna część obliczeń obejmuje przetwarzanie struktur danych, takich jak tablice lub szeregi rekordów ułożone kolejno w pamięci operacyjnej. Tak więc procesor pobiera dane zapisane w sposób uporządkowany w małym jej fragmencie. Oczywiście w długim czasie wykonywania programu procesor potrzebuje dane rozmieszczone w różnych odległych miejscach pamięci. Zwykle jednak, po wykonaniu skoku następne odniesienia odbywają się już lokalnie. Przepisanie bloku kolejnych komórek pamięci do szybkiego układu cache może więc skutecznie przyspieszyć dostęp do pamięci. Na drodze rozważań teoretycznych i różnych symulacji wyznaczono rozmiar tego wąskiego obszaru, do którego odwołuje się procesor. Stwierdzono, iż można przyjąć z prawdopodobieństwem 0,9 że większość odwołań do pamięci mieścić się będzie w bloku o rozmiarze nie przekraczającym 16 kB. Potwierdzają to praktyczne testy. Tak więc wystarczy naprawdę mały układ pamięci podręcznej cache, aby skutecznie przyspieszyć działanie mikroprocesora. Rozmiary całej pamięci RAM współczesnych komputerów są rzędu 64 - 128 MB, podczas gdy rozmiary cache w popularnych procesorach to 8 - 512 kB. Przedstawione wyżej schematy przedstawiają układ cache w dużym uproszczeniu. Istnieje możliwość zastosowania rozwiązania, w którym ten pierwszy poziom cache przyspiesza dostęp do następnego układu pamięci podręcznej, ten z kolei może wymieniać dane jeszcze z następnym, lub z pamięcią główną RAM. W niektórych stosowanych obecnie układach procesorowych można wyróżnić aż trzy poziomy pamięci podręcznej cache. W praktyce stosuje się wielopoziomową pamięć podręczną. W stosowanych obecnie rozwiązaniach można wyróżnić następujące poziomy pamieci podręcznej. 3 L1 - (level 1) zintegrowana z procesorem - umieszczona wewnątrz jego struktury. L2 - (level 2) umieszczona w jednej obudowie układu scalonego mikroprocesora lub na wspólnej płytce hybrydowej (Pentium II ). L3 - (level 3) występuje w bezpośrednim sąsiedztwie procesora ITANIUM. Pamięć podręczna najniższego poziomu (L1) jest stosunkowo mała, ale dane w niej zgromadzone są szybko dostępne dla procesora. W wypadku braku potrzebnych w danym momencie danych (braku trafienia), następuje odwołanie do pamięci kolejnych, wyższych poziomów. Po ich odczycie następuje przepisanie do niższych poziomów, tak by były szybciej dostępne w kolejnych odwołaniach. Jeśli dane nie są aktualnie buforowane w cache, następuje odczyt bloku pamięci głównej RAM, który je zawiera i wymiana zawartości cache. Pamięci niższych poziomów mogą mieć mniejszą pojemność i być bardziej efektywne. Procesor może szybko odczytywać mniejsze porcje danych w jednym cyklu zegara. Wyższe poziomy cache mają większe pojemności, dzięki czemu odwołania do RAM mogą odbywać się rzadziej. Można też w jednym odczycie przepisać większą porcję danych z RAM. Dobrze to obrazuje schemat przepływu danych w procesorze ITANIUM (rys. 1.4). Na tym schemacie pokazano też blok rejestrów procesora (Register File), w których zapisuje on odczytane z pamięci argumenty i wyniki działania rozkazów. Rys 1.4 Schemat przepływu danych w procesorze ITANIUM Jak wynika z rysunku, odczyt danych z pamięci RAM może odbywać się większymi porcjami z prędkością 2.1 GB na sekundę. Dzięki zastosowaniu odpowiedniej magistrali BSB (Back Side Bus) przepływ danych z pamięci podręcznej poziomu L3 do L2 odbywa się z prędkością 16 bajtów w ciągu jednego cyklu zegara. Możliwy jest również odczyt pojedynczych słów bezpośrednio do rejestrów co zaznaczono przerywaną linią. Z cache L2 procesor odczytuje już w jednym cyklu bloki danych wielkości 32 B. Pamięć poziomu L1 najbardziej efektywna w zakresie czasu dostępu, udostępnia procesorowi już pojedyncze słowa - zmienne przetwarzane w kolejnych rozkazach. Schemat 1.4 najpełniej obrazuje korzyści jakie daje układ pamięci podręcznych cache. Ważnym zagadnieniem, jest podział pamięci podręcznej na oddzielny blok dla kodu programu i oddzielny blok dla danych. W taki sposób jest podzielona pamięć poziomu L1 procesora ITANIUM - rys 1.4. Pamięć cache poziomów L2 i L3 jest już wspólna dla rozkazów i danych. W wielu (szczególnie starszych) procesorach wykorzystuje się również jednolitą pamięć podręczną poziomu L1. Takie rozwiązanie też posiada pewne zalety. Poniżej przedstawiono korzyści płynące z zastosowania pamięci oddzielnej i jednolitej. Pamięć oddzielna kod i dane Eliminowana jest rywalizacja o dostęp do pamięci między układem pobierania i dekodowania rozkazów w procesorze, a jednostką wykonującą w tym samym czasie inne, poprzednio pobrane rozkazy, które mogą wymagać odczytu pewnych zmiennych z cache. Ma to szczególne znaczenie w przypadku procesorów superskalarnych, w których kilka rozkazów jest wykonywanych równolegle. Dlatego we współczesnych procesorach poziom pamięci podręcznej L1 jest zawsze dzielony na blok danych i instrukcji. Pamięć łączna dla kodu i danych Poprawia się współczynnik trafień w tak zorganizowanej pamięci podręcznej, dzięki temu, że naturalnie równoważy się zapotrzebowanie na przechowywanie rozkazów i danych. Jeśli na przykład program wymaga ciągłego pobierania rozkazów i w małym stopniu korzysta z danych, dostępna pamięć podręczna zapełni się w większości rozkazami. 4 Oddzielna cache dla danych w takiej sytuacji pozostałaby nie wykorzystana. Drugą zaletą jest to, że upraszcza się układ procesora - łatwiej jest zrealizować w jego strukturze jeden bufor pamięci cache niż dwa. 2. Sposoby dołączania pamięci cache do procesora Układ cache jest w obecnych procesorach ściśle związany z ich strukturą - właściwie poziom L1 fizycznie stanowi integralną część mikroprocesora. Jednak aby łatwiej było przedstawić zasadę działania i sposoby dostępu procesora do pamięci podręcznej, ta część programu traktuje je ją jako oddzielny blok logiczny, dołączony do mikroprocesora, nie zajmując się jej fizycznym umiejscowieniem. Pisząc o sposobach dołączania, mamy więc na myśli sposób umieszczenia bloku cache na drodze procesor - pamięć. Obecnie stosuje się trzy podstawowe sposoby dostępu procesora do pamięci podręcznej. Look - Aside (dostęp bezpośredni) Procesor odwołuje się do cache wykorzystując magistralę pamięciową. Pamięć podręczna jest podłączona równolegle z pamięcią operacyjną RAM. W takim układzie częstotliwość pracy obu pamięci jest taka sama (komunikacja odbywa się po wspólnej magistrali), tylko czas dostępu dzięki szybkości cache może ulec skróceniu. Wykorzystanie tej samej magistrali nie jest korzystne. Jest ona blokowana przy każdym dostępie procesora do cache i nie może być w tym samym czasie udostępniona innym urządzeniom. Look - Throgh (dostęp "przez") Układ pamięci podręcznej pośredniczy w dostępie procesora do RAM. Procesor odwołuje się do układu cache, natomiast ten układ jest dołączony przez magistralę pamięciową do RAM. Backside (dostęp "z tyłu") UIkład pamięci podręcznej jest dołączony do procesora przez oddzielną magistralę nazywaną BSB (Back Side Bus). Druga magistrala FSB (Front Side Bus) łączy procesor z pamięcią główną. W tym układzie częstotliwości obu magistral są zupełnie niezależne. Możliwe jest też wykorzystanie magistrali FSB przez inne urządzenia zapisujące do pamięci RAM, w czasie gdy procesor komunikuje się z cache po BSB. 5 3. Budowa i organizacja pamięci podręcznej Pamięć cache jest zorganizowana w linijki (o rozmiarach 16 lub 32 bajty - 128 lub 256 bitów), w których są przechowywane informacje pobrane z RAM w postaci słów binarnych. Jedna linijka jest najmniejszą porcją informacji blokiem danych jaki układ cache wymienia z pamięcią operacyjną RAM. W różnych linijkach może być więc zapisana kopia zawartości odległych bloków z pamięci głównej. Wewnętrzną organizację cache najlepiej jest przedstawić posługując się praktycznym przykładem. Poniżej opisano budowę 32-bajtowej linijki pamięci podręcznej L1 w procesorze Pentium (rys. 3.1). W innych procesorach mogą być zastosowane trochę inne rozwiązania, jednak ogólna zasada organizacji cache w linijki pozostaje taka sama. Rys 3.1 Organizacja linijki w procesorze Pentium. Procesor Pentium posiada oddzielną pamięć podręczną poziomu L1 dla kodu programu (8kB) i oddzielną dla danych (8kB). Każda z nich jest podzielona na 256 linijek (256 x 32 B = 8kB). Aby zbiór takich linijek był dla procesora użyteczną strukturą, w której łatwo odszukać potrzebne dane, musi istnieć mechanizm zapisywania i kodowania dodatkowych informacji na temat każdej linijki. Przede wszystkim potrzebna jest informacja o tym, które fragmenty zawartości pamięci RAM są aktualnie skopiowane w poszczególnych linijkach. Jest to niezbędne, aby podczas żądania procesora odczytu z pamięci, kontroler cache mógł poprawnie określić czy dane są dostępne w linijkach, czy trzeba je sprowadzić z RAM. Wszystkie te informacje przechowuje się w katalogu cache (czasem określanym skrótem TAG-RAM). Jest on częścią pamięci podręcznej, która zawiera rekordy odpowiadające każdej linijce cache. Są w nich zakodowane informacje na temat danych aktualnie zapisanych w odpowiednich linijkach. Wartości poszczególnych pół tych rekordów mogą także wskazywać, że dana linijka jest wolna. W procesorze Pentium katalog jest podzielony na dwie równe części (opisywane jako katalog 1 i katalog 2), każda zawiera 128 rekordów. Jeden rekord odpowiada jednej linijce cache (razem jest więc 256 linijek). Z każdą parą rekordów o tym samym indeksie w katalogu 1 i katalogu 2 (0 do 127) jest dodatkowo związany jeden bit LRU. Budowę tak zorganizowanej pamięci cache przedstawia rysunek 3.2. Rys. 3.2 Organizacja wewnętrznej pamięci podręcznej procesora Pentium. Każdy rekord z katalogu składa się z następujących części: - znacznik (20 - bitowy) jest to dwadzieścia najstarszych bitów adresu wskazującego odwzorowany w skojarzonej z rekordem linijce obszar pamięci RAM - dwa bity MESI, które pozwalają zakodować cztery możliwe statusy danych w odpowiadającej rekordowi linijce. Określa się na tej podstawie czy linijki cache zawierają aktualną kopię danych z RAM, lub są wolne (dokładny opis wszystkich statusów znajduje się w dalszej części tego rozdziału). Strukturę adresu, który jednoznacznie wskazuje dane zgromadzone w tak zorganizowanej pamięci cache pokazuje rysunek 3.3. 6 Rys 3.3. Struktura adresu pamięci procesora Pentium. Podczas dostępu procesora do cache układy logiczne dzielą przekazywany przez niego adres na następujące części: - Znacznik (20 bitów) jest porównywany ze znacznikiem w katalogu cache (rys. 3.2). Na podstawie porównania znaczników określa się, czy potrzebne procesorowi dane są w linijce cache (określanie trafienia). - Wiersz (7 bitów) określa, która pozycja (indeks) w katalogu 1 i katalogu 2 rys 3.2. może odwzorowywać potrzebne dane. - Słowo (3 bity) pozwala na określenie, które z ośmiu 32-bitowych słów przechowywanych w linijce zawiera dane potrzebne procesorowi. - Bajt (2 bity) określa, który bajt w 32- bitowym słowie jest aktualnie potrzebny mikroprocesorowi. W wypadku braku trafienia, tak zbudowany adres wskazuje blok w pamięci operacyjnej RAM, zawierający potrzebne dane (dokładnie opisuje to rozdział omawiający sposoby odwzorowania). Zgodnie z zasadą działania cache, po odczytaniu zawartości bloku z RAM musi ona być zapisana do pamięci podręcznej. Na podstawie bitu LRU oraz części adresu (pola wiersz) jest wyznaczana linijka, do której można dokonać zapisu. Dokładnie te problemy opisują następne rozdziały. Skrót MESI używany do określania bitów w katalogu cache, został utworzony od pierwszych liter angielskich określeń czterech możliwych stanów linijki (Modified, Exclusive, Schared, Invalid). Zamieszczona niżej Tabela 1 przedstawia te stany oraz opisuje ich znaczenie w układzie cache procesora Pentium. Tabela 1. Stan linijki Modified zmodyfikowany Exclusive wyłączny Shared Wspólny Invalid nieważny Ważność Linijki ważna ważna ważna nieważna Aktualność kopii danych w RAM aieaktualna aktualna aktualna linijka nie zawiera ważnych danych Kopie w innych pamięciach podręcznych brak brak możliwe możliwe Zapis danych w tym bloku pamięci może być w cache może być w musi być w cache ze cache i w zmianą statusu RAM musi być w RAM Jak wynika z tabeli, procesor może odczytywać dane zapisane w linijkach cache, gdy ich status jest zmodyfikowany, wyłączny, lub wspólny. Wtedy na pewno linijka zawiera określony blok danych przepisanych z RAM. Stan nieważny oznacza, że linijka jest wolna i może być w razie potrzeby wypełniona danymi. Na podstawie stanu bitów MESI można także określić, czy dane w poszczególnych linijkach są także zapisane w innych poziomach pamięci podręcznej. Stan bitów MESI zmienia się również podczas modyfikacji danych w pamięci. Dotychczas omawiane były jedynie zagadnienia związane ze skróceniem czas dostępu do danych i rozkazów podczas ich odczytu. Jednak procesor nie tylko odczytuje z pamięci RAM. W trakcie wykonywania programu musi też tam zapisywać wyniki swoich operacji, modyfikować pewne zmienne i dane. Niektóre z nich mogą być w tym czasie skopiowane także do pamięci podręcznej. Wiąże się z tym konieczność zadbania o aktualność obu kopii danych. Do pamięci operacyjnej oprócz procesora mogą też mieć dostęp inne urządzenia. Mogą one zapisywać i odczytywać RAM bez udziału mikroprocesora. Jeśli dokonają zmiany słowa w RAM, którego kopia aktualnie jest przechowywana w cache, mogą spowodować, że procesor posiada w pamięci podręcznej nieaktualne dane. Również gdy procesor w trakcie 7 programu dokona zmiany danych tylko w cache, inne urządzenia mogą odczytać bezpośrednio z RAM stare błędne wartości. Należy zaznaczyć, że taka niespójność może występować tylko dla pamięci podręcznej danych. W oddzielnej pamięci podręcznej kodu programu procesor nie zapisuje swoich wyników, gdyż przechowuje ona jedynie dla niego instrukcje - kolejne rozkazy. Są różne rozwiązania problemu spójności danych stosowane w różnych procesorach, jednakże generalnie można wyróżnić dwa sposoby: Write Trougch (zapis jednoczesny) W tym sposobie każdy zapis danych wykonywany jest jednocześnie zarówno do pamięci głównej jak i do cache. Każdy zapis wymaga więc dostępu procesora do pamięci RAM. Również każdy bezpośredni zapis do RAM wykonywany przez inne urządzenia musi być monitorowany przez procesor. W ten sposób może on w razie potrzeby uaktualnić zawartość cache. Jest to więc najbardziej naturalny sposób, jednak generuje znaczny przepływ danych między pamięciami co powoduje duże opóźnienia. Wirte Back (zapis opóźniony) W tym trybie przy zapisie procesor aktualizuje tylko pamięć podręczną. Jednocześnie dla zmodyfikowanych linijek układ cache ustawia odpowiednie statusy (na bitach MESI - opisane w Tabeli 1). Zawartość pamięci głównej jest aktualizowana później na żądanie. Może ono być wyrażone przez instrukcję programową WBINVO (Write Back and Invalid Data Cache), lub specjalny sterujący sygnał sprzętowy. Aktualizacja jest też wyzwalana w wyniku braku trafienia w fazie odczytu z pamięci głównej. Gdy trzeba dokonać wymiany linijki cache, zawartość linijki usuwanej mającej status zmodyfikowany (zakodowany na bitach MESI), musi być koniecznie zapisana do RAM. Taka implementacja sposobu utrzymania spójności danych jest bardziej wydajna, minimalizuje ilość cyklów zapisu do pamięci głównej. Problemem jest jednak to, że bezpośredni dostęp zewnętrznych modułów wejścia-wyjścia do RAM także powoduje konieczność uaktualniania pamięci cache co może powodować pewne opóźnienia. W trakcie wykonywania programu może też nastąpić konieczność zapisu danych w obszarach RAM, które nie są aktualnie skopiowane do pamięci podręcznej (sytuacja zaznaczona w ostatnim wierszu i ostatniej kolumnie Tabeli 1). Niektóre procesory w takim wypadku mogą po prostu dokonywać zapisu w RAM z pominięciem układu cache. W nowszych generacjach procesorów stosuje się mechanizm, w którym zapis danych pociąga za sobą skopiowanie odpowiedniego bloku RAM do linijki cache, gdzie jest on modyfikowany. Dzięki temu ewentualny odczyt lub zapis tych samych danych w następnych rozkazach procesora może przebiegać już bez konieczności odwoływania się do RAM. Problemy związane z zapisem w RAM komplikują się jeszcze bardziej w układach wieloprocesorowych (opisanych dokładnie w rozdziale 3.2), gdzie kilka procesorów mających własne pamięci podręczne a w nich kopie niektórych danych, współpracuje z jednym układem pamięci głównej. Wykorzystuje się w nich specjalny protokół sprawdzania aktualności linijek cache w oparciu o bity MESI. 8 4. Sposoby odwzorowania pamięci RAM w cache, zalety i wady poszczególnych rozwiązań W małym buforze pamięci podręcznej (8- 512 kB) przechowywane są aktualnie używane przez procesor kopie zawartości fragmentów dużej pamięci RAM, która ma rozmiary rzędu 64 - 128 MB. Musi istnieć mechanizm, który pozwala na identyfikację fragmentów RAM umieszczonych w poszczególnych linijkach cache. Mapowanie bezpośrednie (Direct Mapped) - określane też jako odwzorowanie bezpośrednie Rys 4.1 Organizacja pamięci podręcznej o bezpośrednim odwzorowaniu W tym rozwiązaniu pamięć operacyjna RAM jest podzielona na bloki - grupy kolejnych adresów. Blok zawiera określoną liczbę słów i odpowiada rozmiarem linijce cache. W każdej linijce pamięci podręcznej mogą być przechowywane tylko określone bloki z RAM. Jak wynika z rysunku 4.1, adres przekazywany przez procesor jest interpretowany przez układy logiczne cache jako trzy pola (znacznik + wiersz + słowo). Przypisanie linijek do konkretnych fragmentów RAM jest jednoznacznie określone przez część adresu stanowiącą r- bitowe pole wiersz. Przykładowo w linijce 0 pamięci podręcznej mogą być przechowywane tylko te bloki z RAM, w adresach których r- bitowe pole wiersz jest równe 0. W linijce 1 tylko te, dla których pole wiersz zawiera liczbę 1 itd. Tak więc w zależności od rozmiarów cache (ilości dostępnych linijek) i wielkości odwzorowywanej pamięci głównej, dzieli się pamięć RAM na bloki, a te z kolei grupuje się przypisując do konkretnych linijek. Przy czym rozmiar linijki jest równy wielkości bloku, a ilość linijek wyznacza wielkość grupy bloków możliwych do odwzorowania w przeznaczonej dla nich linijce. Taką organizację określają ilości bitów z adresu przeznaczone dla kolejnych pól: znacznik, wiersz i słowo (oznaczonych na rysunku 4.1 literami s, r, w). Pole oznaczone jako znacznik (s- bitowy) - tworzą najstarsze bity adresu w RAM. W momencie kopiowania bloku pamięci operacyjnej do linijki cache, pole to jest zapisywane w odpowiadającym tej linijce rekordzie w katalogu cache. Najmłodsza część adresu - słowo (w - bitowe) oznacza numer słowa w konkretnej linijce lub w bloku RAM, do którego odwołuje się procesor. W momencie gdy procesor chce odczytać z pamięci określone dane wyznacza ich adres w RAM. Układy logiczne cache na podstawie pola wiersz wiersz (części tego adresu) określają, która linijka cache może te dane zawierać. Następnie porównywany jest odpowiadający jej znacznik z katalogu cache z najstarszą częścią adresu. W wypadku zgodności znaczników układ porównujący generuje sygnał trafienia (wtedy dane są szybko odczytywane z pamięci podręcznej), lub sygnał chybienia (wtedy następuje odczyt z RAM). Aby ułatwić użytkowi zrozumienie tego zagadnienia poniżej przedstawiono praktyczny przykład takiego odwzorowania: - pamięć RAM ma rozmiar 16 MB - pamięć podręczna ma rozmiar 64 kB - adres który jednoznacznie wyznacza wszystkie bajty w pamięci RAM musi być 24- bitowy (224 = 16 MB). - pamięć podręczna jest zorganizowana w linijki 16- bajtowe - musi więc zawierać 4 K linijek (4 K x 16 B = 64 kB). - pole wiersz wyznaczające linijkę jest 12- bitowe (212 = 4K). - ponieważ jest 16 bajtów w jednej linijce pole słowo musi być czterobitowe. - najstarsze osiem bitów adresu stanowi pole znacznik. 9 Strukturę adresu przedstawia poniższy rysunek. Rys 4.1.1. Przykład struktury adresu w pamięci przy odwzorowaniu bezpośrednim. Poniżej przedstawiono tabelę określającą przypisanie konkretnych bloków pamięci RAM do poszczególnych linijek cache. Z analizy tabeli wynika, że nigdy nie może się zdarzyć taka sytuacja aby w jednym wierszu pamięci cache mogły być odwzorowane bloki pamięci, których adres zawiera taką samą wartość w polu znacznik Podsumowując opis odwzorowania bezpośredniego można przedstawić następujące zalety tego rozwiązania: - prostota konstrukcji - łatwo jest zbudować układ porównujący i wydzielający poszczególne pola z adresu. - szybkość wyszukiwania informacji - sprawdzenie czy potrzebny blok jest w cache wymaga jednej operacji porównania (pola znacznik). Zasadniczą wadą jest mała efektywność działania takiego układu, szczególnie w sytuacji gdy procesor potrzebuje często dane z różnych bloków pamięci RAM, których odwzorowanie jest przewidziane w tej samej linijce. Za każdym razem kontroler cache musi usunąć z linijki pamięci podręcznej aktualny blok i wpisać inny, chociaż jest prawie pewne, że w następnym rozkazie będzie on potrzebny ponownie. Przykładowo w pamięci opisanej w powyższej tabeli taka sytuacja występuje przy odczytywaniu na przemian adresów 000000 i 010000 (zapis szesnastkowy). Zgodnie z zasadą odwzorowania bezpośredniego musi być wtedy na przemian wymieniana zawartość linijki 0 - wypełniana blokami 16 kolejnych bajtów o adresach 00000(0-F) i 01000(0-F). Pełna asocjacja (Fully Asociative) określana też jako odwzorowanie w pełni skojarzeniowe. 10 Rys 4.2 Organizacja pamięci podręcznej w pełni skojarzeniowej Procesor określa potrzebne mu dane przekazując ich adres, który układy logiczne cache dzielą na pole znacznik i słowo (zgodnie z rysunkiem 4.2). Układ cache sprawdza, czy w katalogu pamięci podręcznej znajduje się rekord, który zawiera znacznik identyczny z najstarszą częścią adresu. Jeśli taki rekord zostanie odnaleziony, odpowiadająca mu linijka zawiera potrzebne procesorowi dane (jest trafienie). W przeciwnym wypadku układ porównujący wysyła sygnał chybienia w pamięci podręcznej, co oznacza konieczność odczytu z RAM. Odczyt danych nie odwzorowanych z pamięci głównej wiąże się z ich zapisem do wolnej linijki, oraz skopiowaniem s- bitowego znacznika z adresu do odpowiadającego jej rekordu w katalogu cache. Taka organizacja pozwala na składowanie dowolnego bloku RAM w dowolnym miejscu pamięci podręcznej. Jest to bardzo elastyczna konstrukcja i umożliwia dużą skuteczność cache niezależną od ułożenia kodu programów i danych w RAM. Jednak ma bardzo dużą wadę - odszukiwanie informacji w pamięci podręcznej wymaga przeglądania dużego katalogu znaczników. Poniżej przedstawiono praktyczny przykład odwzorowania w pełni skojarzeniowego dla pamięci RAM 16 MB i pamięci cache 64 kB zorganizowanej w 16- B linijki. Strukturę adresu przedstawia poniższy rysunek. Rys 4.1.2 Przykład struktury adresu w pamięci przy odwzorowaniu skojarzeniowym Największym problemem w tym sposobie odwzorowania jest zbudowanie układu, który porównuje równolegle znaczniki z częścią adresu przekazanego przez procesor. Jak przedstawia schemat logiczny (rys. 4.2), układ ten musi generować sygnał trafienia w taki sposób, aby wiadomo było, która linijka zawiera potrzebne dane. Stwierdzenie braku trafienia w pamięci cache o rozmiarze 64 KB, zorganizowanej w 16 bajtowe linijki, wymaga od układu porównania 4096 znaczników (sprawdzenia 4 K linijek). Asocjacja zespołowa (Set Asociative) - określana też jako odwzorowanie sekcyjno-skojarzeniowe Jest to rozwiązanie najczęściej spotykane w obecnych procesorach. Łączy ono w sobie zalety dwóch poprzednich, opisanych wyżej sposobów odwzorowania. Po części zostało już przedstawione przy omawianiu organizacji pamięci podręcznej procesora Pentium w rozdziale 4.3. Rys 4.3 Dwudrożna sekcyjno-skojarzeniowa organizacja pamięci podręcznej Pamięć cache jest tu podzielona na sekcje (zespoły), w każdej sekcji znajduje się pewna liczba linijek. Na rysunku 4.3 schemat logiczny przedstawia rozwiązanie z dwoma linijkami w jednej sekcji takie jak zastosowano w procesorze Pentium . Tak więc, jedna sekcja to dwie linijki o tym samym numerze - indeksie z katalogu 1 i katalogu 2. Tak zorganizowaną pamięć określa się jako dwudrożną (lub dwukanałową). Często stosowane są też rozwiązania z czterema linijkami w jednej sekcji, wtedy pamięć podręczna jest czterokanałowa. Jak wynika z rysunku 4.3, adres przekazywany przez procesor jest rozkładany na trzy składniki: - Słowo - oznacza miejsce, pozycję danej w linijce cache lub bloku RAM (analogicznie jak w innych sposobach odwzorowania) - Sekcja - wyznacza jednoznacznie dwie linijki zgrupowane w jednej sekcji, w której dany blok może być odwzorowany. - Znacznik - jest wykorzystywany do określenia, czy potrzebne procesorowi dane są aktualnie w cache i w której z linijek 11 w sekcji są zapisane. W wypadku pamięci dwudrożnej w celu określenia trafienia należy dokonać dwóch porównań znaczników. Po wnikliwej analizie opisanych wcześniej dwóch poprzednich rozwiązań, zrozumienie zasady działania układu z rysunku 4.3 nie powinno sprawić trudności użytkownikowi programu wspomagającego nauczanie. Jeśli ten model (rys. 4.3) ograniczy się do jednej sekcji zawierającej wszystkie linijki cache, to będzie to odwzorowanie w pełni skojarzeniowe (wtedy część adresu oznaczona jako sekcja staje się zbędna, więcej bitów musi natomiast zawierać pole znacznik). Jeśli w układzie będzie występowało tyle sekcji ile linijek (jedna linijka w jednej sekcji) to będzie to odwzorowanie bezpośrednie - sekcja oznacza wtedy jednoznacznie numer wiersza (pole wiersz na rysunku 4.1 ) czyli linijki w cache. W odwzorowaniu sekcyjno-skojarzeniowym występuje problem podjęcia decyzji, którą z linijek w obrębie sekcji należy wykorzystać, w wypadku potrzeby zapisania w cache nowego bloku odczytanego z RAM. Na podstawie jego adresu przekazanego przez procesor (pola sekcja) określa się, w której sekcji blok może być odwzorowany. Dodatkowe informacje z katalogu cache (bity LRU) służą do wyznaczania linijki w tej sekcji, która jest wolna lub zawiera najmniej potrzebne dane - jest to omówione w rozdziale 4.6. Praktyczny przykład takiego sposobu odwzorowania jest taki sam jak opisywane przy omawianiu poprzednich układów: - 16 MB pamięci RAM jest odwzorowane w 64 KB cache - linijki 16 bajtowe (razem 4K linijek) - pamięć cache jest podzielona na katalog 1 (2 K linijek) i katalog 2 (2 K linijek) - 11- bitowe pole sekcja jednoznacznie wskazuje dwie linijki które ją tworzą (211=2048 - 2K) - w wyniku porównania 9-bitowego znacznika sprawdza się, która z linijek odwzorowuje dany blok z pamięci lub stwierdza się brak trafienia. Rys 4.1.3 Przykład struktury adresu przy odwzorowaniu sekcyjno-skojarzeniowym. Przykładowo w sekcji 0 tak zorganizowanej pamięci mogą być odwzorowane następujące bloki RAM: 00000(0-F), 00800(0-F), 01800(0-F), ........., FF800(0-F). Adres żadnego z przypisanych do tej samej sekcji bloków nie może mieć takich samych dziewięciu najstarszych bitów (takiego samego znacznika). 5. Określanie "trafienia" - czy dane o określonym adresie są w cache Zagadnienie to dotyczy określenia czy potrzebne w danym momencie procesorowi dane z pamięci operacyjnej, są aktualnie skopiowane w cache. Tak jak już wspomniano we wprowadzeniu do tematyki, stwierdzono że w pewnym momencie pracy procesora osiąga się stan, w którym dla kolejnych odniesień do pamięci bardzo duże jest prawdopodobieństwo (0,9), że dane będą w cache. Musi być jednak zastosowany jakiś mechanizm, który to sprawdzi określi trafienie lub stwierdzi jego brak. Sposoby określania trafienia zostały szeroko omówione przy przedstawianiu sposobów odwzorowania RAM w pamięci cache. Poniższa tabela stanowi podsumowanie zalet i wad sposobów odwzorowania i określania trafienia. stopień skuteczność układu skomplikowania cache w zalezności układu określającego od kodu programu trafienie jedno porównanie mała - w wypadku jednoznaczne - w znacznika z konieczności odczytu jednej linijce mogą bezpośrednie konkretnej linijki z na przemian bloków mały być tylko określone częścią adresu w przypisanych do tej bloki RAM samej linijki porównanie znaczników z duży - konieczność dowolne bloki w wszyskich duża niezależnie od skojarzeniowe porównywania dużej dowolnych linijkach niepustych linijek z kodu liczby znaczników częścią adresu w RAM określone grupy porównanie sekcyjnobloków mogą być znaczników z optymalna optymalny skojarzeniowe odwzorowane w linijek należących przypisanie bloków Odwzorowanie RAM do linijek cache określanie trafienia 12 grupie linijek (sekcji) do określonej sekcji 6. Decyzja o wymianie linijki W wypadku wykrycia braku trafienia w cache, procesor musi odczytać zawartość potrzebnej komórki pamięci z RAM. Jest bardzo prawdopodobne, że dane z tej komórki oraz innych leżących obok niej będą jeszcze potrzebne w czasie dalszego wykonywania programu. Dlatego procesor musi zapisać cały odczytany z RAM blok danych w pamięci podręcznej. Może się jednak tak zdarzyć, że wszystkie linijki z sekcji pamięci cache, do której blok powinien być skopiowany są już w tym momencie zapełnione ważnymi danymi. Trzeba wtedy określić, zawartość której linijki jest najmniej potrzebna i można zastąpić ją nowym blokiem danych z RAM. Istnieją różne algorytmy wyznaczania linijki, którą należy wymienić. Pierwszy z nich jest skrótowo określany jako LRU (Last Recently Used). Pozwala on wyznaczyć, zawartość której linijki w sekcji była najdłużej przechowywana w pamięci podręcznej i pozostawała niewykorzystywana. Informacje o tym zapisywane są w katalogu pamięci cache w postaci tzw. bitów LRU. W wypadku dwudrożnej pamięci sekcyjnoskojarzeniowej wystarczy właściwie jeden bit LRU. Jego stan określa wtedy, która z dwóch linijek w sekcji była ostatnio odczytywana przez procesor, a tym samym jest większe prawdopodobieństwo, że będzie jeszcze potrzebna. Rys. 6.1 Algorytm LRU . Rysunek 6.1 przedstawia algorytm wyznaczania linijki w pamięci zorganizowanej w cztery kanały (4 linijki w jednej sekcji). Przy takiej organizacji, w katalogu cache każdym czterem linijkom stanowiącym sekcję, są przypisane trzy bity LRU (B0-B2). W trakcie kolejnych odczytów danych z linijek w sekcji, bity LRU są ustawiane w następujący sposób: -B0 jest ustawiany gdy ostatni dostęp do kanału 0 lub kanału 1 okazał się trafieniem -B1 ustawia się jeśli trafiony był kanał 0 lub zeruje jeśli kanał 1 -B0 zeruje się jeśli trafienie było w kanale 2 lub 3 -B2 ustawia się jeśli trafienie było w kanale 2, zeruje jeśli w kanale 3 Algorytm LRU dodatkowo korzysta z informacji o ważności danych w poszczególnych linijkach, zapisanej w odpowiadających im rekordach w katalogu cache. Może to być dodatkowy bit skojarzony z każdą linijką (VAL=1 lub 0) lub informacja zakodowana na dwóch bitach MESI (opis w Tabeli 1). Innym algorytm stosowany w różnych układach jest oparty na kolejce FIFO ("pierwszy wchodzi, pierwszy wychodzi"). Polega on na zastępowaniu tej linijki w sekcji, która najdłużej pozostawała w pamięci, niezależnie od tego jak często była wykorzystywana. Ten sposób może być łatwo zrealizowany przy zastosowaniu buforowania cyklicznego. Kolejnym algorytmem jest LFU ("najrzadziej używana"). Zastępuje się tą linijkę w sekcji, do której było najmniej odniesień. Realizacja polega na skojarzeniu z każdą linijką cache licznika, który zlicza ilość jej odczytów dokonywanych przez procesor. Konieczne jest więc rozbudowanie rekordów przechowywanych w katalogu cache. W niektórych procesorach (np. AMD - K5) linijka, którą należy usunąć jest wyznaczana losowo. Efektywność układu cache zależy od wielu czynników przypadkowych (ułożenia kodu rozkazów i danych, ilości skoków w programie, 13 decyzji użytkownika obsługującego program ). Dlatego metoda losowa w wielu przypadkach daje nie gorsze wyniki niż LRU lub inne algorytmy. Eliminuje się w niej konieczność stosowania dodatkowych bitów, potrzebnych do przechowywania informacji o częstotliwości odniesień do poszczególnych linijek. 7. Problemy związane z cache w systemach wieloprocesorowych Aby skutecznie przybliżyć użytkownikowi tematykę związaną z pamięcią podręczną cache w systemach wieloprocesorowych, trzeba znów rozpocząć prezentację od zamieszczenia informacji na temat architektury takich systemów. Generalnie można wyróżnić dwa podstawowe podejścia przy projektowaniu układów wykorzystujących współpracę wielu procesorów. Są one bezpośrednio związane z zagadnieniem sposobu dostępu takiego układu do pamięci operacyjnej. Opracowywany program dydaktyczny właśnie tą tematyką się zajmuje. W pierwszym podejściu każdy z procesorów stanowi niezależną jednostkę obliczeniową i dysponuje własną pamięcią operacyjną. Mamy tu do czynienia z tak zwanym modelem pamięci rozproszonej. Komunikacja pomiędzy procesorami odbywa się w drodze wymiany pakietów informacji. Każdy procesor posiada także układ cache zwiększający jego efektywność przy odwoływaniu się do pamięci. Jest to więc równoległe zastosowanie wielu układów omawianych w poprzednich częściach programu wspomagającego nauczanie (opisanych w poprzednich rozdziałach). Rys 7.1. Układ wieloprocesorowy z pamięcią rozproszoną. Organizacja współpracy procesorów w układzie przedstawionym na rysunku 7.1 nie nastręcza zbyt wielu dodatkowych problemów w zakresie budowy układów pamięci podręcznej cache. Musi tu jednak istnieć rozbudowany mechanizm wymiany informacji między procesorami. Drugie podejście zakłada istnienie wspólnej pamięci systemowej dostępnej dla każdego procesora (pamięć dzielona). Tutaj pamięci podręczne mogą być różnie umiejscowione. Program prezentuje schematy, na których przedstawione są poszczególne możliwości: - każdy z procesorów posiada własny układ cache poziomu L1 i L2 - rozwiązanie to jest przedstawione na rysunku 7.2. - każdy z procesorów ma własny układ cache poziomu L1 a poziom L2 jest wspólnym buforem dla wszystkich procesorów - rysunek 7.3. 14 Rys 7.2. Układ wieloprocesorowy z wspólną pamięcią dzieloną i oddzielnymi buforami cache L1 i L2. Rys 7.3. Układ wieloprocesorowy z wspólną pamięcią dzieloną i wspólnym buforem cache L2. W układach z rysunków 7.2 i 7.3 pamięci cache są bardziej skomplikowane niż w systemie przedstawionym na rysunku 7.1. Analizując je, należy przyjąć, że każdy z procesorów ma możliwość odczytu i zapisu wspólnej pamięci dzielonej. Niejednokrotnie mogą w niej także zapisywać swoje dane urządzenia zewnętrzne, oznaczone na rysunkach jako wspólne zasoby. Każdy procesor przechowuje w swojej pamięci podręcznej kopię niektórych danych z RAM, aby były one dla niego łatwiej dostępne. Powstaje więc problem spójności tych danych. Musi istnieć taki mechanizm zapisu, który pozwala na uaktualnianie wszystkich kopii zawartości tej samej komórki pamięci głównej. Stosuje się tu różne rozwiązania programowe i sprzętowe. Rozwiązania programowe - polegają na analizie programów wykonywanych przez układy wieloprocesorowe już w fazie kompilacji i określaniu, które przetwarzane przez nie dane są narażone na niespójność. System operacyjny i sam program zapobiega kierowaniu takich danych do pamięci podręcznych - każdy procesor musi je odczytywać zawsze z pamięci głównej (pomijając cache) i tam zapisywać ich zmiany. Jest to bardzo nieefektywne rozwiązanie jeśli chodzi o czas dostępu. Bardziej zaawansowane sposoby polegają na analizie, w którym momencie dane wspólne procesorów mogą być niespójne i tylko wtedy nie są kierowane do cache. Stosując to rozwiązanie w praktyce, część pamięci głównej definiuje się jako wspólną (tak zwaną non-cachable memory), która nie może być kopiowana do pamięci podręcznych. Jeśli dane są narażone na niespójność zapisuje się je właśnie w tym obszarze pamięci głównej systemu. Protokoły katalogowe - to jedno z rozwiązań sprzętowych. Polega na wprowadzeniu tak zwanego katalogu pamięci i specjalnego sterownika, który zapisuje w nim aktualne informacje o kopiach danych przechowywanych przez różne procesory. Sterownik ma możliwość komunikacji ze wszystkimi układami, w wypadku zmiany zawartości pamięci, jeśli istnieje taka konieczność, wysyła do nich rozkazy aktualizacji. Sterownik centralny może jednak w pewnych warunkach zostać przeciążony co powoduje zwolnienie pracy systemu. Protokoły podglądania - stanowią kolejne rozwiązanie sprzętowe, w którym odpowiedzialność za utrzymanie spójności danych rozkłada się na wszystkie sterowniki pamięci podręcznych. Muszą one kontrolować co się dzieje na magistrali komunikacyjnej (podglądać) - w wypadku zmiany danych, których kopia jest w ich cache odpowiednio ją uaktualniać. Muszą tu także być podjęte odpowiednie środki, które zapobiegają przeciążeniu magistrali. 15 Stosuje się dwa rozwiązania protokołów podglądania. 1. Zapis z unieważnianiem - wszystkie dane z pamięci wspólnej są dostępne dla procesorów do odczytu - mogą je kopiować do swoich pamięci podręcznych cache. Jednak jeśli któryś chce zmienić zawartość pamięci, wysyła specjalne powiadomienie - unieważnia wszystkie kopie zmienianego bloku danych pobrane przez inne procesory. Procesor zapisujący musi "dostać" blok wspólnej pamięci na wyłączność na czas jego zmiany. Dopiero po zakończeniu zapisu wszystkie inne procesory mogą z niego korzystać ale teraz odczytają jego aktualną zawartość z pamięci głównej. Przy odwoływaniu się do niego układ cache nie wygeneruje sygnału trafienia, gdyż blok jest już unieważniony w wyniku zmodyfikowania przez inny procesor (taka informacja jest zapisywana w katalogu cache przez ustawienie odpowiedniego statusu linijki). 2. Zapis z aktualizacją - gdy jeden procesor zmienia jakiś blok pamięci, zostaje on dostarczony do wszystkich innych układów, dzięki czemu pamięci podręczne zawierające jego kopię mogą ją uaktualnić. Protokół unieważniania zapisu (pkt. 1) jest najczęściej używany w komercyjnych systemach wieloprocesorowych opartych na procesorach Pentium i PowerPC. Dlatego program dydaktyczny dokładniej go opisuje. Protokół ten jest oparty na kodowaniu odpowiednich statusów linijek cache zawierających dane z RAM. Kodowanie odbywa się przy wykorzystaniu bitów MESI z katalogu cache. Ich funkcję w układzie jednoprocesorowym opisywano już w rozdziale 4.1. Podobnie w systemach wieloprocesorowych, bity określają jeden z czterech możliwych statusów odpowiadającej im linijki: - Zmodyfikowany (modified) - dane w linijce zostały zmodyfikowane (różnią się od odpowiednika w RAM) i są dostępne tylko w tej pamięci podręcznej. - Wyłączny (exclusive) - dane w linijce są takie same jak w RAM i nie występują w innych pamięciach cache. - Wspólny (shared) - dane w linijce są takie same jak w RAM i mogą występować w innych pamięciach cache. - Nieważny (invalid) - linijka nie zawiera ważnych danych Od pierwszych liter angielskich słów utworzono skrót, którym się określa ten protokół - MESI. Pamięci podręczne, w których jest on zaimplementowany uwzględniają współistnienie wielu procesorów i umożliwiają przyspieszenie pobierania danych przez zastosowanie niezależnych układów cache. Graf przejść między poszczególnymi statusami linijki pamięci przedstawia rysunek 7.4. Oznaczenia: RH - trafienie odczytu RMS - chybienie odczytu, wspólny RME - chybienie odczytu, wyłączny WH - trafienie zapisu WM - chybienie zapisu SHR - śledź trafienie odczytu SHW - śledź trafienie zapisu lub odczytu z zamiarem modyfikacji Rys.7.4. Graf przejść protokołu MESI. Gdy w lokalnej pamięci podręcznej następuje chybienie odczytu (brak trafienia), procesor inicjuje odczyt bloku z pamięci głównej. Umieszcza przy tym sygnał na magistrali alarmujący wszystkie pozostałe procesory mające własne pamięci podręczne, żeby śledziły tą operację. Możliwe są wtedy następujące sytuacje: - Jeden z pozostałych procesorów ma kopię tego bloku danych w linijce o statusie wyłączny. Sygnalizuje, że dysponuje tym blokiem po czym zmienia status swojej kopii bloku na wspólny. Procesor inicjujący odczyt umieszcza blok danych w swojej pamięci podręcznej (w wolnej linijce) i nadaje linijce status wspólny (jest to przejście RMS - rys 7.4). - Jeden lub wiele procesorów sygnalizuje, że ma kopię bloku w stanie wspólnym w swojej cache. Wtedy procesor inicjujący też umieszcza blok w wolnej linijce swojej cache i nadaje mu status wspólny (RMS -rys 7.4). - Jeden z pozostałych procesorów ma zmodyfikowaną kopię bloku danych (status modified). Wtedy wysyła sygnał anulujący próbę odczytu danych z pamięci głównej, przejmuje sterowanie magistralą i zapisuje w niej swoją kopię bloku danych. Zmienia jednocześnie status zapisywanej linijki na wspólny (SHR - rys 7.4). Procesor inicjujący ponowi próbę odczytu wtedy będzie sytuacja opisana wyżej - odczyta aktualną kopię danych z pamięci głównej, nadając jej status wspólny. - Jeśli żaden z procesorów nie sygnalizuje posiadania kopii bloku danych w swojej cache, to procesor inicjujący odczytuje potrzebne mu dane umieszcza w swojej linijce cache nadając jej status wyłączny (RME - rys 7.4). Gdy następuje trafienie odczytu (RH - rys 7.4) to procesor pobiera potrzebne mu dane bez zmiany ich aktualnego statusu niezależnie czy w jego pamięci podręcznej są one w stanie zmodyfikowane, wspólne lub wyłączne. 16 Gdy procesor chce zapisać dane w bloku o kreślonym adresie a nie znajduje się on w jego cache, następuje sytuacja chybienia zapisu (WM - rys 7.4). Wtedy inicjowany jest odczyt bloku bez zamiaru modyfikacji. Tu znów możliwe są następujące systuacje: - Jedna z pozostałych pamięci może mieć kopię tego bloku w linijce o statusie zmodyfikowany. W takiej sytuacji procesor inicjujący oddaje sterowanie magistralą. Procesor posiadający zmodyfikowaną kopię danych zapisuje ją do pamięci głównej, jednocześnie ustawia ich status odpowiedniej linijki w swojej cache na nieważny, bo procesor inicjujący dokona za chwilę modyfikacji bloku (SHW - rys 7.4). Następnie procesor inicjujący ponawia odczyt bloku, który jest już ważny wyłącznie w jego pamięci podręcznej i może zostać zmodyfikowany. - Jeśli żadna inna pamięć podręczna nie ma zmodyfikowanej kopii bloku. Procesor inicjujący odczytuje blok i zapisuje go w swojej cache, po czym modyfikuje. W tym czasie jeśli inne pamięci mają blok w statusie wyłączny lub wspólny zmieniają jego stan na nieważny (SHW - rys 7.4). W wypadku potrzeby zapisu bloku, który jest w pamięci podręcznej procesora, następuje sytuacja trafienia zapisu (WH rys 7.4). Wtedy podejmowane są następne czynności w zależności od tego jaki jest status linijki cache przechowującej blok. - Wspólny- Procesor sygnalizuje zamiar modyfikacji bloku wspólnego. Wszystkie inne procesory posiadające jego kopię unieważniają ją. Po tej operacji blok jest w stanie wyłącznym - jego kopia jest ważna tylko w cache procesora inicjującego zapis. Dokonuje się zapisu i ustawia status bloku na zmodyfikowany. - Wyłączny- Procesor inicjujący ma już wyłączną kontrolę nad blokiem więc dokonuje jego modyfikacji. - Zmodyfikowany- Procesor inicjujący ma ciągle wyłączną kontrolę nad blokiem i kolejny raz go aktualizuje. 8. Praktyczne rozwiązania Ta część prezentacji nie wprowadza już nowych zagadnień. Jest to zestawienie i krótki opis rozwiązań w zakresie układu pamięci podręcznej cache spotykanych w popularnych procesorach. AMD K5, AMD K6, AMD K6 III, AMD K7, Intel Pentium, Pentium II, Pentium III,Pentium 4, ITANIUM Więcej szczegółowych infromacji na temat tych i innych procesorów mozna znaleźć na stronach producentów: www.amd.com.pl www.intel.com.pl AMD K5 oddzielna pamięć podręczna L1 dla kodu programu i danych obydwie są zorganizowane w linijki 32 bajtowe jednak najmniejsza porcja danych jednorazowo wymieniana z RAM to dwie takie linijki więc jeden adres TAG na dwie linijki (oddzielne bity MESI, itd.) obydwie 4 kanałowe - odwzorowanie sekcyjno-skojarzeniowe w obydwu występuje podwójny katalog pamięci (TAG) przechowujący odwzorowane adresy fizyczne i liniowe, co znacznie przyspiesza dostęp do cache rozmiar pamięci podręcznej kodu 16kB pamięć danych 8kB sposób zapisu write back, zaimplementowany protokół MESI AMD K6 oddzielna pamięć podręczna L1 dla kodu programu i danych obydwie są zorganizowane w linijki 32 bajtowe jednak najmniejsza porcja danych jednorazowo wymieniana z RAM to dwie takie linijki więc jeden adres TAG na dwie linijki (oddzielne bity MESI, itd.) obydwie 2 kanałowe - odwzorowanie sekcyjno-skojarzeniowe rozmiar pamięci podręcznej kodu 32kB i danych 32kB pamięć danych - zapis write back, protokół MESI algorytm wymiany linijki - LRU AMD K6 III 17 oddzielna pamięć podręczna L1 dla kodu programu i danych pamięć podręczna L2 256kB Write Back, 4 - kanałowa obydwie L1 są 2-kanałowe rozmiar pamięci podręcznej L1 kodu 32kB i danych 32kB pamięć L1 danych - zapis write back AMD K7 oddzielna pamięć podręczna L1 dla kodu programu i danych po 64 kB obydwie 2 kanałowe - odwzorowanie sekcyjno-skojarzeniowe rozmiar pamięci podręcznej L2 - 512 kB dostęp do L2 oddzielną magistralą BSB Intel Pentium po 8 kB pamięci L1 oddzielnie dla kodu i danych obydwie 4-kanałowe - odwzorowanie sekcyjno-skojarzeniowe zapis pamięci danych write back, zastosowany protokół MESI Intel Pentium II po 16 kB pamięci L1 oddzielnie dla kodu i danych obydwie 4-kanałowe - odwzorowanie sekcyjno-skojarzeniowe zapis pamięci danych - write back pamięć L2 512 kB - dostęp po magistrali BSB Intel Pentium III po 16 kB pamięci L1 oddzielnie dla kodu i danych obydwie 4-kanałowe - odwzorowanie sekcyjno-skojarzeniowe zapis pamięci danych - write back pamięć L2 512 kB wspólna (kodu i danych) - dostęp po magistrali BSB Pentium 4 po 16 kB pamięci L1 danych i kodu obydwie 4 kanłowe - asocjacja zespołowa, zorganizowane w 64- bajtowe linijki zapis pamięci danych write trough wspólna pamięć L2 (kodu i danych) 256 kB L2 zorganizowana w 128 B bajtowe linijki L2 odwzorowanie - asocjcja zespołowa 8-kanałowa 18 Intel ITANIUM po 16 kB pamięci L1 danych i kodu obydwie 4 kanłowe - asocjacja zespołowa, zorganizowane w 32 bajtowe linijki zapis pamięci danych write trough wspólna pamięć L2 (kodu i danych) 96 kB zapis pamięci L2 - write back L2 zorganizowana w 64 bajtowe linijki L2 odwzorowanie - asocjcja zespołowa 6-kanałowa w bezpośrednim sąsiedztwie procesora wystepuje też trzeci poziom pamięci podręcznej L3, rozmiar 2 lub 4MB, wspólny dla kodu i danych dostępny przy pomocy 128-bitowej magistrali BSB, zorganizowany wlinijki 64 bajtowe 9. Słownik pojęć i wyjanienia niektórych terminów poświęconych cache ALU Jednostka arytmetyczno-logiczna. Układ ALU realizuje operacje arytmetyczno-logiczne na wektorach informacji cyfrowej wprowadzonych z odpowiednich rejestrów. Czyli dokonuje sumowania, mnożenia, porównywania, odejmowania itd. liczb binarnych. Czas dostępu do pamięci Procesor aby uzyskać zawartość interesującej go komórki pamięci musi określić jej adres - słowo binarne wystawiane na magistrali adresowej. Po ustabilizowaniu się adresu generowany jest sygnał sterujący, po którym układ pamięci odczytuje zawartość komórki i wystawia na magistralę danych. Odczyt danych odbywa się z pewnym opóźnieniem. Czas opóźnienia jest określany jako czas dostępu do pamięci. Ilustruje to rysunek 9.1. Dla pamięci DRAM czas dostępu wynosi ok. 60 ns, dla SDRAM do 10 ns. 19 Rys 9.1 Czas dostępu do pamięci DRAM Pamięć dynamiczna, DRAM (angielskie dynamic memory, dynamic RAM), ulotna pamięć półprzewodnikowa o dostępie swobodnym, której bity są reprezentowane przez stan naładowania kondensatorów. Element pamięci dynamicznej składa się z kondensatora i tranzystora separującego, przy czym funkcję kondensatora może pełnić pojemność układu. W celu równoważenia pasożytniczych upływności kondensatory pamięci muszą być stale doładowywane, co nosi nazwę odświeżania pamięci i polega na cyklicznym odczytywaniu i ponownym zapisywaniu zawartości wszystkich komórek. Hasło opracowano na podstawie Słownika Encyklopedycznego - Informatyka” Wydawnictwa Europa. Autor Zdzisław Płoski. ISBN 83-87977-16-0. Rok wydania 1999. Pamięć operacyjna RAM (Random Access Memory) Pamięć operacyjna o dostępie swobodnym, można ją zapisywać i odczytywać wskazując miejsce (komórkę pamięci) przy pomocy słowa binarnego (adresu). RAM traci zawartość po odłączeniu zasilania. W komputerze do niej jest ładowany program wykonywany przez procesor. Często także służy do przechowywania danych oraz zapisania wyników wykonania programu - istnieje możliwość pobrania i zapisania danych przez procesor z innych urządzeń. Procesor - mikroprocesor ( CPU - Central Procesor Unit) Jest to układ przeznaczony do realizacji operacji arytmetyczno-logicznych na informacji cyfrowej (danych w postaci słów binarnych) wprowadzanych z jego otoczenia (pamięci operacyjnej lub urządzeń wejścia-wyjścia). Rodzaj wykonywanej operacji jest określony przez rozkazy (też słowa binarne), które procesor pobiera z pamięci operacyjnej. Wynik rozkazów może być zapisany w pamięci lub wysłany do urządzeń wejścia-wyjścia. RAM (Random Access Memory) Pamięć operacyjna o dostępie swobodnym, można ją zapisywać i odczytywać wskazując miejsce (komórkę pamięci) przy pomocy słowa binarnego (adresu). RAM traci zawartość po odłączeniu zasilania. ROM (Read Only Memory) Pamięć tylko do odczytu, nie jest zapisywana przez program, który z niej korzysta. Zawiera informacje raz zapisane w strukturze układu tylko do odczytu. Nie traci zawartości po odłączeniu zasilania. W przypadku procesora CPU w pamięci 20 ROM zawarte są informacje o sposobie dekodowania rozkazów - instrukcji programu. SRAM (Static RAM) Informacja zawarta w tej pamięci jest podtrzymywana przez nie przerwanie płynący prąd spoczynkowy. Dzięki temu nie występuje w niej konieczność odświeżania, co znacznie skaca czas dostępu. Socket 7 Gniazdo na płycie głównej komputera przeznaczone do montowania procesorów Pentium P54C, Pentium P55C (MMX), a także w większości przypadków, procesorów AMD K5/K6 i Cyrix M1/M2. Slot 1 Złącze krawędziowe stosowane w nowym standardzie montażu procesorów na płycie głównej. Wprowadzony przez firmę Intel przeznaczony jest do procesora Pentium II. 21