Responsabili:
În urma parcurgerii acestui laborator, studentul va fi capabil:
În cadrul acestui articol vă vom prezenta câteva metode prin care programele se pot optimiza dacă utilizăm eficient operaţiile pe biţi. Un lucru foarte important de reținut este că nu întotdeauna putem folosi aceste operații pentru optimizare, iar laboratorul are ca scop ilustrarea câtorva exemple, pe care le puteți întâlni și pe viitor.
Veți învăța în anul 2 la Analiza Algoritmilor despre complexitatea unui algoritm. Vom considera momentan că un algoritm este mai rapid decât altul dacă are mai puțini pași (exemplu un for cu n = 100 de pași este mai rapid decât un for cu 1000 de pași).
În exemplul anterior performanța se referă la timp (dacă executăm mai puține instrucțiuni într-un program, ne așteptăm să se termine mai repede). Acest aspect va fi abordat pe larg la materiile AA și PA.
În acest laborator vom vorbi despre altă metrică de măsurare a performanței unui program, mai exact despre memoria folosită de un program.
De ce este important și acest aspect? Dacă din punct de vedere al timpului de execuție, sunt situații în care putem aștepta mai mult timp pentru a se termina programul, din punctul de vedere al memoriei folosite avem o limitare exactă. Un exemplu simplu este calculatorul nostru, care are 4GB/8GB/16GB. Dacă mașina noastră are X GB RAM, dintre care o parte importantă o ocupă sistemul de operare, asta înseamnă că într-un anumit program nu putem folosi o cantitate nelimitată de RAM (mai multe detalii la CN2, SO). Pentru simplitate, momentan presupunem că programul nostru nu poate rula pe o mașină cu X GB, dacă are nevoie de mai mult de X GB.
Dacă ajungem într-o astfel de situație în mod evident trebuie să schimbăm ceva, însă de multe ori putem păstra algoritmul și să facem câteva modificări în implementare, care exploatează anumite abilități ale limbajului C (ex. operații pe biți).
În laboratorul 2 au fost prezentate tipurile de date implicite din C și dimensiunea acestora.
Pentru a afla dimensiunea în bytes a unei variabile se poate folosi operatorul sizeof.
sizeof poate fi folosit și pentru măsurarea dimesiunii unui vector / matrice alocat(a) static.
Memoria totală folosită de un program poate fi calculată ca suma tuturor dimensiunilor ocupate de variabilele din program.
De obicei, ne interesează să știm ordinul de mărime al spațiului de memorie alocat, astfel, de cele mai multe ori, putem contoriza doar tablourile.
Vom descoperi mai multe în următoare laboratoare.
Operatorii limbajului C pot fi unari, binari sau ternari, fiecare având o precedenţă şi o asociativitate bine definite (vezi lab02).
În tabelul următor reamintim operatorii limbajului C care sunt folosiți la nivel de bit.
| Operator | Descriere | Asociativitate |
|---|---|---|
| ~ | Complement faţă de 1 pe biţi | dreapta-stânga |
| << si >> | Deplasare stânga/dreapta a biţilor | stânga-dreapta |
| & | ŞI pe biţi | stânga-dreapta |
| ^ | SAU-EXCLUSIV pe biţi | stânga-dreapta |
| | | SAU pe biţi | stânga-dreapta |
| &= și |= | Atribuire cu ŞI/SAU | dreapta-stânga |
| ^= | Atribuire cu SAU-EXCLUSIV | dreapta-stânga |
| <<= şi >>= | Atribuire cu deplasare de biţi | dreapta-stânga |
Dacă nu sunteți sigur de precendența unui operator, folosiți o pereche de paranteze rotunde în plus în expresia voastră! Nu exagerați cu parantezele, codul poate deveni ilizibil.
În C sunt definite doar shiftări logice. Acestea pot fi la stânga (<<) sau la dreapta (>>), reprezentând deplasarea in binar a cifrelor și completarea pozițiilor “golite” cu zerouri.
Se poate deduce următoarea relație: $ n << k = n * 2^k $.
Se poate deduce următoarea relație: $ n >> k = [n / 2^k ] $.
Având la dispoziție operațiile prezentate mai sus, putem răspunde la următoarele întrebări.
Pentru a răspunde ușor, pentru fiecare întrebare vom aplica o operație pe biți între n și o valoarea numită mască .
| $b_7$ | … | $b_{i+1}$ | $b_i$ | $b_{i-1}$ | … | $b_0$ | ||
|---|---|---|---|---|---|---|---|---|
| n | * | … | * | ? | * | … | * | |
| mask | 0 | … | 0 | 1 | 0 | … | 0 | op |
| x | 0 | … | 0 | ? | 0 | … | 0 |
| $b_7$ | … | $b_{i+1}$ | $b_i$ | $b_{i-1}$ | … | $b_0$ | ||
|---|---|---|---|---|---|---|---|---|
| n | $n_7$ | … | $n_{i+1}$ | * | $n_{i-1}$ | … | $n_0$ | |
| mask | $0 $ | … | $0 $ | $1 $ | $0 $ | … | $0 $ | op |
| n op mask | $n_7$ | … | $n_{i+1}$ | 1 | $n_{i-1}$ | … | $n_0$ |
| $b_7$ | … | $b_{i+1}$ | $b_i$ | $b_{i-1}$ | … | $b_0$ | ||
|---|---|---|---|---|---|---|---|---|
| n | $n_7$ | … | $n_{i+1}$ | * | $n_{i-1}$ | … | $n_0$ | |
| mask | $1 $ | … | $1 $ | $0 $ | $1 $ | … | $1 $ | op |
| n op mask | $n_7$ | … | $n_{i+1}$ | 0 | $n_{i-1}$ | … | $n_0$ |
Exercițiile pentru laborator se găsesc pe PPCLP Laborator12: Optimizarea programelor cu operații pe biți.