Performanta in sistemele multi-cpu si multi-core

Performanta in sistemele multi-cpu

  Articol pentru programatori in special. O prezentare foarte interesanta (engleza) se afla la final. O sa extrag cateva idei care mi se par interesante despre optimizare performanta din perspectiva hardware. Informatiile sunt relevante pentru achizitionarea procesoarelor si pentru "tuning" aplicatii software.


  Ce limiteaza viteza de procesare

  •   Procesoarele au atins o limita tehnologica pe la viteza de 3Ghz. Intr-o cuanta de timp atat de mica (1s/3.000.000.000), lumina (si orice alt semnal) poate parcurge doar aproximativ 10cm, ordinul de marime al unui procesor. Procesorul nu poate functiona mai repede pentru ca atat ii ia semnalului electric sa faca o tura prin procesor.
  • Procesorul reordoneaza executia unor instructiuni care nu depind unele de altele, pentru a putea executa mai multe operatii intr-o singura cuanta de timp, in paralel. De multe ori insa operatia urmatoare depinde de rezultatul celei precedente, deci nu se poate lansa a doua operatie pana nu se termina prima.
  •  Pentru a creste performanta, s-a inceput crearea procesoarelor multi-core, care incearca sa imparta procesarea pe mai multe unitati procesor. Din cauza interdependenti intre operatii, un program rulat pe 2 procesoare obtine de obicei mult sub dublul performante.
  • Un program care are doar un fir de executie (thread) obtine pe doua procesoare sub 10% in plus fata de a rula pe un singur procesor. Pe de alta parte se pot rula doua programe in paralel la aceeasi viteza cu a le rula pe fiecare singure pe un procesor. Este vorba de programe care consuma mult CPU fiecare, nu programe care stau deschise fara sa faca nimic.
  • Memoria RAM functioneaza cam de 100 ori mai lent decat procesorul, la fel cum Hard-Discul este mult mai lent fata de RAM. Pentru ca procesorul sa poata executa instructiuni la viteza maxima, exista cache-ul L1 care functioneaza aproximativ la viteza procesorului.
  • Cache-ul este o fereastra spre memorie. El incarca zona de memorie care este in executie, iar cat timp executia nu iese din acea zona procesorul poate functiona la viteza maxima.
  • Imediat ce executia iese din fereastra de cache, procesorul asteapta echivalentul a peste 100 de instructiuni sa se incarce o noua bucata din memorie in cache. De aceea un cache L1 mai mare inseamna o viteza mai mare de executie. Cache-ul L1 este foarte scump, deci mic. Unele procesoare au si cache L2 (de 10 ori mai lent si de 10 ori mai mare/ieftin).
  •  Exista si cache L3 care este doar de aproximativ 2 ori mai rapid decat memoria RAM, dar care ajuta sistemele multiprocesor sa nu congestioneze accesul la memoria RAM.
  •  Viteza de executie a unui program nu mai depinde atat de tare de frecventa procesorului, cat de numarul de accesari ale memoriei pe care nu le gaseste in cache. Algoritmi complexi incearca sa pre-incarce in cache memoria necesara in viitorul apropiat, totusi in multe cazuri nu reuseste.
  • Chiar si un 5% de "cache miss" (accesari ale memoriei in afara zonei cache) scade performanta drastic. In loc ca 100 operatii sa dureze 100 cuante timp, vor dura 95+5*100, deci aproape de 6 ori mai mult decat ar dura executate 100% in cache. S-ar putea ca un cache L1 mai mare sa ajute mai mult decat arata benchmark-urile, intrucat programele  benchmark sunt relativ mici, putand sa incapa complet in cache.
  • Viteza cu care ruleaza un program tine foarte mult de felul in care sunt aranjate instructiunile in memorie. Compilatoarele si masinile virtuale incearca sa ajute la aceasta operatie. Salturile in structuri mari de date si "indirectarile" produc totusi foarte multe salturi in afara zonei de memorie incarcate in cache, deci scad performanta.
  • Programarea orientata pe obiecte (C++) tinde sa genereze multe obiecte distribuite in zone de memorie ne-alaturate, deci un cod care iese mai des din cache. Programarea C foloseste in general functii mai mari, indirectari mai putine, zone de date mai compact distribuite, deci are sanse sa genereze un cod care foloseste cache-ul mai eficient.
  • Atunci cand procesorul asteapta dupa memoria RAM, procesorul va arata incarcat 100%, desi el de fapt ... sta degeaba. Este aproape inposibil de detectat impactul iesirilor din cache asupra vitezei, altfel decat prin teste cu diferite procesoare.


   Probleme de concurenta in sistemele multiprocesor

  •    Sistemele multiprocesor au cate un cache langa fiecare procesor. Sincronizarea cu memoria este un proces complex, care de multe ori blocheaza un procesor in asteptarea rezultatului altui procesor. Cache-urile "discuta" intre ele despre locatiile de memorie folosite in paralel
  • Din considerente de performanta, citirile din RAM nu se fac in aceeasi ordine cu scrierile, cel putin pe arhitectura x86/PC. Ce se garanteaza este doar ca fiecare procesor va vedea actualizat ce a scris el. Scrierile altui procesor pot ajunge in alta ordine via memorie.
  • Sa luam doua threaduri care pornesc ambele cu variabilele comune flag=0 si adresa=0. Primul thread seteaza adresa=123 si apoi semnaleaza asta punand flag=1. Este posibil ca un alt thread, pe alt procesor, sa vada flag=1 dar adresa=0 (nemodificata). In foarte scurt timp, si al doilea thread va putea vedea ca flag=1 si adresa=123. Totusi, daca al doilea thread verifica flag-ul exact dupa modificare, ajunge sa foloseasca adresa=0 (ne-initializata).
  • Pentru sincronizarea corecta a accesului la date folosite in comun de threaduri trebuie folosite lock-uri/mutexi, care elimina problema de reordonare pe multi-core. Primitivele de lock evalueaza si modifica atomic variabilele mutex, astfel ca un alt procesor nu poate citi in mijlocul operatiei. Din pacate aceasta sincronizare va bloca pentru scurt timp activitatea tuturor procesoarelor, si va inactiva informatia din cache-ul celorlalte procesoare, ceea ce duce la pierderi in performanta.

  Concluzie: devine tot mai importanta intelegerea arhitecturii hardware pentru crearea unui soft performant ca viteza. Programele viitorului trebuie sa poata fi executate pe fire de executie paralele si cu interdependente cat mai mici, pentru a putea scala pe tot mai multe cores. Frecventa procesoarelor nu va mai creste mult, doar vom avea din ce in ce mai multe procesoare impachetate intr-un cip.

A Crash Course in Modern Hardware:

http://www.infoq.com/presentations/click-crash-course-modern-hardware