Table of Contents

Software Managed Cache

Concepte de baza despre cache

Cache-ul este o zona de stocare temporara care permite un acces rapid la date frecvent/recent accesate. Componentele si caracteristicile unui cache sunt:

Cand se fac accese la memoria principala, se cauta mai intai datele (sau codul) in cache. Din motive de performanta, cautarea se face doar in submultimea bine specificata unde ar putea fi stocata acea unitate de memorie, si anume in setul coresponzator de linii de cache. Daca vreunul din tag-urile din acea zona corespund adresei referite spunem ca avem un cache-hit; altfel avem un cache-miss.

Tipuri de cache-uri

Din punct de vedere al asociativitatii, cache-urile se impart in trei categorii:

Cache implementat software pentru un SPE

SPE-urile pot accesa direct, atat pentru cod cat si pentru date, doar memoria locala (LS), care are o capacitate de 256 KB. Toate accesele la memoria principala (sau alte zone de memorie mapate in spatiul de adresa curent, cum ar fi memoria video) se fac prin cereri DMA, fapt ce impune un efort suplimentar din partea programatorului, marind complexitatea programului. Implementarea unui cache in software poate:

Utilizare

Un program care utilizeaza sofware cache-ul oferit de SDK-ul Cell/BE trebuie sa includa fisierul header “cache-api.h” si o serie de defintii plasate inainte de directiva '#include' care declara cache-ul. Definitiile disponibile (nu toate sunt obligatorii) sunt:

declarare_cache.c
/* definitii obligatorii */
#define CACHE_NAME               my_cache
#define CACHED_TYPE              my_type_t
 
/* definitii optionale; daca o definitie lipseste se foloseste o valoare implicita */
#define CACHE_LOG2NWAY     2     /* 4-way */
#define CACHE_LOG2NSETS    3     /* 8 set-uri */
#define CACHE_TYPE         1     /* RW */
#define CACHELINE_LOG2SIZE 9     /* 512b */
#define CACHE_STATS              /* activare statistici */
 
/* in mod obligatoriu <cache-api.h> trebuie inclus dupa definirea variabilelor CACHE_* */
/* "cache-api.h" foloseste aceste definitii pentru a crea tipuri de date noi care implementeaza cache-ul */
#include <cache-api.h>

Pentru a defini mai multe cache-uri se redefinesc variabilele CACHE_* si se include din nou “cache-api.h”.

In tabelul de mai jos gasiti functiile care pot fi folosite pentru a interactiona cu sistemul de cache implementat software de SDK-ul Cell/BE ('name' reprezinta numele cache-ului cu care se lucreaza):

Instructiune Descriere
cache_rd(name, eaddr) Copiaza valoarea stocata la adresa eaddr in cache. Returneaza valoarea.
cache_wr(name, eaddr, val) Scrie in cache valoarea val. In memoria principala valoarea va fi stocata la adresa eaddr.
cache_flush(name) Forteaza scrierea tuturor datelor modificate (dirty) in cache inapoi in memoria principala.
cache_rd_x4(name, eaddr) Citeste un vector care contine 4 valori unsigned int de la adresa eaddr.
e_pr_stats(name) Afiseaza statistici despre cache.

Software cache-ul poate fi operat in mod sincron (folosind API-ul prezentat anterior) sau asincron (folosit un API diferit, bazat pe adresele din LS care aparțin cache-ului). In modul sincron, accesul la datele din cache se face doar prin adrese din spatiul de adrese al PPE-ului (adica din memoria principala sau eventual memoria video). Desi datele din memoria principala sunt aduse in LS-ul SPE-ului, folosind cache-ul implementat software, programatorul nu va folosi adresele locale pentru a accesa datele.

cache_sincron.c
#define CACHE_NAME         MY_CACHE      /* numele cache-ului */
#define CACHED_TYPE        int           /* tipul elementului de baza al cache-ului */
 
/* atribute optionale */
#define CACHE_TYPE         CACHE_TYPE_RW /* acces de scriere si citire */
#define CACHELINE_LOG2SIZE 11            /* 2^11 = lungimea unei linii de cache de 2048 bytes */
#define CACHE_LOG2NWAY     2             /* 2^2 = 4-way cache */
#define CACHE_LOG2NSETS    4             /* 2^4 = 16 seturi */
 
#include <cache-api.h>
 
int main(unsigned long long spu_id, unsigned long long parm){
        int a, b;
        unsigned eaddr_a, eaddr_b;
 
        /* initializeaza adresele efective cu o adresa din memoria principala */
        eaddr_a = parm;
        eaddr_b = parm + sizeof(int);
 
        /* citeste a si b din memoria principala (folosind adresa efectiva) */
        a = cache_rd(MY_CACHE, eaddr_a);
        b = cache_rd(MY_CACHE, eaddr_b);
 
        /* modifica valorile locale */
        a++;
        b++;
 
        /* scrie valorile modificate in cache. */
        /* nu se face imediat scriere in memoria principala (nu e un cache write-through) */
        /* se actualizeaza doar copiile din LS si se marcheaza ca "dirty" liniile corespunzatoare */
        cache_wr(MY_CACHE, eaddr_a, a);
        cache_wr(MY_CACHE, eaddr_b, b);
 
        /* scrie toate liniile modificate (dirty) inapoi in memoria principala */
        cache_flush(MY_CACHE);
        return 0;
}

# (3p) In memoria principala este definita o matrice ce descrie calea de la intrarea într-un labirint pana la o comoara. Drumul spre comoara este encodat în matrice ca un set de salturi de la niste coordonate (rand, coloana) la altele (un nod din drum contine ca informatie urmatorul nod la care trebuie sa se sara). Cautarea va incepe din celula (0, 0). Stim ca am ajuns la comoara cand trebuie sa sarim inapoi in celula (0, 0). Scheletul contine un program PPU care genereaza un labirint si trimite SPU-ului adresa catre începutul matricii precum si dimensiunea unei linii a labirintului. SPU-ul va trebui sa gaseasca coordonatele comorii si sa le scrie în prima celula a labirintului (0, 0). Programul PPU va verifica apoi daca in celula (0, 0) se gasesc coordonatele comorii.