Sunteți pe pagina 1din 208

CAPITOLUL 1

INTRODUCERE IN ALGORITMICA DE RETEA

Scopul algoritmicii de reţea este combaterea strangulărilor de reţea


cauzate de diferite implementări. Pe lângă tehnici specifice, sunt
cristalizate principiile creării de soluţii pentru evitarea strangulărilor în
internet, înglobate în ceea ce se numeste algoritmica de reţea sau teoria
algoritmilor de reţea. Ea asigură cititorului ustensile pentru crearea
diferitelor implementări, pentru anumite contexte particulare şi pentru
abordarea noilor strangulări care cu siguranţă vor apărea în lumea în
permanentă schimbare a reţelelor.
Algoritmica de reţea sau teoria algoritmilor de reţea trece dincolo de
crearea algoritmilor eficienţi pentru sarcinile reţelei, chiar dacă aceştia
au un rol important. In particular, teoria algoritmilor de reţea recunoaşte
importanţa majoră a abordării sistemelor interdisciplinare pentru
fluentizarea implementărilor de reţea. Algoritmica de reţea este o
abordare interdisciplinară deoarece cuprinde domenii ca arhitecturi şi
sisteme de operare (pentru creşterea vitezei serverului), proiectare
hardware (pentru creşterea vitezei componentelor de reţea cum sunt
ruterele), şi proiectarea de algoritmi (pentru proiectarea unor algoritmi
scalabili). Algoritmica de reţea este şi o abordare de sistem, fiind descris
un set de 15 principii şi utilizarea lor, care exploatează faptul că ruterele
şi serverele sunt sisteme, în care eficienţa poate fi obţinută prin
deplasarea funcţiilor în spaţiu şi timp între subsisteme.
Problemele algoritmicii de reţea sunt legate de performanţa
fundamentală legată de strangulările de reţea/bottlenecks. Soluţiile
adoptate de algoritmica de reţea generează un set de tehnici
fundamentale pentru tratarea acestor strangulări.

1.1. PROBLEMA: STRANGULĂRILE DE REŢEA


Principala problemă avută în vedere este realizarea unor reţele uşor de
folosit, utilizând hardware-ul la capacitatea maximă. Uşurinţa folosirii
vine de la folosirea unor abstractizări puternice de reţea, cum sunt
interfeţele socket şi expedierea/înaintarea bazată pe prefix a pachetelor.
Trebuie avut grijă însă, deoarece astfel de abstractizări pot duce la
scăderea performanţelor comparativ cu capacitatea transmisiilor pe
legături ca fibra optică. Pentru a putea înţelege această diferenţă de
performanţă vom examina două categorii fundamentale de dispozitive de
reţea, nodurile terminale/endnodes şi ruterele.

Strangulări la nodurile terminale


Nodurile terminale sunt punctele de final ale reţelei. Ele includ
calculatoarele personale şi staţiile de lucru precum şi serverele de mari
dimensiuni, furnizoare de servicii. Nodurile terminale sunt dedicate
calculelor, spre deosebire de reţele, şi de obicei sunt proiectate ca să poată
realiza calcule de uz general. Astfel strangulările nodurilor terminale sunt
de obicei rezultatul a două cauze: structura şi scala/dimensiunea.

1
• Structura: Pentru a putea rula orice cod, calculatoarele personale şi
serverele mari au de obicei un sistem de operare care mediază între
aplicaţii şi hardware. Pentru a uşura dezvoltarea software-ului, majoritatea
sistemelor de operare de mari dimensiuni sunt structurate cu grijă, ca un
software pe niveluri; pentru a proteja sistemul de operare de alte aplicaţii,
sistemele de operare implementează un set de mecanisme de protecţie; în
final, rutinele nucleului sistemului de operare, cum sunt planificatoarele si
procedurile de alocare, sunt scrise folosind mecanisme generale, orientate
spre o clasă de aplicaţii cât mai largă posibil. Din păcate, combinaţia între
software-ul pe niveluri, mecanismele de protecţie, şi generalitatea excesivă
poate duce la încetinirea semnificativă a software-ului reţelei, chiar şi cu
procesoarele cele mai rapide.
• Scala/dimensiunea: Apariţia serverelor mari oferind Web şi alte
servicii duce la apariţia altor probleme, de performanţă. In particular, un
server mare cum este un server de Web, va avea de obicei mii de clienţi
concurenţi. Multe sisteme de operare folosesc structuri de date ineficiente
şi algoritmi care au fost proiectaţi pentru o epocă în care numărul de
legături a fost mic.
In figura 1.1 sunt prezentate principalele strangulări ale nodurilor
terminale, împreună cu cauzele şi soluţiile lor. Prima strangulare are loc
deoarece structurile sistemului de operare convenţional impun copierea
pachetelor de date între domeniile protejate; situaţia se complică şi mai
mult în cazul serverelor de Web prin copieri similare pentru sistemul de
fişiere şi alte manevre, ca sumele ciclice de control, care verifică toate
pachetele de date. Se descriu un număr de tehnici de reducere a acestor
supraincarcari, menţinând ca scop abstractizările sistemului, ca protecţia
şi structura. A doua supraîncărcare majoră este cea generată de control,
cauzată de comutarea între firele de control (sau domeniile de protecţie) în
timpul procesării unui pachet.

Strangulare Cauze Exemple de soluţii


Copierea mai multor blocuri
de date fără intervenţia
Copierea Protecţia, structura
sistemului de operare(ex.
RDMA)
Implemenetarea protocoalelor
Comutarea la nivelul utilizatorului
Planificarea complexă
contextului Servere Web comandate de
evenimente
Canale directe de la aplicaţii
Apelurile de sistem Protecţia, structura
spre drivere (ex. VIA)
Scalarea la numarul
Contoarele de timp Contoare de timp ciclice
contoarelor
Scalarea la numărul de BPF şi Pathfinder/căutător de
Demultiplexarea
noduri terminale cale
Sumele de control/ Generalitatea
Calculul multibit
CRC-uri Scalarea la viteza legăturilor
Codul protocolului Generalitatea Predicţia antetului

Figura 1.1 Prezentarea strangulărilor nodurilor terminale

Aplicaţiile de reţea folosesc contoare de timp/temporizatoare pentru a


rezolva defecţiunile. Cu un număr mare de legături la un server
2
supraîncărcarea dată de contoarele de timp poate deveni mare. De
asemenea, mesajele de la reţea trebuie demultiplexate/orientate pentru a fi
recepţionate de aplicaţia corectă. In final, mai există alte câteva sarcini de
procesare obişnuite ale protocoalelor, cum sunt alocarea buffer-
elor/tampoanelor de memorie şi suma de control.
Strangulările în rutere
Deşi ne axăm pe ruterele pentru Internet, aproape toate tehnicile descrise
pot fi folosite la fel de bine şi în cazul altor echipamente de reţea, cum
sunt punţile/bridge, comutatoarele/switch, porţile/gateway, monitoarele şi
alte dispozitive de securitate, precum si pentru protocoale diferite de IP, ca
FiberChannel. De aceea, în continuare, ruterul este gândit ca un
“dispozitiv generic de interconectare a reţelei”. Spre deosebire de nodurile
terminale, acesta este un dispozitiv cu scop special dedicat lucrului cu
reţelele. Problemele fundamentale pe care le întâlneşte un ruter sunt legate
de dimensiune/scală şi servicii.
• Scala: Dispozitivele de reţea se confruntă cu două tipuri de scalare:
scalarea benzii şi scalarea populării. Scalarea benzii are loc deoarece
legăturile pe cale optică devin din ce în ce mai rapide, viteza legăturilor
crescând de la 1-Gbps la 40-Gbps, şi deoarece traficul in Internet creşte
continuu datorită unui set vast de aplicaţii noi. Scalarea populării are loc
deoarece tot mai multe noduri terminale sunt adăugate la Internet odată cu
conectarea online a tot mai multor întreprinderi.
• Serviciile: Nevoia de viteză şi dimensiune a crescut mult faţă de
industria reţelelor din anii ’80 şi ’90, pe măsură ce tot mai multe afaceri
deveneau online (de ex. Amazon), şi au fost create noi servicii online( de
ex. Ebay). Dar succesul Internetului impune o atenţie deosebită în
următoarea decadă, pentru a-l face mai eficient, şi a asigura garanţii de
performanţă, securitate şi siguranţă. La urma urmei, dacă producătorii (de
ex. Dell) vând mai mult online decât prin alte mijloace, este important să
se ofere garanţii ale reţelei – întârzierea în timpul congestiei, protecţia în
timpul atacurilor, şi disponibilitatea când apar defecţiuni. Găsirea unor
metode de implementare a acestor noi servicii la viteze mari va fi o mare
provocare pentru furnizorii de rutere în decada următoare.In figura 1.2
sunt prezentate principalele strangulări din rutere abordate aici, împreună
cu cauzele şi soluţiile lor.
In primul rând, toate dispozitivele de reţea expediază pachete destinaţiei
prin consultarea (looking up) unui tabel de înaintare(forwarding). Prin
consultarea celui mai simplu tabel de înaintare se găseşte o potrivire
exactă (exact match) cu adresa destinaţiei, ca de exemplu în cazul punţilor.
Sunt descrise scheme de căutare rapide şi scalabile, pentru potriviri
exacte. Din păcate, scalarea la dimensiunea populaţiei a făcut ca pentru
rutere căutările sa fie mult mai complexe. Pentru a trata populaţiile mari de
pe Internet, ruterele reţin o singură intrare numită prefix (analog cu codul
telefonic de zonă) pentru un grup mare de staţii. Astfel ruterele trebuie să
facă o căutare mult mai complexă pentru potrivirea celui mai lung
prefix/the longest-prefix-match. Sunt descrise soluţii la această problemă,
scalabile la viteze mari şi dimensiuni mari ale tabelelor.
Multe rutere oferă azi ceea ce este deseori numit diferenţierea
serviciului, prin care diferite pachete pot fi tratate diferit pentru a oferi
garanţii asupra serviciului şi a securităţii. Din păcate, acest lucru necesită o
3
formă şi mai complexă de căutare numită clasificarea pachetelor, unde
căutarea se bazează pe destinaţie, sursă, şi chiar serviciile pe care un
pachet le oferă.
Apoi, toate dispozitivele de reţea pot fi considerate comutatoare ce
şuntează pachetele, care vin de la un set de legături de intrare spre un set
de legături de ieşire. Problema fundamentală este crearea unui comutator
de viteză mare. Acest lucru este dificil, mai ales dacă ne gândim la
creşterea diferenţelor între vitezele optice şi electronice. Soluţia standard
este folosirea paralelismului printr-un comutator crossbar. Din păcate,
programarea unui comutator crossbar la viteze mari nu este uşoară, şi
paralelismul este limitat de fenomenul cunoscut ca HOL (head-of-line
blocking)/ blocare la începutul liniei. Mai rău, creşterea populaţiei şi
multiplexarea optică forţează furnizorii de comutatoare să creeze
comutatoare cu un număr mare de porturi, care exacerbează aceste
probleme.

Strangulare Cauze Exemple de soluţii


Căutări
Scalarea la viteza legăturii Dispersarea paralelă/hashing
exacte
Scalarea la viteza legăturii
Căutări de Noduri ordonate multibit
Scalarea la dimensiunea bazei de
prefix comprimate
date pentru prefixe
Algoritmi cu arbori de
Diferenţierea serviciilor (DiffServ)
Clasificarea decizie
Scalarea la viteza legăturii şi la
pachetelor Paralelism hardware
dimensiune
(CAM-uri)
Diferenţa între vitezele electronice
Comutator crossbar
Comutare şi cele optice
Cozi de ieşire virtuale
Blocare HOL
Tratarea FQ ponderat
corectă a Diferenţierea serviciilor Servire carusel cu deficit
cozilor Scalarea la viteza legăturii (Deficit round robin)
FQ (fair Scalarea la dimensiunea memoriei DiffServ,Nucleu fără stări
queueing) (Core Stateless)
Lăţimea de
Scalarea la viteza magistralelor Legături directe sigure prin
bandă
interne magistrale interne
internă
Măsurarea Scalarea la viteza legăturii DCU al firmei Juniper
Căutarea traseului cu filtre
Scalarea la numărul şi intensitatea
Securitatea Bloom
atacurilor
Extragerea „worm sigatures”

FIGURA 1.2 Prezentarea strangulărilor nodurilor terminale

In timp ce strangulările anterioare sunt cauzate de scalare, următoarea


strangulare este cauzată de nevoia de servicii noi. Altă strangulare care
devine o problemă în creştere este lăţimea de bandă a ruterului. Se
folosesc tehnici simple: conectarea directă prin magistrale interne/striping
şi legături chip-la-chip.
Dintre serviciile ce trebuie să facă parte dintr-un viitor Internet, bine
administrat, este includerea în rutere a unui suport pentru măsurări,
deoarece măsurările sunt cheia pentru a proiecta reţele care oferă garanţii.
Ruterele de azi asigură ceva suport pentru măsurări, contorizări si
înregistrări de NetFlow.

4
Suportul pentru securitate este parţial inclus deja în rutere. Datorită
sofisticării crescute, agresivităţii, şi ratei de atacuri asupra reţelei, este
esenţială implementarea securităţii în rutere sau dispozitive de reţea
specializate detectării/prevenirii intruziunilor. Dar dacă dispozitivul de
securitate nu poate ţine pasul cu legăturile de mare viteză, pot fi omise
informaţii vitale, necesare pentru detectarea unui atac.
Filtre Bloom: structură de date aleatorizată ingenios pentru reprezentarea concisă a unui
set/mulţime, care suportă interogări/queries despre apartenenţa aproximativă la
mulţime.Are o mare eficienţă spaţială, iar riscul/costul este de a detecta falsuri pozitive.

1.2. TEHNICI: TEORIA ALGORITMILOR DE REŢEA


Vor fi discutate tehnici specifice: pentru întreruperi, copiere, şi contoare
de timp circulare/timing wheels; pentru Pathfinder şi Sting; de ce unele
rutere sunt foarte încete; şi dacă serverele de Web pot să se scaleze la noile
dimensiuni. Ceea ce leagă împreuna toate aceste probleme este
algoritmica de reţea; algoritmica de retea admite importanţa primordială a
unei abordari sistemice, necesară pentru fluentizarea implementărilor de
reţea.
Internet-ul este un sistem compus din rutere şi legături; dar este mai
puţin vizibil faptul că fiecare dispozitiv de reţea, de la un Cisco GSR la un
server Web Apache, reprezintă sisteme, construite prin interconectarea
subsistemelor. De exemplu, un ruter nucleu este compus din carduri de
linie cu motoare de înaintare şi memorii de pachete, conectate printr-un
comutator crossbar. Comportamentul ruterului depinde de decizii luate la
diferite scale de timp, începând cu momentul producerii lor (când
parametrii impliciţi sunt stocaţi în NVRAM) până la momentul calculului
rutei (când ruterele calculează traseul) şi la momentul expedierii
pachetelor (când pachetele sunt trimise ruterelor adiacente).
Astfel o observaţie importantă în abordarea sistemică este că deseori
poate fi proiectat un subsistem eficient prin mutarea în spaţiu (spre alte
subsisteme) a câtorva din funcţiile sale, sau mutarea în timp (anterior sau
posterior momentului aparent în care funcţia este necesară). Intr-un anume
sens, cel care practică algoritmica de retea este un oportunist fără scrupule
care schimbă regulile jocului în orice moment, pentru a-l face mai uşor.
Singura condiţie este ca funcţiile oferite de sistemul global să continue să
satisfacă utilizatorul.
Ţinând cont de constrângerile impuse implementărilor reţelei la viteze
mari –sarcini din ce în ce mai complexe, sisteme tot mai mari, memorii
mici de viteză mare, şi un număr redus de accesări ale memoriei –trebuie
folosit orice artificiu, orice metodă sau resursă pentru a face faţă vitezei şi
dimensiunii din ce în ce mai mari ale Internetului. Proiectantul poate
încerca să plaseze problema în hardware, să schimbe ipotezele sistemului,
să proiecteze un algoritm nou – orice este necesar pentru a rezolva
problema.

1.2.1 Exemplu: depistarea pachetelor rele (evil packets)


Imaginati-va un monitor de reţea terminal/front-end, sau un sistem de
detectare a intruşilor, de la periferia unei reţele de intreprindere, ce vrea să
marcheze pachetele de intrare suspecte, care ar putea conţine atacuri
asupra unor calculatoare interne. Un atac des întâlnit este inundarea

5
bufferului/depăşirea capacităţii bufferului, când codul maşinii C se
plasează în antetul de reţea F.
Daca staţia destinaţie alocă un buffer prea mic pentru câmpul F al
antetului şi nu are grijă să verifice depăşirea/inundarea, codul C poate
ajunge în stiva staţiei destinaţie. Cu un mic efort suplimentar, intrusul
poate face ca staţia destinaţie să execute codul rău C. C va prelua controlul
staţiei destinaţie. In figura 1.3 este reprezentat un astfel de atac, incorporat
într-un câmp familiar, Web URL (Uniform Resource Locator) destinaţie.
Cum poate monitorul detecta prezenţa unui URL suspect? O cale este să
observăm că URL-urile care conţin un cod dăunător sunt deseori prea
lungi (o verificare uşoară) şi deseori au o mare parte de caractere
neobişnuite pentru URL-uri, cum este #. Monitorul poate marca astfel de
pachete (cu o lungime prea mare sau cu prea multe caractere neobişnuite),
printr-o examinare amănunţită.
Dar implicaţiile acestei strategii asupra securităţii trebuie gândite atent.
De exemplu, pot exista programe inofensive, cum sunt scripturi CGI în
URLuri, ce duc pe piste false. Fără a intra în prea multe amănunte legate
de toate implicaţiile arhitecturale, să presupunem că aceasta a fost o
specificare trimisă arhitectului de chip-uri, de către arhitectul de
securitate.Vom folosi o problemă ca model, pentru a observa modul de
lucru al teoriei algoritmilor.Având de a face cu o astfel de specificare,
proiectantul de chip-uri poate folosi următoarele procese de proiectare,
care ilustrează unele principii ale algoritmicii de reţea. Procesul începe cu
proiectarea strawman şi rafinarea proiectarii, prin proiectarea unui
algoritm mai bun, relaxarea specificaţiilor şi exploatarea hardware-ului.
Cod dăunător

Sistem de AIM://depăşire # * # ! * # ...... *


detecţie al
intruşilor

FIGURA 1.3 Găsirea unui pachet dăunător, observand frecventa caracterelor nelistabile

Tabloul Tabloul
Prag Contor Cod dăunător
0 2% 5
AIM://depăşire # * # ! * # ...... *
• •
• •
# 1% 3
Incrementează
• •
• •

255

FIGURA 1.4 Soluţia strawman pentru detectarea unui pachet dăunător: se contorizează
fiecare caracter într-un tablou contor şi apoi în pasul final se compară valorile cu cele din
tabloul de praguri

6
1.2.2 Soluţia strawman
Verificarea lungimii totale este implementată direct, deci ne concentrăm
la verificarea unor caractere suspicioase predominante. Prima soluţie
strawman este ilustrată în figura 1.4. Chip-ul păstrează două tablouri, T şi
C, cu câte 256 de elemente fiecare, unul pentru fiecare valoare posibilă, pe
caractere de 8 biţi. Tabloul de praguri, T, conţine procentul acceptabil (ca
o fracţiune din lungimea totală a URLului) pentru fiecare caracter. Dacă
apariţiile unui caracter într-un URL trec peste prag, pachetul este marcat.
Fiecare caracter are un prag diferit.
Tabloul de contorizare C, în mijloc, conţine contorul curent C[i] pentru
fiecare caracter i posibil. Când chip-ul citeşte un nou caracter “i” in URL,
se incrementează C[i] cu 1. C[i] are iniţial valoarea 0 pentru toate valorile
lui i când un nou pachet este întâlnit. Procesul de incrementare începe
numai după ce chip-ul analizează antetul HTTP şi recunoaşte începutul
URL-ului.
In HTTP, sfârşitul URL-ului este reprezentat cu două caractere de rând
nou; aşa că lungimea unui URL se poate şti doar după ce s-a parcurs
întregul şir URL. Astfel, după ce a fost întâlnit sfârşitul URLului, chip-ul
realizează o ultimă parcurgere a tabloului C. Dacă C [ j ] ≥ L ⋅ T [ j ] pentru
orice j, unde L este lungimea URL-ului, pachetul va fi marcat.
Presupunem că pachetele vin cu viteză mare în monitor şi dorim să
terminăm procesarea pachetului înainte de sosirea următorului. Această
cerinţă, numită procesare la viteza liniei/wire speed processing, este des
întâlnită în reţele; se previne amânarea procesării chiar şi în ce mai
defavorabil caz. Pentru a îndeplini cerinţele de procesare la viteza liniei,
ideal chip-ul ar trebui să facă un număr mic şi constant de operaţii pentru
fiecare octet al URL-ului. Presupunem că pasul principal de incrementare
al contorului poate fi făcut pe durata recepţiei octetului. Dar, cele două
treceri prin tablou, pentru iniţializare si verificare a depăşirilor de prag, fac
această proiectare lentă. Dimensiunea minimă a unui pachet este de 40 de
octeţi, incluzând doar antetele de reţea. Adăugând 768 operaţii în plus (1
scriere şi 1 citire pentru fiecare element din C, şi 1 citire din T pentru
fiecare din cei 256 indici) modelul de proiectare devine irealizabil.

1.2.3 Gândirea algoritmică


Intuitiv, a doua trecere prin tablourile C şi T de la sfârşit pare o risipă.
De exemplu, este suficientă avertizarea când orice caracter depăşeşte
pragul. Atunci de ce să verificam toate caracterele? Acest lucru sugerează
să urmărim doar cel mai mare contor de caracter c; la sfârşit algoritmul
trebuie să verifice doar dacă c e deasupra pragului, ţinând cont de L
lungimea totală a URL-ului.

7
Tabloul Tabloul
Prag Contor Cod dăunător
0 2% 5
AIM://depasire # * # ! * # ...... *
• •
• •
# 1% 3
1) Incrementează
• • 3) Dacă C[i]/T[i] > Max, Max = C[i]/T[i]
• 2) Citire •

255

FIGURA 1.5 Evitarea buclei finale prin tabloul cu praguri, urmărind doar Max, cel mai
mare contor relativ la valoarea de prag, întâlnit până acum.

Metoda nu prea funcţionează. Un caracter nesuspicios cum este “e”


poate apărea destul de des, şi deci probabil că i se atribuie un prag ridicat.
Dar dacă ţinem evidenţa numai pentru “e”, 20 de apariţii de exemplu, s-ar
putea să nu mai ţinem evidenţa lui “#” cu 10 apariţii. Dacă pragul pentru
“#” este mult mai mic, algoritmul poate duce la un răspuns negativ fals:
chip-ul poate eşua la alarmarea despre un pachet care ar trebui marcat.
Contraexemplul sugerează următoarea corecţie. Chip-ul ţine într-un
registru evidenţa valorii maxime a contorului relativ la valoarea pragului.
Mai precis, chip-ul ţine evidenţa celei mai mari valori a contorului
relativizat, Max, corespunzătoare unui caracter k oarecare, astfel încât
C [ k ] / T [ k ] = Max este cel mai mare dintre toate caracterele contorizate
până acum. Dacă un caracter nou i este citit, chip-ul incrementează C [i ] .
Dacă C [i ] / T [i ] > Max , atunci chip-ul înlocuieşte valoarea curentă a lui
Max memorată, cu C [i ] / T [i ] . La sfârşitul procesării URL-ului, chip-ul
ne alertează dacă Max ≥ L .
Iată de ce această soluţie e bună: dacă Max = C [ k ] / T [ k ] ≥ L , evident
că pachetul trebuie marcat, deoarece caracterul k depăşeşte pragul. Pe de
altă parte, dacă C [ k ] / T [ k ] < L , atunci pentru orice i, avem
C [i ] / T [i ] ≤ C [ k ] / T [ k ] < L . Astfel dacă Max cade sub prag, atunci nici
un caracter nu va trece peste prag. Astfel nu pot exista răspunsuri false
negative (vezi fig. 1.5).

1.2.4 Rafinarea algoritmului: exploatarea hardware-ului


Noul algoritm a eliminat bucla de final dar încă trebuie să realizeze
operaţia de împărţire în timpul procesării fiecărui octet. Logica împărţirii
este ceva mai complicată şi trebuie evitată dacă se poate– dar cum?
Revenind la specificaţii şi scopul lor, probabil că pragurile nu trebuie să
fie numere reale exacte în virgulă flotantă. Puţin probabil ca proiectantul
care deduce pragurile să poată estima precis valoarea; probabil că 2.78%
va fi aproximat cu 3% fără a afecta prea tare securitatea. Atunci de ce să

8
nu aproximăm pragul cu o putere a lui 2, în loc să folosim valoarea exactă
a pragului respectiv? Astfel dacă pragul este 1/29, de ce să nu îl
aproximăm cu 1/32?

Tabloul Prag
plus Contor Cod dăunător
0 2% 5
AIM://supraincarcare # * # ! * # ...... *
• •
• •
# 1% 3
1) Citeşte un cuvânt lung
• • 2) Compară şi marchează dacă e necesar
• • 3) Scrie valoarea incrementată

255

FIGURA 1.6 Folosirea unui cuvânt lung şi un tablou compus pentru a combina 2 citiri
într-una

Schimbarea specificaţiei în felul acesta necesită o negociere cu arhitectul


sistemului. Presupunem că arhitectul este de acord cu propunerea. Un prag
ca 1/32 poate fi codat compact ca o putere a lui 2 – ex. 5. Această valoare
de prag deplasată/shift poate fi memorată în tabloul de praguri, în locul
unei fracţii.
Când este întâlnit un caracter j, chip-ul incrementează C [ j ] ca de obicei
şi-l deplasează apoi spre stânga– împărţirea cu 1/ x este înlocuită cu
înmulţirea lui x cu pragul indicat. Dacă valoarea deplasată e mai mare
decât ultima valoare Max stocată, chip-ul înlocuieşte vechea valoare cu
noua valoare şi continuă.
Astfel logica necesară pentru implementarea procesării unui octet constă
dintr-o deplasare şi o comparaţie. E necesar doar un registru pentru
memorarea valorii Max. Dar e necesară o citire a tabloului Prag (pentru a
citi valoarea deplasată), o citire a tabloului Contor (pentru a citi vechiul
contor) şi o scriere in tabloul Contor (pentru a reînscrie valoarea
incrementată).
Citirea memoriei durează acum 1-2nsec chiar şi pentru cele mai rapide
memorii pe chip, dar poate dura până la 10nsec pentru memoriile mai lente
– deci e mai lentă ca logica. Întârzierile porţilor sunt de ordinul
picosecundelor, şi logica de deplasare nu necesită prea multe porţi. Astfel,
strangularea de procesare este dată de numărul de accesări ale memoriei.
Implementarea chip-ului poate combina cele două citiri ale memoriei
într-o singură citire, reunind cele două tablouri Prag şi Contor într-unul
singur (fig.1.6). Ideea este de a face cuvintele din memorie destul de lungi
pentru a conţine şi contorul (15 biţi pentru a manevra pachete cu lungimea
32K) şi pragul (care depinde de precizia necesară, nu mai mult de 14 biţi).
Astfel cele două câmpuri pot fi uşor combinate într-un cuvânt mai mare de
29 biţi. In practică, hardware-ul poate manevra cuvinte mult mai mari, de
până la 1000 biţi. De altfel trebuie avut în vedere că extragerea celor două
câmpuri dintr-un singur cuvânt, o sarcină laborioasă prin software, este

9
banală prin hardware, prin cablarea adecvată a firelor între registre, sau
prin folosirea multiplexoarelor.

1.2.5 Ştergerea
Deşi bucla terminală a fost eliminată, nu a fost rezolvată problema
delicată a buclei de iniţializare. Pentru a trata această problemă, chip-ul are
la dispoziţie un timp de iniţializare, dupa analiza URL-lui pachetului
curent şi înainte de tratarea URL-lui următorului pachet.
Din păcate, dimensiunea minimă a pachetelor poate fi mică, de până la
50 bytes, inclusiv antetul HTTP. Presupunând că sunt 40 bytes non-URL
şi 10 bytes URL, iniţializarea unei zone de 256 de bytes nu poate fi
făcută fără un cost suplimentar de 256/40 =6 operaţii per byte, necesare
prelucrării unui URL. Ca şi la bucla de procesare a URL-lui, fiecare
iniţializare presupune o citire şi o scriere a zonei comasate.
O metodă este amânarea lucrului până nu e absolut necesar, în speranţa
că nu va mai fi necesar. De fapt, chip-ul nu trebuie să iniţializeze C [i ]
până când caracterul i e accesat prima dată în pachetul următor. Dar cum
poate să ştie chip-ul că accesează caracterul i pentru prima dată? Pentru a
implementa evaluarea relaxată, fiecare cuvânt de memorie, reprezentând o
intrare în zona comasată, trebuie extins pentru a include G [i ] , numărul
generaţiei. Acest număr de generaţie, pe 3 biţi, poate fi văzut ca un contor,
care numară de câte ori e întâlnit pachetul, cu excepţia faptului că e limitat
la trei biţi. Astfel, chip-ul are un registru suplimentar g pentru fiecare i, pe
lângă G [i ] , cu 3 biţi; registrul g este incrementat modulo 8 la fiecare
pachet intâlnit. In plus, de câte ori C [i ] este actualizat, chip-ul
actualizează şi G [i ] pentru a reflecta valoarea curentă a lui g.
Având numerele generaţiei, chip-ul nu mai trebuie să iniţializeze zona de
numărare dupa ce pachetul curent a fost procesat. Totuşi, să considerăm
cazul unui pachet cu numărul generaţiei h, care conţine un caracter i în
URL-ul său. Când chip-ul întâlneşte caracterul i în timpul procesării
pachetului, citeşte C [i ] şi G [i ] din zona de numărare. Dacă G [i ] ≠ h
înseamnă că intrarea i a fost accesată anterior de un pachet şi nu a fost
iniţializată în consecinţă. Astfel, logica va repune pe 1 valoarea lui
C [i ] (iniţializare plus incrementare) şi va pune pe h valoarea lui G [i ] (fig.
1.7).
Apare imediat o obiecţie.Deoarece numărul generaţiei are doar 3 biţi, iar
valoarea lui g este ciclică, pot apare erori. Dacă G [i ] = 5 iar intrarea i nu
este accesată până când nu au trecut deja 8 pachete, valoarea curentă a lui
g este 8. Dacă următorul pachet conţine caracterul i, C [i ] nu va mai fi
iniţializat şi contorul va număra eronat valoarea de ordine a caracterului i
din pachetul curent, cu valoarea din urmă cu opt pachete.
Chip-ul poate evita această eroare printr-o buclă de filtrare separată, care
citeşte tabelul şi iniţializează toate contoarele cu numerele de generaţie
învechite. Pentru corectitudine, chip-ul trebuie să realizeze o scanare
completă a zonei pentru toate cele opt pachete procesate. Incetinirea
existentă, de 40 non-URL octeti per pachet, garantează o încetinire de 320
10
bytes non-URL pentru cele opt pachete, ceea ce este suficient pentru a
iniţializa o zonă de 256 de elemente, utilizând o citire şi o scriere per octet
indiferent de tipul de octet, URL sau non-URL. Se poate câştiga mai multă
întârziere dacă este nevoie, crescand numărul de biţi ai numărului
generaţiei, cu preţul unei uşoare creşteri a necesarului de memorare a
zonei.

Fig 1.7 Soluţia finala cu numere de generaţie pentru rafinarea buclei de iniţializare

In acest caz, chip-ul trebuie să conţină două stări, una de procesare a


biţilor aparţinând URL-lui şi una de procesare a biţilor non-URL. Când un
URL este complet procesat, chip-ul trece în starea de filtrare/scrub. Chip-
ul dispune de un alt registru, care indică intrarea s a următoarei zone de
filtrat. Dacă G [ s ] ≠ g , G [ s ] este resetat la valoarea g, iar C [ s ] este
iniţializat pe 0.
Deci utilizarea a 3 biti suplimentari, pentru numărul generaţiei pentru
fiecare intrare de zonă, a dus la reducerea ciclurilor de iniţializare cu preţul
creşterii zonei de memorare. Intrarea zonei comasate are acum doar 32 de
biţi, 15 biţi pentru contor, 14 biţi pentru valoarea de prag de comutare, şi 3
biţi pentru numărul generaţiei. Verificările adăugate iniţializării, necesare
în timpul procesării byte-lor URL nu cresc numărul de referiri la memorie
(strangulare) ci cresc puţin logica de procesare. In plus, mai e nevoie de
două registre pe chip pentru memorarea şirurilor g şi s.

1.2.6 Caracteristicile algoritmilor de reţea


Detectarea un pachet malefic pune în evidenţă trei aspecte importante:
a) Algoritmica reţelelor este un domeniu interdisciplinar. Datorită
vitezelor mari de procesare în reţele, proiectanul de rutere va fi presat să
nu utilizeze implementări hardware.Exemplul exploatează câteva
caracteristici ale hardware-ului. Astfel, se presupune că: sunt posibile, fără
dificultate, cuvinte lungi de diferite dimensiuni; deplasarea este mai uşoară
decât împărţirea; referirile la memorie conduc la strangulări; este fezabilă
implemetarea unor memorii rapide pe chip, cu zone de 256 de
elemente;este fezabilă adăugarea unor registre suplimentare; modificările
aduse logicii pentru a combina procesarea URL şi iniţializarea sunt uşor de
implementat.
Inţelegerea unor aspecte ale proiectării hardware poate ajuta, chiar pe un
programator software, să înţeleagă fezabilitatea diferitelor implemantări
hardware. Abordarea interdisciplinară poate conduce la o proiectare
optimă. Sunt prezentate câteva modele hardware care oferă posibilitatea
11
soluţionării şi rafinării unor probleme de implementare. De asemenea, sunt
prezentate modele simple de sisteme de operare, deoarece pentru
îmbunătăţirea performanţelor, terminalele (serverele web) şi clienţii
trebuie să înţeleagă problemele sistemelor de operare, după cum ruterele şi
dipozitivele de reţea implică o bună cunoaştere şi adaptare a hardware-
ului.
b) Algoritmica de reţea admite primordialitatea gândirii sistemice.
Specificaţiile au fost relaxate permiţând praguri aproximative, ca puteri ale
lui 2, ceea ce simplifică hardware-lui.Relaxarea specificaţiilor şi
translatarea activităţii de la un subsistem la altul este o tehnică obişnuită,
dar care nu este încurajată în sistemul educaţional din universităţile în care
fiecare domeniu este predat izolat de celelalte domenii. De aceea, în zilele
noastre, există cursuri de algoritmi, de sisteme de operare şi de reţele.
Acest fapt are tendinţa de a promova gândirea la nivel de “cutie neagră” în
loc de o gândire globală, sau sistemică.Exemplul a făcut aluzie la alte
tehnici cum ar fi “evaluarea relaxata” (lazy evaluation) şi compromisul
memorie pentru procesare cu scopul de a elimina zonele de
contorizare.Aşadar s-a încercat sintetizarea principiilor sistemelor utilizate
în algoritmică (15 principii), precum şi explicarea şi analiza
implementărilor realizate pe baza acestor principii.
c) Algoritmica de reţea poate beneficia de pe urma gândirii
algoritmice. Dşi gândirea la nivel de sistem este importantă pentru a
rafina de câte ori este posibil, o problemă, sunt multe situaţii în care
constrângerile impuse sistemelor nu permit eliminarea unor probleme. Ca
exemplu, dupa sublinierea importanţei gândirii algoritmice prin relaxarea
specificaţiilor, problema “ falsului pozitiv ” a condus la luarea în calcul a
contorului maxim relativ la valoarea sa de prag. Alt exemplu este
comutarea etichetelor/tag switching, când s-a încercat rafinarea căutării
IP/lookup prin comutarea etichetelor, deşi multe rutere au recurs la alţi
algoritmi eficienţi de căutare.
Deoarece modelele sunt diferite de modelele teoretice standard, deseori
nu e suficient să fie reutilizaţi orbeşte algoritmii existenţi. Ca exemplu,
necesitatea de a programa un comutator crossbar în 8ms conduce la luarea
în considerare a potrivirii maxime prin încercări, mai simplă decât
algoritmii complicaţi care duc la asocieri optimale.
Alt exemplu descrie cum implementarea BSD a procedurii de căutare
(lookup) reutilizează orbeşte o structură de date denumită proba Patricia,
ce utilizează un contor de omisiuni, pentru căutarea IP. Algoritmul rezultat
necesită o urmărire recursivă complexă. O simplă modificare, care
păstrează biţii omişi, elimină urmărirea recursivă. Dar acest lucru
presupune o pătrundere în cutia neagră (algoritmul) şi aplicării sale.
Algoritmul a fost mulţi ani considerat o capodoperă şi a fost implementat
în hardware, în multe rutere. Dar de fapt nu este decât o implementare
hardware a BSD Patricia tries with backtracking. E util de ştiut că o simplă
modificare a algoritmului poate simplifica hardware-ul substanţial.
In concluzie, folosirea fără discernământ critic a algoritmilor standard
poate duce la eşuarea perfecţionărilor, prin măsuri inadecvate (e.g., pentru
filtrele de pachete ca BPF, introducerea unui nou clasificator duce la timpi
mai mari decat timpii de căutare), datorită modelelor nepotrivite (ignorarea
liniilor cache în software şi a paralelismului în hardware) şi datorită
12
analizelor neadecvate (e.g., ordinul complexităţii ascunde constant factorii
esenţiali ce asigură înaintarea rapidă a pachetelor pe liniile fizice).
Aşadar, un alt scop este de a urmări implementări care pătrund în
esenţa algoritmilor, şi folosirea tehnicilor fundamentale algoritmice (ca de
exemplu tehnica divide et impera/dezbină şi cucereşte, şi aleatorismul).
Acestea conduc la următoarele concluzii.
Definiţie: Algoritmica de reţea este folosirea abordării sistemice
interdisciplinare, combinată cu gândirea algoritmică, pentru proiectarea
implementărilor rapide ale sarcinilor de procesare în reţea, la nivelul
serverelor, ruterelor şi a altor dispozitive de reţea.

Obiectiv Motiv Subiecte simple


Modele Inţelegerea modelelor Tehnologii de memorii :întreţesere,
simple de sisteme de combinare SRAM/DRAM
operare,hardware,reţele
Strategii Inţelegerea principiilor Pasarea indicaţiilor, evaluarea
sistemelor pentru evitarea relaxată, suplimentarea stărilor,
strangulărilor exploatarea localizării
Probleme Practică aplicarea Proiectarea unui motor de
principiilor la probleme verificare(lookup) pentru monitorul
simple reţelei
Fig.1.8 Algoritmica de reţea (cu modele, strategii şi probleme simple).

1.3 Exerciţiu: Implementarea Chi-Square. Statistica chi-square se


foloseşte pentru a vedea dacă frecvenţele caracterelor observate sunt
neobişnuit de diferite (comparativ cu variaţia aleatoare normală) faţă de
frecvenţele estimate ale caracterelor. Acesta este un test mai sofisticat
decât un simplu detector de prag. Se presupune că pragurile reprezintă
frecvenţele aşteptate. Statistica se calculează pentru toate valorile
caracterelelor i prin:
( Frecventa estimata [i ] − Frecventa observata [i ])
2

Frecventa estimata [i ]
Chip-ul ar trebui să semnalizeze dacă rezultatul final depăşeşte un
anumit prag (ca exemplu, o valoare de 14,2 semnifică că sunt 1,4% şanse
ca diferenţa să se datoreze unor variaţii aleatoare). Găsiţi o cale de
implementare eficientă a acestei metode, presupunând că lungimea este
cunoscută doar la sfârşit.

13
CAPITOLUL 2

MODELE DE IMPLEMENTARE A RETELELOR


Pentru a îmbunătăţi performanţele nodurilor terminale şi a
ruterelor, un implementator trebuie să cunoască regulile jocului. O
dificultate centrală este aceea că algoritmii de reţea cuprind patru domenii
diferite: protoacoale, arhitecturi hardware, siteme de operare şi algoritmi.
Inovaţiile în domeniul reţelelor apar când experţii din diferitele domenii
conlucrează pentru a obţine soluţii sinergice. Poate oare un proiectant
hardware să înţeleagă problemele legate de protocoale, iar un proiectant de
algoritmi să înţeleagă problemele hardware, fără un studiu temeinic?
Se începe cu modele simple, capabile să explice şi să anticipeze, fără
detalii inutile. Modelul ar trebui să ofere posibilitatea ca o persoană
creativă din afara unui domeniu să poată crea proiecte, ce pot fi verificate
de un expert din interiorul domeniului. De exemplu, un proiectant
hardware de chip-uri ar trebui să fie capabil să sugereze schimbări de
software în driver-ul chip-lui, iar un teoretician din domeniul
calculatoarelor ar trebui să fie în stare să viseze la algoritmi de asociere
pentru switch-uri.

2.1 Protocoale
Secţiunea 2.1.1 descrie protocolul de transport TCP şi protocolul de
rutare IP. Aceste două exemple sunt utilizate pentru a asigura un model
abstract de protocol şi funcţiile acestuia din &2.1.2. Secţiunea 2.1.3 se
încheie cu ipoteze asupra performanţelor reţelelor. Cititorii familiarizaţi cu
TCP/IP ar putea dori să sară peste &2.1.2.

2.1.1 Protocoale de transport şi dirijare


Aplicaţiile se bazează pe o transmitere fiabilă, sarcină ce revine
protocolului de transport, aşa cum este protocolul TCP (Transport Control
Protocol). Sarcina protocolului TCP este de a asigura transmiterea şi
recepţionarea aplicaţiilor, ca şi cum ar fi două cozi de aşteptare separate,
una în fiecare direcţie, chiar dacă emiţătorul şi receptorul sunt separate de
o reţea cu pierderi. Aşadar, orice ar scrie aplicaţia emiţătoare în coada sa
de aşteptare locală TCP, ar trebui să apară, în aceeaşi ordine, în coada de
aşteptare locală TCP a receptorului şi viceversa. Protocolul TCP
implementează acest mecanism prin fragmentarea cozii de aşteptare a
datelor aplicaţiei în segmente şi transmiterea fiecărui segment până când s-
a recepţionat confirmarea.
Dacă aplicaţia este, de exemplu, o videoconferinţă care nu necesită
neapărat o garanţie a fiabilităţii, se poate alege protocolul denumit UDP
(User Datagram Protocol) în locul TCP-lui. Spre deosebire de TCP, UDP
nu necesită aşteptarea unor confirmări sau retransmisii deoarece nu
garantează transmisia fiabilă.
Protocoalele de transport cum sunt TCP şi UDP lucrează prin
transmiterea unui segment de la un nod emiţător la un nod receptor prin
intermediul internetului. Sarcina actuală de transmitere a unui segment
revenind protocolului internet de rutare IP.

14
Rutarea în Internet este impărţită în două părţi “forwarding and routing”,
înaintare/dirijare. Inaintarea reprezintă procesul prin care pachetele sunt
transmise între sursă şi destinaţie trecând prin ruterele intermediare.
Pachetul reprezintă un segment TCP care are ataşat un antet de rutare care
conţine adresele internet destinaţie.
In timp ce procesul de înaintare trebuie să se realizeze la viteze mari,
tabelele de rutare din cadrul fiecărui ruter trebuie construite de către un
protocol de rutare, mai ales în cazul unor modificări de topologie cum este
cazul unor legături întrerupte. Sunt câteva protocoale de rutare mai
cunoscute, cum este protocolul bazat pe vectorii distanţă (RIP), sau pe
starea legăturii (BGP).

2.1.2 Model abstract de protocol


Un protocol reprezintă o stare a maşinii pentru toate nodurile participante
la protocol, împreună cu interfeţele şi mesajele. Un model pentru un
protocol este prezentat în figura 2.1. Specificaţiile trebuie să descrie cum
diagrama de stări a maşinii evoluează şi răspunde la interfeţele utilizator,
la mesajele recepţionate şi la contorul de evenimente. De exemplu, când o
aplicaţie face o cerere de conectare, diagrama de stări a maşinii TCP
emiţătoare se iniţializează prin alegerea unui număr de secvenţă iniţial,
ajunge la aşa numita stare SYN-STATE şi trimite un mesaj SYN. Ca un al
doilea exemplu, un protocol de rutare cum este OSPF are câte o diagramă
de stare a maşinii corespunzătoare fiecărui ruter; când un pachet de stare
LSP (Link State Packet) ajunge la un ruter cu număr de secvenţă mai mare
decât ultimul LSP transmis de către sursă, noul LSP ar trebui să fie
memorat şi transmis către toate ruterele învecinate. In timp ce protocolul
de stare a liniei este foarte diferit de protocolul TCP, ambele protocoale
pot fi abstractizate de către diagrama de stări prezentată în figura 2.1. In
afara protocoalelor TCP şi IP, vor fi luate în considerare şi alte protocoale,
aşa cum este protocolul HTTP. Un asemenea model este prezentat în
figura 2.2 fiind folosit drept referinţă.
In primul rând, (fig.2.2 jos), diagrama de stări trebuie să
recepţioneze/trimită pachete de date. Aceasta implică manevrarea datelor
sau operaţii de scriere şi citire a fiecărui octet dintr-un pachet. De
exemplu, TCP trebuie să copieze datele recepţionate în buffer-ele
aplicaţiilor, în timp ce ruterele trebuie să comute pachetele între liniile de
intrare şi cele de ieşire. Antetul TCP specifică, de asemenea, o sumă de
control ce trebuie calculată în funcţie de toate datele. Copierea datelor
necesită de asemenea alocarea de resurse, cum sunt buffer-ele.
In al doilea rând, (fig.2.2, sus), diagrama de stări trebuie să
demultiplexeze datele către unul sau mai multi clienţi. In unele cazuri,
programele client trebuie activate, necesitând un transfer costisitor al
controlului. De exemplu, când TCP receptionează o pagină web, trebuie să
demultiplexeze datele către aplicaţiile de tip web-browser utilizând
câmpurile cu numărul portului şi s-ar putea să fie nevoie să lanseze
procesul rulând browser-ul. Figura 2.2 descrie câteva funcţii generice
utilizate de mai multe protocoale. In primul rând, protocoalele trebuie să
îndeplinească condiţia crucială de a se putea bloca la viteze mari iar uneori
de a putea fi manipulate. De exemplu, un pachet TCP recepţionat face ca
protocolul TCP să caute în tabela de stări a conexiunii, în timp ce
15
pachetele IP recepţionate fac ca protocolul IP să caute o tabelă de
expediere.

Figura 2.2 Funcţii comune ale protocolului, folosind tabela de stări (înegrită)

In al doilea rând, protocoalele au nevoie să seteze eficient contoarele


pentru a controla, de exemplu, retransmisiile protocolului TCP. In al
treilea rând, dacă un modul al protocolului gestionează mai mulţi clienţi
trebuie să-i programeze eficient. De exemplu, TCP trebuie să programeze
procesarea diferitelor conexiuni, în timp ce un ruter trebuie să se asigure
că dominarea conversaţiilor între câteva perechi de calculatoare nu
blochează alte conversaţii. Multe protocoale permit ca mari cantităţi de
date să fie fragmentate în bucăţi mai mici care necesită o reasamblare
ulterioară. Asemenea funcţii generice sunt uneori costisitoare, costul lor
putând fi atenuat utilizând tehnici potrivite.Aşadar fiecare funcţie generică
a protocolului merită să fie studiată individual.

2.1.3 Performanţă şi măsurări


Această secţiune descrie câteva măsurători importante şi presupuneri de
performanţă. Se consideră un sistem (cum este o reţea sau chiar un singur
ruter) unde sarcinile (cum ar fi transmiterea mesajelor) sosesc, iar după
realizarea lor, pleacă. Cei mai importanţi parametri în reţele sunt
16
debitul/traficul util (throughput) şi latenţa (latency). Debitul indică
numărul de operaţii realizate cu succes per secundă. Latenţa măsoară
timpul (cel mai slab posibil) de realizare a unei operaţii. Posesorii
sistemelor (ISP, ruterele) caută să maximizeze debitul util pentru a-şi mări
câştigurile, pe când utilizatorii sistemului doresc o latenţă cap-la-cap mai
scăzută de câteva milisecunde. Latenţa afectează de asemenea viteza de
calcul în reţea.
Următoarele observaţii legate de performanţa mediului Internet sunt utile
când se iau în considerare compromisurile implementării.
• Vitezele liniilor : viteza liniilor principale de tranzit (backbone) poate fi
marită până la 10Gps şi 40Gbps, iar viteza liniilor locale ajunge la ordinul
Gbps. Legăturile wireless şi cele de acasă sunt la momentul curent mult
mai încete.
• Dominanţa TCP şi Web : traficul Web reprezintă peste 70% din traficul
de bytes sau pachete, iar traficul TCP reprezintă 90% din trafic.
Transferurile reduse : majoritatea documentelor Web accesate sunt de
dimensiuni reduse ; un studiu SPEC arată că 50% din fişierele accesate
sunt de 50 kilobytes sau mai puţin.
• Latenţa slabă : întârzierea în timp real depăşeşte limitările impuse de
viteza luminii: măsurătorile efectuate au semnalat o întârziere de 241 msec
dealungul Statelor Unite, în comparaţie cu întârzierile datorate vitezei
luminii care este de 30 msec. Latenţa crescută se datorează eforturilor de
îmbunătăţire a debitului, cum ar fi compresia în modemuri şi pipelining-
ului în rutere.
• Localizarea slabă : studiul asupra traficului din backbone-ul reţelei
indică 250.000 de perechi diferite sursă-destinaţie ce trec printr-un ruter
într-un interval redus de timp. Estimări mai recente indică în jur de un
milion de transmisii concurente. Reunind grupurile de antete care au
aceeaşi adresă destinaţie sau alte resurse comune, nu se reduce
semnificativ numărul de clase al antetelor. Astfel, localizarea, sau
probabilitatea de reutilizare a calculului, investit într-un pachet, pentru un
pachet viitor, este mică.
• Pachetele de dimensiuni reduse: aproximativ jumătate din pachetele
recepţionate de către un ruter sunt pachete de confirmări de dimensiune
minimă de 40 octeţi. Pentru a se evita pierderea de pachete importante
într-un flux de pachete de dimensiuni minime, majoritatea producătorilor
de rutere şi adaptoare de reţea ţintesc către expedierea la viteza
liniilor/wire speed forwarding.
• Măsurări critice : trebuie făcută distincţia între măsurări globale ale
performanţei (întârzirea cap-la-cap şi lărgimea de bandă), şi măsurări
locale ale performanţei (viteza de căutare/lookup a ruterelor). Măsurările
globale sunt cruciale pentru o evaluare globală a reţelei. Aici ne
concentrăm numai asupra măsurărilor locale ale performanţei, şi anume
performanţele de expediere (forwarding) şi resursele (logice, de memorie).
• Instrumente : majoritatea instrumentelor de management a reţelelor,
cum este HP’s Open View, lucrează cu măsurări globale. Instrumentele
necesare pentru măsurările locale sunt instrumente de măsurare a
performanţei din interiorul calculatoarelor, aşa cum este “profiling
software”.

17
Exemplele includ: Rational’s Quantify (http://www.rational.com) pentru
aplicaţii software, Intel’s Vtune (www.intel.com/software/products/vtune),
şi chiar osciloscoape hardware. De asemenea sunt utile programele de
monitorizare a reţelelor aşa cum este tcpdump (www.tcpdump.org).

2.2 Hardware
Pe măsură ce legăturile de date se apropie de viteze de 40 gigabit/s
(OC-768), un pachet de 40 bytes trebuie transmis în 8 nsec. La aşa viteze,
transmisia mai departe a pachetelor este de obicei implementată hardware,
şi nu de un procesor programabil. Nu se poate participa la procesul de
proiectare a unui astfel de hardware, fără înţelegerea condiţiilor impuse
proiectanţilor şi mecanismelor unui asemenea hardware competitiv.
Câteva modele simple pot permite înţelegerea şi facilitează lucrul cu astfel
de modele de hardware.
Căutările Internet sunt implementate de obicei folosind logica
combinatională, pachetele Internet sunt stocate în memoriile ruterelor, şi
un ruter Internet este pus laolaltă cu comutatoare şi chip-uri de căutare. În
consecinţă, se începe cu implementarea logică, se continuă cu descrierea
memoriilor, şi se termină cu schema bazată pe componente. Pentru detalii,
dăm spre referinţa clasicul VLSI, care încă se mai foloseşte, şi arhitectura
clasică a calculatorului.

2.2.1 Logica combinatorie


Modelele foarte simple pentru porţile logice de bază cum ar fi:
Inversorul, SI-NU şi SAU-NU pot fi înţelese chiar şi de un programator.
Totuşi, nu este necesar să ştim cum sunt implementate porţile logice de
bază, pentru a avea o privire de ansamblu asupra proiectării hardware.
Primul pas spre înţelegerea proiectării logice este observaţia următoare.
Fiind date porţile inversoare, SI-NU şi SAU-NU, algebra booleană arată că
poate fi implementată orice funcţie booleană f ( I1 ,..., I n ) , de n variabile de
intrare. Fiecare bit al multibitului de ieşire poate fi considerat ca o funcţie
de biţii de intrare. Minimizarea logicii foloseşte la eliminarea porţilor
redundante şi uneori la creşterea vitezei. De exemplu, dacă “+” înseamnă
poartă SAU şi “ • ” poartă SI, atunci funcţia :
O = I1 ⋅ I 2 + I1 ⋅ I 2
poate fi simplificată la:
O = I1 .

Exemplul 1: Calitatea serviciului (QOS) şi codoarele de prioritate: să


presupunem că avem un ruter de reţea care menţine pentru pachete n cozi
de aşteptare per legătură de ieşire, unde coada i are o prioritate mai mare
decât coada j dacă i < j . Aceasta este o problemă de QOS. Planificatorul
transmisiei din ruter trebuie să aleagă un pachet din prima coadă nevidă, în
ordinea priorităţilor. Planificatorul menţine un vector pe N-biţi (bitmap): I,
aşa încât I [ j ] = 1 dacă şi numai dacă coada j este nevidă; atunci
planificatorul poate găsi coada nevidă cu cea mai mare prioritate, prin
găsirea celei mai mici poziţii din I cu bitul setat. Proiectanţii hardware
cunosc această funcţie sub denumirea de codor de prioritate. Totuşi, chiar

18
şi un proiectant de software trebuie să-şi dea seama că această funcţie se
pretează la o implementare hardware, pentru un n rezonabil. Funcţia este
descrisă mai detaliat în exemplul 2.

2.2.2 Sincronizare şi putere


Pentru transmisia mai departe a unui pachet de 40 bytes la viteza unui
OC-768, orice funcţie de reţea aplicată pachetului trebuie terminată în 8
nsec. De aceea intârzierea maximă la transmisia semnalului de la intrare la
ieşire, pe orice cale logică, nu trebuie să depăşească 8 nsec. In acest scop,
e necesar un model al timpului de intârziere introdus de un tranzistor. In
ansamblu, fiecare poartă, NU sau SI-NU, poate fi gândită ca un set de
capacitoare şi rezistoare care trebuie încărcate (când se schimbă valorile de
intrare) ca să se poata calcula valorile de ieşire. Chiar mai rău, încărcarea
unei porţi de intrare poate determina ca ieşirile unor porţi ulterioare să
încarce intrări următoare, şi aşa mai departe. In consecinţă, pentru o
functie combinaţională, întârzierea în calcularea funcţiei este suma
întârzierilor de încărcare şi de descărcare introduse de căile cele mai
defavorabile ale tranzistoarelor. Timpii de întârziere introduşi de astfel de
căi trebuie să se încadreze în timpul minim de sosire al pachetelor.
De asemenea este nevoie de energie pentru a încărca condensatoarele;
energia pe unitatea de timp (puterea) este proporţională cu capacitatea,
pătratul tensiunii, şi frecvenţa cu care se poate schimba
intrarea P = C ⋅ V 2 ⋅ f . Noile procese reduc nivelul tensiunii şi al
capacităţii, iar circuitele de viteză mare trebuie să crească frecvenţa de
ceas. Paralelism înseamnă că mai multe capacităţi trebuiesc încărcate
simultan. In consecinţă, multe chip-uri de mare viteză disipă foarte multă
caldură, necesitând tehnici deosebite de răcire. ISP-urile şi facilităţile de
colocare sunt mari consumatori de putere. Desi nu tratăm schimbul de
caldură, e bine de ştiut că chip-urile şi ruterele sunt câteodată limitate în
putere. Câteva limite practice de astăzi sunt: 30 W/cm2 si respectiv 10 000
W/foot2 într-un centru de date (1 foot =0.305m).
Exemplul 2: Proiectarea codoarelor de prioritate: Să considerăm
problema estimării sincronizării necesare codorului de prioritate din
exemplul 1, pentru o legătură OC-768, folosind pachete de 40 bytes. In
consecinţă circuitul are la dispoziţie 8 nsec să producă ieşirea.
Considerând că intrarea I şi ieşirile O sunt vectori pe N biti ca O [ j ] = 1
dacă şi numai dacă I [ j ] = 1 şi I [ k ] = 0 pentru toate k < j . Trebuie
observat că ieşirea este reprezentată în unar (de multe ori numită
reprezentare 1 hot) şi nu în binar. Specificaţia conduce direct la ecuaţia
combinaţiilor logice: O[ j] = I[1]...I[ j −1]I[ j] pentru j > 0.
Acest model poate fi implementat direct folosind N porţi SI, una pentru
fiecare bit de ieşire, unde cele N porţi au un număr de intrări care variază
de la 1 la N. Intuitiv, din moment ce N porţi de intrare SI iau
( )
O ( N ) tranzistoare, avem un model. Modelul 1, cu O N 2 tranzistoare
care pare că iau O (1) timp. Chiar şi acest nivel de proiectare este util deşi
s-ar putea şi mai bine.

19
Un alt model mai economic din punct de vedere al suprafeţei este bazat
pe observaţia că fiecare bit de ieşire O ( j ) necesită poarta SI componentă
a primilor j − 1 biţi de intrare. In consecinţă definim rezultatele parţiale
P[ j] = I[1]...I[ j −1] pentru j = 2...N . Este clar că O [ j ] = I [ j ] P [ j ] . Dar P [ j ]
poate fi construită recursiv folosind ecuaţia P[ j] = P[ j −1]⋅ I[ j] care poate fi
implementată folosind N porţi SI de câte două intrări, conectate în serie.
Aceasta conduce la un model, modelul 2 care necesită O ( N ) tranzistoare
dar care ia O ( N ) timp.
Modelul 1 este rapid şi mare, şi modelul 2 este lent şi redus. Acesta este
un compromis familiar spaţiu-timp care sugerează că putem avea o soluţie
intermediară. Calculul lui P [ j ] în modelul 2 seamană cu un arbore binar
neechilibrat de înălţime N. Totuşi, este evident că P [ N ] poate fi calculat
folosind un arbore binar complet echilibrat de porţi SI cu 2 intrări, de
înălţime log N . Rezultatele parţiale ale arborelui binar pot fi combinate în
feluri simple astfel încât să avem P [ j ] pentru toate j < N folosind
acelaşi arbore binar.
De exemplu dacă N=8, pentru calculul lui P[8] calculăm X = I[0]...I[3] şi
Y = I[4]...I[7] şi calculăm SI din X şi Y la rădăcină. Deci este uşor să
calculăm P[5] spre exemplu, folosind una sau mai multe porţi SI,
calculând X ⋅ I[4] . O astfel de metodă este foarte utilizată de proiectanţii
hardware pentru înlocuirea lanţului de calcul aparent lung de O ( N ) , cu
lanţuri de lungime 2 log N . Deoarece a fost folosită prima dată ca să
grăbească lanţurile carry, este cunoscut sub denumirea de carry lookahead
sau simplu look-ahead. Chiar dacă tehnicile look-ahead par complexe,
chiar şi programatorii le pot stăpâni pentru că la baza lor, folosesc tehnica
dezbină-şi-cucereşte.

2.2.3 Creşterea nivelului de abstractizare a proiectării hardware


Proiectarea manuală a fiecărui tranzistor dintr-un nou cip de reţea, care
constă în 1 milion de tranzistoare, ar fi consumatoare de timp. Procesul de
proiectare poate fi redus la câteva luni folosind blocurile constructive (vezi
tehnologiile de construire a blocurilor funcţionale, ca PLA, PAL şi a
celulelor standard).
Un lucru important este faptul că la fel ca şi programatorii care
refolosesc codurile, proiectanţii de hardware refolosesc implementări de
funcţii frecvent utilizate. In afară de blocurile de calcul comune, ca
blocurile de adunare, multiplicare, comparare şi codare a prioritatilor,
proiectanţii folosesc de asemenea blocuri decodoare, blocuri de deplasare
cu împrumut (barrel shifter), multiplexoare şi demultiplexoare.
Un decodor converteşte o valoare log N biţi la una N biţi unară de
aceeaşi valoare; reprezentările binare sunt mai compacte, dar
reprezentările unare sunt mai convenabile pentru calcul. Un bloc de
deplasare cu împrumut deplasează o intrare I cu s pozitii la stânga sau la
dreapta, cu biţii mutaţi circular de la un capăt la celălalt.

20
Un multiplexor (mux) conectează una din mai multe intrări la o ieşire
comună, în timp ce dualul său: demultiplexorul, rutează o intrare la una
din mai multe posibile ieşiri. Mai precis, un multiplexor conectează unul
din n biţi de intrare I j la ieşirea O dacă un semnal de selecţie S de log n
biţi codează valoarea j în binar. Dualul său, demultiplexorul, conectează
intrarea I la ieşirea O j dacă semnalul S codează valoarea j în binar.

FIGURA 2.3: Multiplexor cu 4 intrări cu biţii de selecţie S0 şi S1 , pornind de la trei


multiplexoare cu 2 intrări (multiplexorul are simbolul standard trapezoidal).

Astfel trebuie descompusă o funcţie logică complexă în funcţii standard,


folosind recursivitatea când este nevoie. Aceasta este o reducere şi urmează
principiul dezbină-şi-cucereşte fiind folosită de programatori cu uşurinţă. De
exemplu, figura 2.3 arată problema tipică Lego cu care se confruntă
proiectanţii hardware: construiţi un multiplexor de 4 intrări pornind de la
multiplexoare de 2 intrări. Incepeţi prin a alege una dintre intrările I 0 si
I1 folosind un multiplexor cu 2 intrări şi apoi una dintre I 2 şi I 3 folosind un
alt multiplexor de 2 intrări. Rezultatele multiplexoarelor de 2 intrări din primul
etaj, trebuie combinate folosind un al 3-lea multiplexor de 2 intrări; singura
observaţie este că semnalul de selecţie pentru primele 2 multiplexoare este cel
mai puţin semnificativ bit S0 al semnalului de selecţie de 2-biţi, iar al treilea
multiplexor selectează jumătatea de sus sau cea de jos, astfel încât foloseşte S1
ca bit de selecţie.
Următorul exemplu de reţea arată că reducerea este o unealtă de proiectare
puternică pentru proiectarea funcţiilor de reţea.
Exemplul 3: Planificarea crossbar şi codoare de prioritate programabile:
Exemplele 1 şi 2 au motivat proiectarea unui codor de prioritate rapid (PE).
Un mecanism obişnuit de arbitrare de ruter foloseşte o formă îmbunătăţită de
codor de prioritate numit codor de prioritate programabil (PPE). Avem o
intrare I de N biţi ca mai înainte, împreună cu o intrare adiţională P de log N
biţi. Circuitul PPE trebuie să calculeze o ieşire O astfel încât O [ j ] = 1 , unde j
este prima pozitie după P (tratat ca valoare binară) care are o cifră binară
diferită de zero în I. Dacă P = 0 , această problemă se reduce la un codor de
prioritate simplu. PPE-urile apar natural în arbitrarea switchurilor. Deocamdată
presupunem că un ruter conectează N legături de comunicaţie Presupunem că
mai multe legaturi de intrare vor să transmită un pachet la legătura de ieşire L.
Pentru a evita coliziunea la ieşirea L, fiecare intrare trimite o cerere la L în
primul interval de timp; L alege legătura de intrare căreia îi va servi cererea în

21
slotul al doilea; intrarea aleasă expediează un pachet în al treilea interval
temporal.
Pentru a lua decizia de servire, L poate să stocheze cererile primite la
sfârşitul slotului 1 într-un vector R de N-biţi, unde R [i ] = 1 dacă legătura de
intrare i doreşte să transmită la L. Pentru corectitudine, L ar trebui să reţină
ultima intrare P servită. După aceea, L ar trebui să servească prima legătură
de intrare de după P, care are o cerere. Aceasta este o problemă PPE cu R şi P
ca intrări. Deoarece un ruter trebuie să facă o arbitrare pentru fiecare poziţie
temporală şi pentru fiecare legătură de ieşire, este nevoie de un model PPE
rapid şi eficient ca arie. Chiar şi un programator poate să înţeleagă, şi posibil să
repete procesul folosit pentru a proiecta PPE-ul din Tiny Tera, un switch
construit la Stanford şi mai târziu comercializat. Ideea de bază este reducerea:
reducerea proiectării unui PPE la proiectarea unui PE (exemplul 2).
Prima idee este simplă. PPE este în esenţă un PE a cărui prioritate mazimă
începe de la pozitia P în loc de 0. Un barrel-shifter poate fi folosit pentru a
deplasa I mai întâi spre stânga cu P biţi. După aceasta se poate folosi un
simplu PE. Bineinţeles că vectorul bitilor de iesire este acum deplasat; deci
trebuie restabilită ordinea originală deplasând ieşirea lui PE la dreapta cu P
biţi. Un bloc de deplasare cu împrumut pentru N-biti de intrare poate să fie
implementat folosind un arbore de multiplexoare de 2 intrări într-un timp în
jur de log N . Astfel două blocuri de deplasare cu împrumut şi un PE
echivalează cu aproximativ 3log N întârzieri de porţi.
Un proiect mai rapid, care necesită numai 2 log N întârzieri de porţi
este următorul. Problema se poate împărţi în două părţi. Dacă intrarea
are nişte biţi setaţi la poziţia P sau mai mare, atunci rezultatul poate fi
găsit folosind un PE care operează pe intrarea originală după ce au fost
setaţi pe 0 toţi biţii de intrare a căror poziţie este mai mică decât P. Pe pe
de altă parte, dacă intrarea nu are nici un bit setat la poziţia P sau mai
mare, atunci rezultatul poate fi găsit folosind PE-ul care operează pe
intrarea originală fără nici o mascare. Acest rezultat din modelul din
figura 2.4, testat pe Texas Instrument Cell Library, a fost aproape de
două ori mai rapid şi a însemnat folosirea unei suprafeţe de trei ori mai
mică decât modelul blocului de deplasare cu împrumut, pentru un ruter
cu 32-porturi.
Deci logica modelului pentru o componentă de timp critică, un switch,
poate fi realizată folosind reduceri şi modele simple.

2.2.4 Memorii
In punctele terminale şi rutere, înaintarea/expedierea pachetelor se face
folosind logica combinaţională, dar pachetele şi stările necesare înaintării
trebuiesc stocate în memorii. Deoarece accesarea memoriilor este
semnificativ mai lentă decât întârzierile logice, memoriile sunt cele care
generează strangulări masive în rutere şi în puncte terminale.
În plus, diferitele subsistemele necesită memorii cu caracteristici diferite.
De exemplu, vânzătorii de rutere cred că este important un buffer de 200 de
msec pentru a evita pierderea pachetelor în timpul perioadelor de congestie.
La, să zicem 40 Gbit/sec per legătură, un astfel de buffer de pachete necesită o
cantitate enormă de memorie. Pe pe de altă parte, căutările ruterelor necesită o

22
cantitate mai mică de memorie, accesată aleator. Astfel este bine să avem
modelele simple pentru memorii cu diferite tehnologii.

REGISTRE
Un bistabil (flip-flop) conectează două sau mai multe tranzistoare într-o
buclă, astfel încât (în absenţa căderilor tensiunii) cifra binară să stea nedefinit
de mult timp pe ieşire fără să se modifice valoarea tensiunii. Un registru este o
colecţie ordonată de bistabile. Cele mai moderne procesoare au registre pe
chip de 32 sau 64 biţi. Un registru pe 32 biţi conţine 32 de bistabile,
fiecare pentru a memora 1 bit. Accesul de la logică la un registru, pe
acelaşi chip, este extrem de rapid, în jur de 0.5-1 nsec.
SRAM
O memorie statică cu acces aleator (SRAM) conţine N registre adresate cu o
adresă A, pe log N biţi. SRAM este numită astfel deoarece reîmprospătarea
bistabilelor este de tipul “static”. În afară de bistabile, SRAM are nevoie şi de un
decodor care decodează A, într-o valoare unară, folosită pentru accesarea
registrului potrivit. Accesarea unui SRAM pe-chip este doar puţin mai lentă decât
accesarea unui registru, din cauza întârzierii introduse de decodare. În momentul
actual, este posibil să se obţină SRAM pe chip cu timp de acces de 0.5 nsec. Timpi de
acces de 1-2 nsec pentru SRAM pe-chip şi 5-10 nsec pentru SRAM care nu este pe chip,
se întâlnesc în mod uzual.
DRAM
O celulă binară SRAM necesită cel puţin cinci tranzistoare. Astfel
SRAM este întotdeauna mai puţin densă sau mai scumpă decât tehnologia
de memorie bazată

FIGURA 2.4: Proiectul Tiny Tera PPE foloseşte codor de prioritate 1, pentru a găsi
setul cel mai mare de biţi, dacă există, a tuturor biţilor mai mari decât P folosind o mască
de codare a lui P. Dacă nu e găsit nici un bit, e validată ieşirea codorului 2 de prioritate
(poarta SI de jos). Rezultatele celor două codoare sunt apoi combinate cu o poartă SAU
cu N intrări.

pe DRAM (Dynamic RAM). Ideea de bază este să se înlocuiască bucla de


răspuns (şi implicit tranzistoarele suplimentare) folosită pentru a memora
un bit într-un bistabil, cu o capacitate care poate să stocheze bitul; sarcina
se scurge, dar încet. Pierderea datorată descărcării este compensată
reîmprospătând celula DRAM în mai puţin de o milisecundă. Bineînţeles,
fabricarea unei capacităţi mari folosind puţin siliciu e complexă.
23
Capacitatea chip-urilor DRAM pare că devine de 4 ori mai mare la fiecare
3 ani şi tinde spre 1 Gbit pe un singur chip. Adresarea acestor biţi, chiar
dacă ei sunt construiţi împreună în aceeaşi capsulă sub forma unor registre
de 4 sau 32 de biţi, este dificilă. Adresa trebuie să fie decodificată de la, să
zicem 20b la una din 220 valori. Complexitatea unei astfel de logici de
decodare sugerează folosirea tehnicii dezbină şi cucereşte. Dar de ce să nu
se folosească o decodare în doi paşi în schimb?

FIGURA 2.5: Memoriile mai mari sunt organizate pe două dimensiuni în rânduri şi
coloane. Selectarea unui cuvânt constă în selectarea mai intâi a rândului şi apoi a coloanei
din acel rând.

Figura 2.5 arată că cele mai multe memorii sunt organizate intern pe
două dimensiuni, în rânduri şi coloane. Cifrele binare de adresă de sus sunt
decodificate pentru a selecta rândul, şi după aceea cifrele binare de adresă
de jos sunt folosite pentru a decodifica coloana. Utilizatorul furnizează
mai întâi biţii de adresă a rândului şi activează un semnal numit RAS(Row
Address Strobe); apoi utilizatorul furnizează biţii de adresă ai coloanei şi
activează un semnal numit CAS(Column Address Strobe). Dupa un timp
specificat, cuvântul dorit din memorie poate fi citit. Presupunând rânduri şi
coloane egale, se reduce complexitatea porţii de decodare de la O ( N ) la
O ( ( N ) ) cu preţul creşterii întârzierii cu timpul necesar pentru încă o
decodare. In afară de timpul de întârziere necesar, dintre RAS şi CAS, mai
este de asemenea necesară şi o întârziere de preîncărcare între invocări
succesive ale lui RAS şi CAS, timp ce permite încărcarea
condensatoarelor.Cele mai rapide chip-uri DRAM au nevoie în jur de 40-60 nsec
(latenţa) pentru acces, cu timpi mai lungi ca de exemplu 100 nsec, între citiri
succesive (consum) din cauza restricţiilor de preîncărcare. Acesta latenţă include
timpul necesar pentru a adresa liniile externe de interfaţă la pinii memoriei DRAM;
inovaţiile recente permit realizarea de DRAM pe chip, cu timpi de acces mai mici,
în jur de 30 nsec. DRAM va fi întotdeauna mai densă dar mai lentă decât SRAM..

Page-Mode DRAM
Unul din motivele înţelegerii DRAM este înţelegerea artificiului
utilizat pentru accelerarea timpului de acces, numit page mode/modul
pagină. Acest mod de acces este benefic pentru a accesa structuri de date
care au o localizare spaţială, în care cuvintele de memorie adiacente sunt
24
accesate succesiv. Dar accesând un rând (fig. 2.5), se pot accesa cuvinte
din rând, fără întârzieri suplimentare de RAS şi preîncărcare. RAM–urile
video exploatează această structură având o citire de rând într-un SRAM,
care poate fi citit serial, pentru a reîmprospăta un ecran (display) la viteze
înalte. In afara de page mode şi video RAM, mai sunt şi alte idei care
exploatează structura DRAM, care pot fi folositoare în reţele.
Multe chip-uri de DRAM au avantajul că adresele de linii şi coloane nu sunt cerute în
acelaşi timp astfel ca pot fi multiplezate pe aceeaşi pini, reducând necesarul de pini per
chip.

FIGURA 2.6: Ideea RAMBUS, SDRAM, etc., este crearea unui singur chip cu
DRAM multiple paralele, câştigând astfel lăţime de bandă de memorie şi folosind doar
un set de linii de adrese şi de date.

DRAM -uri întreţesute


În timp ce latenţa memoriei este critică pentru viteza de calcul, debitul
memoriei (numit şi lăţime de bandă) este de asemenea important pentru multe
aplicaţii de reţea. Cu o memorie DRAM care are cuvinte de 32b şi ciclul de 100
nsec, debitul de ieşire, folosind o singură copie a memoriei DRAM este limitat la
32 biţi la fiecare 100 nsec. Debitul de ieşire poate fi îmbunătăţit folosind accesările
la DRAM-uri multiple (fig. 2.6), numite bancuri de memorie, care pot avea o
singură magistrală. Utilizatorul poate să înceapă o citire a bancului1 punând adresa
pe magistrala de adresă. Presupunem că fiecare banc DRAM are nevoie de 100
nsec pentru a returna datele selectate.
În loc să aştepte inactiv în acest timp de întârziere de 100-nsec, utilizatorul poate
să pună o a doua adresă pentru bancul 2 pe magistrală, a treia pentru bancul 3, şi aşa
mai departe. Dacă punerea fiecărei adrese ia 10 nsec, utilizatorul poate alimnta 10
bancuri DRAM înainte să ajungă răspunsul la cererea făcută asupra primului banc
DRAM, urmată 10 nsec mai târziu de răspunsul la cererea făcută asupra celui de-al
doilea banc DRAM, şi aşa mai departe. Deci lăţimea de bandă netă de memorie
folosită în acest exemplu este de 10 ori mai mare decât cea a unei singure memorii
DRAM, atâta timp cât utilizatorul poate face în aşa fel încât să aibă adresări
consecutive la bancuri consecutive.
In timp ce ideea folosirii de bancuri de memorie multiple este una veche, în ultimii
5 ani proiectanţii memoriilor au integrat mai multe bancuri într-un singur chip (fig.
2.6), în care liniile de adresă şi de date pentru toate bancurile de memorie sunt
multiplexate folosind o reţea comună de mare viteză numită magistrală/bus. In plus,
accesurile de tip “page mode” sunt de cele mai multe ori permise pe fiecare banc.
Există multiple tehnologii de realizare a memoriilor folosind această idee de bază,
cu diferite valori pentru mărimea DRAM, protocolul de citire şi scriere şi numărul

25
bancurilor. Exemple importante includ SDRAM cu 2 bancuri şi RDRAM (Remote
DRAM)cu 16 bancuri.
Exemplul 4. Căutări/lookup de fluxuri pipeline: un flux este caracterizat de
existenţa unei adrese IP a sursei şi a destinaţiei, şi de porturi TCP. Unii clienţi doresc
ca ruterele să ţină evidenţa pachetelor trimise de fiecare flux de reţea, pentru
contorizare. Sunt deci necesare structuri de date care să stocheze un contor pentru
fiecare flux ID şi care să suporte două operaţii: Insert (FlowID) pentru inserarea
unui nou ID de flux şi Lookup (FlowID) pentru găsirea locaţiei contorului de flux
ID. Căutarea necesită o potrivire exactă a ID-ului fluxului – care este în jur de 96 biţi
– în timpul recepţionării pachetului. Aceasta poate fi făcută prin orice algoritm de
potrivire exactă, cum ar fi cel de dispersare/hashing.
Totuşi, pe măsură ce tot mai mulţi vânzători de rutere vor să limiteze timpul
de lookup pentru cazul cel mai defavorabil, căutarea binară este cea mai bună.
Presupunem că aceste căutări ale ID-urilor fluxurilor trebuie făcute la viteza
liniei, pentru cazul cel mai defavorabil de pachete de 40 bytes la viteze de 2.5
Gbit/sec/viteze OC-48. In consecinţă chip-ul are la dispoziţie 128 nsec pentru a
găsi un ID de flux.
Pentru a mărgini întârzierile de căutare, considerăm un arbore binar simetric, ca
arborele B. Logica parcurgerii arborelui este uşoară. Pentru creşterea vitezei, ar
trebui ca ID-urile fluxurilor şi numărătoarele să fie stocate într-o memorie
SRAM.. Estimările actuale actuale în ceea ce priveşte nucleul ruterelor arată în
jur de 1 milion de fluxuri concurente. Păstrarea stării pentru 1 milion de fluxuri în
SRAM este scumpă. Chiar dacă folosim doar DRAM-uri, cu arbori binari cu
factor de branşament de 2, ar necesita log21.000.000=20 accese de memorie.
Chiar cu un timp de acces optimist al memoriei DRAM de 50 nsec, timpul
necesar întregii căutări este de 1 μsec, care este prea lent
O soluţie este folosirea pipelining-ului (fig. 2.7), în care accesul logic prin
pipeline la ID-urile fluxurilor stocate într-o memorie RDRAM cu 16 bancuri este
ca în figura 2.6. Toate nodurile de înălţime i din arborele binar sunt stocate în
bancul i al RDRAM-ului. Chip-ul de căutare face16 căutări de ID-uri de fluxuri
(pentru 16 pachete) deodată. De exemplu după ce a căutat în nodul rădăcină
pentru pachetul 1 în bancul 1, chip-ul poate să caute, la nodul arborelui de nivel
doi, după pachetul 1 în bancul 2 şi (foarte repede dupa asta) poate să caute la
nodul rădăcină al arborelui pentru pachetul 2 din bancul 1. Când firul/thread
procesului de căutare a pachetului 1 accesează bancul 16, firul procesului de
căutare a pachetului 16 accesează bancul 1. Deoarece RDRAM-ul direct
funcţionează la 800 Mhz, timpul dintre cererile de adresare a magistralei RAM-
ului este mic comparativ cu timpul de acces de citire de 60 nsec. Astfel, în timp
ce unui singur pachet îi trebuie16 ∗ 60 nsec, procesarea concurentă a 16 pachete
permite debitul de o găsire de ID de flux la fiecare 60 nsec.

26
FIGURA 2.7: Găsirea identităţii fluxului, cu un chip de căutare pipeline, care lucrează la găsirea a
până la 16 ID-uri de fluxuri în acelaşi timp, fiecare din ele accesând un banc DRAM independent. Chip-
ul de căutare returnează un index procesorului de reţea care actualizează numărătorul ID a fluxului.

Dar, un arbore binar cu 16 nivele permite doar 216 = 64 K ID-uri de flux, prea
puţin. Din fericire RAMBUS permite o variantă de page mode, unde 8 cuvinte de
32 biţi pot fi accesate aproape în acelaşi timp ca 1 cuvânt. Astfel putem găsi două
chei de 96 biţi şi 3 pointeri de 20 biţi într-un acces la memorie de 256 biţi. Se
poate deci folosi un arbore cu branşament de 3, care permite potenţial 316 sau 43
milioane de ID-uri de fluxuri.

2.2.5 Tehnici de proiectare a subsistemelor de memorii


Problema găsirii ID-urilor fluxurilor de date ilustrează 3 tehnici majore folosite
de obicei în proiectarea subsistemului memoriei pentru chip-urile de reţea.
• Intreţeserea memoriilor şi tehnica pipeline: Tehnici similare sunt folosite
în căutarea IP-urilor şi algoritmilor de planificare care implementează QOS.
Bancurile multiple pot fi implementate folosind câteva memorii externe, o
singură memorie externă ca RAMBUS, sau un SRAM pe chip care contine şi
logica de procesare.
• Procesarea paralelă a cuvintelor lungi: Multe modele de reţea, ca schema
vectorilor binari de la Lucent, folosesc cuvinte de memorie lungi care pot fi procesate în
paralel. Aceasta poate fi implementată cu DRAM si folosind page-mode-ul, sau cu SRAM
şi făcând fiecare cuvânt de memorie mai mare.
• Combinarea DRAM şi SRAM: Tinând cont de faptul că SRAM este scump şi
rapid iar DRAM este ieftin şi lent, este de aşeptat o combinaţie a acestora. Folosirea SRAM
ca şi cache pentru bazele de date DRAM este clasică, dar sunt multe aplicaţii creative a
memoriei ierarhizate. In exerciţii e analizat efectul unui SRAM mic în proiectarea unui chip
căutator de ID. O aplicaţe mai neobişnuită a acestei tehnici de implementare a unui număr
mare de numărătoare, este stocarea în SRAM a biţilor de rang inferior.
E mai important ca tânarul proiectant să înţeleagă aceste tehnici de proiectare (decât să
stie detalii de implementare a memoriilor) ca să producă implementări hardware creative a
funcţiilor de reţea.

2.2.6 Proiectarea la nivel de componente


Metodele din ultimele două secţiuni pot fi folosite pentru a realiza o
maşină de stări/state machine care implementează o prelucrare oarecare.
O maşină cu stări are starea curentă stocată în memorie; maşina
procesează datele de intrare folosind logica combinaţională, citeşte starea
curentă şi eventual memorează starea. Un exemplu de maşină complexă cu
stări este procesorul Pentium, a cărui stare curentă este o combinaţie de
27
registre, memorie cache şi memorie principală. Un exemplu de maşină cu
stări mai simplă este chip-ul de căutare a ID-ului fluxului (flow ID lookup)
din figura 2.7, a cărui stare e dată de registrele folosite pentru urmărirea
fiecăreia din cele 16 căutări concurente şi RDRAM-ul care memorează
arborele binar B.
Doar puţine chip-uri cheie trebuie proiectate pentru a realiza un ruter sau
o placă de reţea, iar restul proiectării este la nivelul componentelor:
organizarea şi interconectarea chip-urilor pe placă şi amplasarea plăcii în
carcasă ţinînd cont de factorul de formă, de putere şi de răcire. Un aspect
cheie în proiectarea la nivel de componente este înţelegerea limitării
impuse de numărul de pini (pin-count) care de obicei permite „verificării
de paritate” rapidă a proiectelor fezabile.
Exemplul 5. Influenţa numărului de pini asupra bufferelor din rutere:
considerăm un ruter cu 5 legături, fiecare de 10Gb/s. Necesarul de buffer-e
va fi 200ms*50Gb/s, deci 10Gb. Din cauza costului şi a puterii se folosesc
DRAM-uri pentru memoriile tampon/buffer de pachete. Deoarece fiecare
pachet trebuie să intre şi să iasă în/din buffer, lăţimea totală de bandă
trebuie să fie dublul lăţimii de bandă de intrare (ex.100Gb/s). Dacă
presupunem o supraîncărcare de 100% pentru antetele pachetelor interne,
legăturile dintre pachetele din cozile de aşteptare şi lăţime irosită de bandă
de memorie, atunci este rezonabil să considerăm o lăţime de bandă de
200Gb/s pentru memorie.
Pentru o singură memorie RDRAM directă cu 16 bancuri, specificaţiile
dau o lăţime maximă de bandă a memoriei de 1.6Gb/s, sau 13Gb/s.
Accesarea fiecărei memorii RDRAM necesită 64 de pini de interfaţă
pentru date şi 25 de pini pentru adrese şi control, deci un total de 90 pini.
Pentru a obţine 200Gb/s avem nevoie de 16 memorii RDRAM, care
necesită 1440 de pini în total. O limită superioară conservativă a
numărului de pini per chip este de circa 1000. Chiar dacă producătorul de
rutere ar construi un chip foarte rapid, personalizat conform cererii
clientului, capabil să trateze toate pachetele la rata maximă, tot mai este
nevoie de măcar un chip pentru manevrarea traficului în/din buffer-ul de
pachete RAMBUS. În concluzie limitarea numărului de pini este o
condiţie cheie în partiţionarea proiectării între chip-uri.

2.2.7 Concluzii despre hardware


Din partea de hardware cele mai importante sunt tehnicile de proiectare
din &2.2.5. Următorii parametri sunt utili pentru proiectanţii de sisteme:
• Scalarea complexităţii chipului: numărul total de componente per
chip se dublează la fiecare doi ani. La momentul de faţă un ASIC
(Application Specific Integrated Circuit) poate conţine mai multe milioane
de porţi logice plus încă 50 Mbiţi într-o memorie SRAM pe chip. DRAM
încorporată pe chip este o opţiune comună, pentru a avea mai mult spaţiu
pe chip chiar cu întârzieri mai mari.
• Viteza chip-ului: frecvenţe de 1GHz sunt comune, unele chip-uri
ajungând până la 3GHz. Un ciclu de tact pentru un chip care lucrează la
1GHz este de 1ns. Dacă se optează pentru operarea în paralel prin
pipeline-uri şi memorii cu cuvinte extinse, se pot efectua mai multe
operaţii pe un singur ciclu de tact, mărindu-se astfel substanţial viteza de
procesare.
28
• I/E chip-ului: numărul de pini per chip creşte de la an la an, dar
deocamdată limitarea superioară este de 1000 pini per chip.
• I/E seriale: sunt disponibile legături seriale între chip-uri de pînă la
10Gb/s.
• Scalarea memoriei: există memorii SRAM pe chip cu timpi de acces
de 1ns, iar memoriile SRAM externe au de obicei timpi de acces de 2.5ns.
Pentru memoriile DRAM avem 30ns pentru cele integrate pe chip şi 60ns
pentru cele externe chipului. Desigur se pot folosi memorii DRAM
întreţesute pentru a îmbunătăţi rata de transfer a subsistemului memoriei
pentru anumite aplicaţii. Costul DRAM e de 4-10 ori mai mic decât
SRAM la aceeaşi capacitate.
• Puterea şi carcasa: consumul mare de putere a ruterelor de mare
viteză necesită o proiectare adecvată a sistemului de răcire/ventilaţie. De
asemenea există şi o cerinţă de minimizare a dimensiunilor ruterelor
pentru a putea fi montate în rack-uri dedicate cât mai compacte.
Valorile acestor parametri au o influenţă clară asupra proiectării de reţele
de mare viteză. De exemplu, la viteze OC-768, un pachet de 40 octeţi
ajunge în 3.2ns, deci este clar că toate stările prin care trece prelucrarea
pachetului trebuie să fie puse în memorii SRAM integrate pe chip. Din
păcate capacitatea acestor memorii pe chip nu creşte atât de repede ca şi
numărul de fluxuri din ruter. Cu o memorie SRAM de 1ns, cel mult 3
operaţii de acces a memoriei pot fi efectuate spre un singur banc de
memorie în timpul sosirii unui pachet.
Deci tehnicile de proiectare din &2.2.5 trebuie folosite în interiorul
chip-ului pentru a obţine paralelismul în chip-uri, folosind bancuri
multiple de memorie şi cuvinte extinse, şi pentru a creşte memoria
utilizabilă combinând memoriile de pe chip cu memoriile externe. Dar
deoarece densitatea de chip-uri şi constrângerile de putere limitează
paralelismul la un factor de maxim 60, concluzia este că toate funcţiile de
prelucrare a pachetelor la viteză mare trebuie să folosească cel mult 200
accesări ale memoriei şi o memorie pe chip limitată.

2.3. Arhitectura echipamentelor de reţea


Optimizarea performanţelor reţelelor necesită optimizarea căilor datelor
prin nodul sursă şi prin fiecare ruter. Din acest motiv este importantă
înţelegerea arhitecturii interne a nodurilor terminale şi a ruterelor. În
esenţă şi ruterele şi nodurile terminale sunt maşini cu stări, dar arhitectura
fiecăruia este optimizată pentru scopuri diferite. Nodurile terminale au o
arhitectură mai generală pentru relizarea unor operaţii comune, în timp ce
ruterele sunt specializate pentru comunicaţia de tip Internet.

2.3.1 Arhitectura nodurilor terminale


Un procesor cum este Pentium este o maşină cu stări, care ia o secvenţă
de instrucţiuni şi date de intrare, după care trimite ieşirea spre anumite
echipamente de I/E, precum imprimante şi terminale. Pentru a asigura
programelor un spaţiu mare de memorare a stărilor, cea mai mare parte a
informaţiei este stocată în memorii externe ieftine precum DRAM. În PC-
uri această memorie reprezintă memoria principală şi este implementată
folosind 1GB sau mai mult de memorii DRAM întreţesute, precum
SDRAM. Dar memoriile DRAM necesită un timp de acces mare (60ns) şi
29
dacă stările proceselor ar fi memorate numai în DRAM, atunci o
instrucţiune ar necesita 60ns pentru a scrie şi a citi din memorie.
Procesoarele mai folosesc o memorie pentru a câştiga viteză care se
numeşte cache. Memoria cache este o memorie relativ mică formată din
memorii SRAM, care memorează bucăţi de stări folosite des, mărind
astfel viteza de acces. Unele memorii SRAM (ex. L1 cache) se află în
procesor pe când altele (ex. L2 cache) sunt externe procesorului. O
memorie cache este un tablou care mapează adresele de memorie ale unor
locaţii şi conţinutul acestora. Cache-ul procesorului foloseşte o funcţie
simplă de căutare: extrage câţiva biţi din adresă pentru a-i indexa într-un
tablou şi apoi caută în paralel toate adresele care se potrivesc cu elementul
din tablou. Când o locaţie de memorie tebuie citită din DRAM ea este
stocată în cache şi un element deja existent din cache poate fi eliberat.
Datele folosite des sunt stocate într-o memorie cache de date în timp ce
instrucţiunile folosite des sunt stocate într-o memorie cache de intrucţiuni.
Memorarea in cache este eficientă dacă instrucţiunile şi datele prezintă o
localizare temporală (poziţia corespunzătoare este reutilizată frecvent într-
o perioadă scurtă de timp) sau o localizare spaţială (accesarea unei locaţii
este urmată de accesarea unei locaţii vecine). Localizările spaţiale sunt
avantajoase deoarece accesarea unei locaţii din DRAM implică accesarea
unei linii R şi apoi a unei coloane din acea linie. Deci citirea cuvintelor de
le linia R este mai puţin costisitoare după ce linia R a fost accesată. Astfel,
procesorul Pentium extrage anticipat 128 de biţi vecini în memoria cache
(mărimea unei linii din cache), de fiecare dată cand sunt accesaţi 32 de biţi
de date. Accesarea celor 96 de biţi adiacenţi nu va atrage o disfuncţie în
memoria cache.
Multe benchmark-uri (programe pentru testarea performantei calculelor)
prezintă localizări temporale şi spaţiale, dar în cazul fluxurilor de pachete
cel mai probabil se va întâlni doar fenomenul localizărilor spaţiale.
Imbunătăţirea performanţelor protocoalelor nodurilor terminale necesita o
analiza atenta a efectelor memoriei cache.
În figura 2.8 se prezintă arhitectura unui nod terminal:

CPU Memorie

MMU, Cache
Magistrală sistem

Adaptor magistrală

Interfaţă reţea

Magistrală I/E

Figura 2.8: Arhitectura unei staţii de lucru

Procesorul, CPU, este conectat la o magistrală/bus (similara cu o retea ca


Ethernet, dar optimizata prin faptul ca dispozitivele sunt apropiate), prin
30
care comunică cu celelalte componente trimiţând mesaje pe magistrala.
Echipamentele de I/E sunt de regulă memory mapped, adică pentru
procesor toate dispozitivele de I/E (placa de reţea sau discul) reprezintă
locaţii de memorie. Astfel se asigură o comunicare uniformă între CPU şi
orice echipament, folosind aceleaşi convenţii ca la accesarea memoriei. Cu
terminologia din retele, o citire/scriere se poate vedea ca un mesaj trimis
pe magistrală, adresat unei locatii de memorie, locatie la care se poate afla
si un dispozitiv de I/E.
Maşinile moderne permit accesul direct la memorie DMA (Direct
Memory Acces), prin care echipamente precum discul/adaptorul de reţea
trimit datele de citit/scris pe magistrală, direct la memorie fără intervenţia
procesorului. Dar la un moment dat doar o entitate poate folosi magistrala,
motiv pentru care adaptorul trebuie să concureze pentru magistrală.
Fiecare echipament care ocupă magistrala va „fura cicluri” procesorului,
deoarece procesorul e obligat sa astepte pentru accesul la memorie cat
timp dispozitivul trimite mesaje pe magistrala.
Tot în figura 2.8 se observă că adaptorul de magistrală este conectat la
magistrale diferite (magistrala de sistem sau de memorie) iar toate
echipamentele sunt conectate la magistrala de I/E. Magistrala de memorie
este proiectată pentru viteze mari, şi este reproiectată pentru fiecare
procesor nou. Magistrala de I/E este standard (ex. magistrala PCI) ea fiind
aleasă pentru a rămâne compatibilă şi cu echipamentele de I/E mai vechi,
astfel ca ea este mai lentă decât cea de memorie.
Trebuie reţinut că rata de transfer a unei aplicaţii de reţea este limitată
de viteza celei mai lente dintre magistrale, de obicei magistrala de I/E.
Încă şi mai rău este faptul că sunt necesare şi copii suplimentare pentru
diverse sisteme de operare, astfel ca fiecare pachet transmis sau
recepţionat de staţia de lucru va traversa magistrala de cateva ori.(vezi
capitolul 5 pentru tehnicile de evitare a traversării multiple a magistralei).
Procesoarele moderne folosesc tehnica pipeline/seriala pentru extragerea
instrucţiunii, decodarea instrucţiunii, citirea şi scrierea datelor, care sunt
divizate în mai multe stări intermediare. Maşinile superscalare şi cele cu
multiple fire de execuţie/multithreaded trec dincolo de tehnica pipeline
prin tratarea mai multor instrucţiuni în acelaşi timp în mod concurenţial. În
timp ce aceste metode înlătura congestia cauzata de calcule, ele nu
îmbunătăţesc congestia cauzata de transferul de date. Se consideră
următorul exemplu de arhitectura:
Exemplul 6: arhitectură de nod terminal cu comutator crossbar: in figura
2.9 magistrala nodului terminal este înlocuită de un comutator hardware
programabil, folosit deseori la rutere.
Comutatorul conţine un număr de magistrale paralele a.î. fiecare set de
perechi de puncte terminale disjuncte poate fi conectat în paralel prin
intermediul comutatorului. Astfel, în figură, procesorul este conectat la
memoria 1, în timp ce placa de reţea este conectată la memoria 2. În acest
fel, pachetele care sosesc din reţea pot fi stocate în memoria 2 fără a
interfera cu procesorul care citeşte din memoria 1. În momentul în care
procesorul doreşte să citească pachetul de date sosit, comutatorul poate fi
reprogramat pentru a conecta procesorul cu memoria 2 şi placa de reţea cu
memoria 1.

31
Comutator programabil
paralel

Procesor Memorie 1

Placă de reţea Memorie 2

Figura 2.9. Folosirea conexiunilor paralele într-un nod terminal pentru a permite
procesarea concurentă şi trafic de reţea printr-un comutator paralel.

Acest mod de funcţionare este eficient dacă coada bufferelor de pachete


goale, folosita de placa de reţea, alternează între cele două memorii. Există
propuneri recente de înlocuire a magistralei de I/E din procesoare cu
tehnologia de comutare Infiniband. Ideea ce trebuie retinuta din acest
exemplu este ca, nu este neaparat buna arhitectura in fig.2.9, ci schimbari
simpe ale arhitecturii pot fi concepute chiar si de catre proiectantii de
protocoale, conform unor modele simple de hardware si arhitecturi.

2.3.2 Arhitectura ruterelor


Modelul unui ruter care acoperă atât ruterele high-end (seria de rutere
Juniper M) precum cat şi ruterele low-end (Cisco Catalyst), este
prezentat în figura 2.10. În principiu, un ruter este un echipament cu un
set de legaturi de intrare, şi un set de legături de ieşire. Sarcina ruterului
este de a comuta pachetul de pe legătura de intrare pe legătura de ieşire
corespunzătoare adresei destinaţie din pachet. În timp ce legăturile de
intrari şi de ieşire sunt reprezentate separat, cele două legături în fiecare
direcţie între două rutere sunt deseori împachetate împreună. Există trei
congestii principale într-un ruter: căutarea adresei, comutarea şi coada de
ieşire.

Ruter
B2
Legături
Comutare de ieşire

B3

Legătura de
intrare i

Planificare
B1
Căutare adresă

Figura 2.10 Model de ruter cu cele 3 congestii principale in calea de inaintare:


căutarea adresei (B1), comutarea (B2) şi planificarea ieşirii (B3).
32
Căutarea adresei (lookup)
Un pachet care soseşte pe legătura de intrare i poartă o adresă IP de 32
biţi (desi majoritatea utilizatorilor lucreaza cu nume de domenii, acestea
sunt translatate in adrese Ipde serviciul DNS, inainte ca pachetul sa fie
transmis). Presupunem că primii 6 biţi ai adresei destinaţie din pachet sunt
100100. Un procesor din ruter va inspecta adresa destinaţie pentru a
determina încotro să dirijeze pachetul în cauză.
Procesorul consultă o tabelă de rutare/inaintare pentru a determina
legătura de ieşire pentru pachet. Acest tabel mai poartă denumirea de FIB
(Forwarding Information Base) – baza de informaţii pentru dirijare. FIB
conţine un set de prefixe şi legatura de ieşire corespunzătoare. Un prefix
poate fi privit ca fiind un „cod de judeţ/zona” de lungime variabilă care
reduce substanţial mărimea tabelei FIB. Un prefix ca 01 ∗ (unde ∗ poate
insemna oricâte alte simboluri), se potriveşte cu toate adresele IP care
încep cu 01.
Presupunem un prefix 100 ∗ care are atribuită legătura de ieşire 6, în
timp ce prefixul 1 ∗ are atribuită ieşirea 2. În acest caz adresa 100100 se
potriveşte cu ambele prefixe şi avem o problemă de ambiguitate. Pentru
înlăturarea incertitudinii, ruterele IP asociază adresa cu prefixul cel mai
lung/the longest prefix matching cu care se potriveşte, astfel că în
exemplul de faţă se va alege legătura de ieşire 6 pentru pachetul de date.
Procesorul care realizează căutarea/identificarea şi procesarea de bază
asupra pachetelor poate să fie un procesor dedicat/partajat de uz
general/chip specializat. Ruterele din primele generaţii foloseau
procesoare partajate, dar acest lucru s-a dovedit a fi o congestie.
Generaţiile mai noi de rutere (familia Cisco GSR) folosesc un procesor
dedicat pentru fiecare legătură de intrare. La început s-au folosit
procesoare de uz general, dar mai nou (Juniper M-160) se folosesc chipuri
specializate (ASIC) cu un oarecare grad de programabilitate.Clientii
pretind ruterelor functii noi, ca echilibrarea incarcarii Web. Ruterele mai
noi folosesc procesoare de reţea (exemplul 7), care sunt procesoare de uz
general optimizate pentru reţele.
Exista o serie de algoritmi folosiţi pentru identificarea prefixului. Multe
rutere de azi oferă un mod de căutare mai complex numit clasificarea
pachetelor, unde pentru căutare se foloseşte ca intrare adresa destinaţie,
adresa sursă şi portul TCP.

Comutarea
După identificarea adresei, procesorul spune sistemului de comutaţie
intern să transfere pachetul de pe intrarea i spre ieşirea corespunzătoare
(ieşirea 6 din exemplu). În procesoarele mai vechi, comutatorul era o
simplă magistrală, ca în figura 2.8. Acest mod de abordare s-a dovedit a fi
o mare limitare de viteză, deoarece, daca comutatorul are N intrări, fiecare
cu B biţi/s, magistrala trebuia să aibă o lăţime de bandă de B·N. Pe măsură
ce N creşte, efectele electrice (capacitatea electrica a magistralei) vor
deveni predominante, limitând viteza magistralei.
Din acest motiv ruterele cele mai rapide de azi folosesc un comutator
paralel (figura 2.9). Rata de tranfer a comutatorului creşte folosind N
magistrale paralele, una pentru fiecare intrare şi una pentru fiecare ieşire.
33
O intrare este conectată cu o ieşire prin comandarea tranzistoarelor care
conectează magistralele de intrare şi ieşire corespunzătoare. În timp ce
realizarea legăturii între magistrale este relativ simplă, partea mai dificilă
este planificarea comutatorului, deoarece mai multe intrari ar putea să
dorească să comunice cu o anumită ieşire în acelaşi timp. Problema
planificării comutatorului se rezumă la identificarea intrărilor şi ieşirilor
disponibile la fiecare interval de sosire a pachetelor.

Memorarea în coada de aşteptare


Dupa ce pachetul (figura 2.10) a fost identificat şi comutat spre ieşirea 6,
ieşirea 6 s-ar putea să fie congestionată, şi deci pachetul s-ar putea să
trebuiască să fie pus într-o coadă de aşteptare pentru ieşirea 6. Majoritatea
ruterelor mai vechi pun pur si simplu pachetele într-o coadă de transmisie
de tip FIFO. Alte rutere folosesc insa metode mai sofisticate de planificare
a ieşirii pentru a asigura alocări echitabile de bandă şi întârziere.
Pe lângă căutare, comutare şi memorarea în coada de aşteptare mai
există un număr de alte sarcini care trebuie efectuate, mai putin critice ca
timp.

Validarea antetului şi sumele de control


Este verificat numărul versiunii fiecărui pachet şi pentru opţiuni este
verificata lungimea antetului. Opţiunile sunt directive de procesare
suplimentare către procesor, dar care sunt folosite rar; astfel de pachete
sunt de cele mai multe ori şuntate spre un procesor special. Şi antetul are o
sumă de control simplă ce trebuie validată. În cele din urmă mai trebuie
decrementat contorul de timp de viata TTL (time to live) şi suma de
control a antetului trebuie recalculată. Suma de control se poate actualiza
incremental. Validarea antetului şi calculul sumelor de control se fac prin
hardware.

Procesarea rutei
Ruterele din interiorul unor domenii implementează RIP şi OSPF, în
timp ce ruterele care leagă domenii diferite trebuie să implementeze şi
BGP(*). Aceste protocoale sunt implementate în unul sau mai multe
procesoare de rutare. De exemplu dacă un pachet de stare a legăturii este
trimis spre ruterul din figura 2.10, procesul de căutare va recunoşte că
acest pachet este destinat ruterului însuşi şi va determina comutarea
pachetului spre procesorul de rutare. Procesorul de rutare menţine baza de
date cu starea legăturilor şi calculează drumurile cele mai scurte. După
calcul, procesorul de rutare încarcă noua tabelă de rutare în fiecare dintre
procesoarele de rutare, fie prin comutator, fie printr-o cale separată din
afara benzii. La început CISCO procesa nu numai pachete Internet dar şi
alte protocoale precum DECNET, SNA sau Appletalk. La ora actuală s-a
introdus comutarea multi-protocol după etichetă (MPLS – Multi Protocol
Label Switch) care este esenţială pentru ruterele core, ale retelelor de
tranzit. În MPLS, antetului IP i se adauga un alt antet care conţine indici
întregi simpli, care pot fi identificaţi direct fără cautarea prefixului.
(*)E posibilla achizitionarea acestor protocoale, dar software-ul trebuie personalizat
conform cu noile platforme hardware. O problema si mai delicata este, in special pentru
BGP si OSPF, ca multe dintre primele implementari ale acestora variaza in mod subtil
fata de specificatiile actuale. Noile implementari care indeplinesc specificatiile ar putea sa
34
nu poata interopera cu ruterele existente. Astfel ca ISP-urile opun rezistenta la
cumpararea noilor rutere, daca acestea nu asigura „calitatea”codului BGP, in sensul
capacitatii de interoperare cu ruterele existente.

Procesarea protocolului
Toate ruterele din ziua de azi trebuie să implementeze protocolul SNMP
(Simple Network Management Protocol) şi să furnizeze un set de contoare
care să poată fi verificate de la distanţă. Pentru a permite comunicarea la
distanţă cu ruterele, majoritatea ruterelor implementează şi protocoalele
TCP şi UDP. Pe lângă acestea ele mai trebuie să implementeze protocolul
ICMP (Internet Control Message Protocol) care este de fapt un protocol de
transmisie a mesajelor de eroare, precum „depăşirea timpului de viata
TTL”.

Fragmentarea, redirecţionarile şi ARP


Este clar că procesarea rutei şi a protocolului este îndeplinită cel mai
bine de un procesor de rutare pe o aşa numită „cale înceată”, dar sunt
cateva funcţii mai ambigue ale ruterului. De exemplu dacă un pachet de
4000 octeţi trebuie transmis pe o legătură cu o limită maximă a pachetelor
MTU (Maximum Transfer Unit) de 1500 octeţi, pachetul va trebui să fie
fragmentat in doua. Desi aceasta fragmentare este in general facuta de
sursa, multe rutere fac acest lucru in calea rapida. O altă funcţie este cea de
trimiteri de Redirecţionări. Dacă un nod terminal trimite un mesaj spre un
ruter greşit, atunci ruterul trebuie să trimită înapoi o redirecţionare
sprenodul terminal. O a treia funcţie de acest fel este transmiterea cererilor
de identificare a adresei ARP (Adress Resolution Protocol). Ruterele din
ziua de azi mai au şi alte funcţii care trebuie rezolvate. Multe rutere din
întreprinderi manevrează pachetele pe baza conţinutului, astfel că
procesarea de pachet se face în funcţie de şirurile de caractere din pachet.
De exemplu ruterele care primesc traficul Web pentru mai multe servere ar
putea sa doreasca sa transmita pachetele cu acelasi URL aceluiasi server
Web. In plus, mai există şi funcţiile de măsurarea şi contorizare a
traficului.

Exemplul 7: Procesoare de reţea: sunt procesoare programabile de uz


general, optimizate pentru traficul de reţea. Crearea lor a fost justificata de
caracterul impredictibil al naturii sarcinilor ruterelor (ca livrarea pe baza
continutului), ceea ce ar face riscanta crearea unor rutere implementate
exclusiv hardware. Procesorul de reţea Intel IXP1200 conţine şase
procesoare interne, fiecare rulând cu 177MHz, şi având un ciclu de tact de
5.6ns. Fiecare procesor recepţionează pachete de la o coadă de intrare
(pachetele sunt stocate într-o memorie DRAM mare). După ce procesorul
a identificat legătura de ieşire, pachetul este pus în coada de ieşire cu o
etichetă ce specifică legătura de ieşire spre care trebuie dirijat.
Problema principală este că procesoarele sunt responsabile de transferul
pachetelor în/din memoria DRAM. La IPX1200 mutarea a 32 de octeţi din
coadă spre DRAM necesită 45 tacte şi 55 de tacte pentru mutarea din
DRAM în coadă. Deoarece dimensiunea minimă a unui pachet este de 40
octeţi sunt necesare 200 cicluri de tact = 1.12 μ s, ceea ce se traduce într-o
rată de rutare de numai 900K pachete/s. Acest neajuns se înlătura folosind
6 astfel de procesoare paralele şi o idee numită multithreading/fire de
35
execuţie multiple. Ideea principală este că fiecare procesor lucrează pe mai
multe pachete, fiecare pachet reprezentând un fir de execuţie; când
procesarea unui pachet stagnează din cauza referirii la memorie se reia
execuţia următorului fir de execuţie. Folosind comutarea rapidă de context
între firele de execuţie şi patru contexte per procesor, procesorul IPX1200
poate obţine teoretic 6 ∗ 4 ∗ 900 = 21.4 Mpachete/s.
Procesoarele de reţea oferă şi instrucţiuni speciale pentru identificarea
adreselor şi funcţii uzuale de dirijare. Unele procesoare de reţea
fluentizeaza miscarea pachetelor de date folosind motoare hardware care
arată procesorului doar antetul fiecărui pachet. Ceea ce rămâne din
pachetul de date este transferat direct spre coada de ieşire.
Procesorul/procesoarele citeste antetul, identifică legătura de ieşire şi scrie
antetul reactualizat în coada de ieşire. Partea hardware concatenează
antetul reactualizat şi pachetul original şi păstrează toate pachetele în
ordine. Desi această metodă evită transferarea prin procesor a datelor din
pachet, ea nu îmbunătăţeşte tratarea pachetelor de dimensiune minimă.

Studiu de caz 2: Memorarea in tampoane şi comutarea optică.


Legăturile pe fibră optică oferă viteze din ce în ce mai mari, dar partea
implementarea electronică a logicii şi memoriile din ruterele core
reprezintă o strangulare. Pachetele sosesc pe legături de fibră optică,
fiecare bit fiind codat ca un impuls luminos. La receptor se converteşte
lumina în semnal electric şi apoi partea electronică implementează
comutarea şi rutarea. Pachetul este depus într-o coadă de ieşire din care
biţii sunt convertiţi iar în impulsuri luminoase. Pentru înlăturarea
congestiei se pot crea rutere total optice.
Din păcate identificarea optica a adresei IP şi mai ales realizarea unor
memorii optice dense pentru pachete este încă destul de dificilă, dar
comutarea luminii între diferite noduri terminale este realizabilă. Un
comutator optic de circuit, conecteaza intrarea X cu ieşirea Y pe o durată
relativ lungă, comparativ cu durata unui singur pachet din comutatoarele
de pachete. Astfel de comutatoare de circuit sunt folosite ca rutere core
flexibile din reţeaua unui ISP (Internet Service Provider) pentru a
interconecta ruterele convenţionale. Dacă de exemplu, traficul dintre
ruterul R1 şi R2 creşte, un operator de servicii internet poate schimba
comutatoarele de circuit spre a mări banda legăturii R1–R2 (pe durata mai
multor minute). Ruterele cu comutare de pachete probabil ca vor exista
încă o perioadă lungă în viitor, ţinând cont de caile rezervate de fluxurile
de scurta durata.

2.4. Sistemele de operare


Un sistem de operare OS (Operating System) este software-ul care sta
deasupra componentelor hardware-ului facilitand sarcina programatorilor
de aplicatii. Pentru cele mai multe rutere de Internet, pachetele la care
timpul de transmisie este critic sunt tratate direct prin hardware (fig. 2.10)
si nu sunt mediate de sistemul de operare. Coduri care sunt mai putin
critice ca timp ruleaza pe sistemul de operare al unui ruter, descris mai jos
(IOS Cisco). Totusi, pentru a imbunatati performantele cap-la-cap pentru
navigare Web, de exemplu, un proiectant trebuie sa inteleaga costurile si
beneficiile folosirii sistemului de operare.
36
Abstractizarile sunt idealizari sau iluzii pe care le inventam pentru a ne
descurca cu ciudateniile lumii reale. Pentru a reduce dificultatile, sistemul
de operare ofera abstractizari pentru programatorii aplicatiilor. La lucrul
cu hardware-ul apar trei probleme importante: intreruperile, administrarea
memoriei, si controlul dispozitivelor de I/O. Pentru a trata aceste
dificultati, sistemele de operare ofera abstractizari cum sunt calculul
neafectat de intreruperi, memoria infinita si I/O simple.
O abstractizare corecta creste productivitatea programatorilor, dar are
doua costuri. Primul, mecanismul de implementare a abstractizarii are un
pret. De exemplu, planificarea proceselor poate cauza supraincarcarea
serverului Web. Al doilea cost, mai putin evident, este acela ca
abstractizarea poate masca puterea, impiedicand programatorul sa
foloseasca optim resursele. De exemplu, managementul memoriei la un
sistem de operare, poate impiedica programatorul unui algoritm de cautare
pe internet sa-si tina in memorie structura de date a cautarii pentru a
maximiza performantele sistemului.

2.4.1. Calculul neintrerupt prin procese


Un program nu poate rula foarte mult pe un procesor fara a fi intrerupt
de adaptorul de retea. Daca programatorii aplicatiilor trebuie sa foloseasca
intreruperi, rularea unui program de 100 de linii de cod ar fi un miracol. In
consecinta sistemele de operare ofera programatorilor abstractizarea unui
calcul secvential neintrerupt sub numele de proces.
Abstractizarea procesului este realizata prin trei mecanisme: comutarea
contextului, planificarea si protectia, primele doua fiind descrise in figura
2.11. In figura, procesul P1 are iluzia ca el ruleaza singur pe procesor . In
realitate, cum se vede si pe axa timpului, procesul P1 poate fi interupt de o
intrerupere de ceas, care lanseaza in lucru pe procesor planificatorul OS .
Inlocuirea lui P1 pretinde OS-ului sa-i salveze starea in memorie.
Planificatorul poate rula rapid si decide sa-i dea controlul procesului P2.

Figura 2.11 Programatorul are iluzia timpului continuu, neintrerupt,dar timpul


procesorului e impartit intre mai multe procese

Relansarea in rulaj a procesului P2 necesita readucerea starii lui P2 din


memorie in procesor. Astfel ca actuala axa a timpului pentru procesor
poate implica schimbari frecvente de context intre procese, dirijate de
planificator. In plus, protectia asigura ca anumite comportamente incorecte
sau daunatoare al unui proces sa nu poata afecta alte procese.

37
Figura 2.12 Procesul de primire a pachetelor de pe internet este impartit intre adaptorul
de retea, nucleu si procesul destinatie

Ca agenti ai calculului, “procesele” sunt de trei tipuri – manipulatorul de


intreruperi, firele de procese si procesele utilizator – clasificate in ordinea
cresterii gradului de generalitate si al costului. Manipulatoarele/handler-e
de intreruperi sunt programe mici folosite pentru rezolvarea cererilor
urgente, cum ar fi sosirea unor mesaje la placa de retea; ele au un numar
redus de stari, folosind doar cateva registre de stare. Procesele utilizator
folosesc complet starile masinii, memoria si registrele; astfel este scumpa
comutarea intre procesele utilizator, la comanda planificatorului. In
cadrul unui singur proces, firele de procese ofera o alternativa mai ieftina
decat procesele. Un fir de proces este un proces simplu care ocupa mai
putine registre de stare, deoarece firele aceluiasi proces partajeaza aceeasi
memorie (si deci aceleasi variabile) . Astfel comutarea contextului intre
doua fire de executie ale aceluiasi proces este mai ieftina decat comutarea
proceselor, deoarece memoria nu trebuie sa fie realocata. Urmatorul
exemplu arata relevanta acestor concepte pentru nodului terminale ale
retelei.
Exemplul 8. Receptorul Livelock in BSD Unix. In BSD UNIX (fig.
2.12), sosirea unui pachet genereaza o intrerupere. Intreruperea este un
semnal hardware care determina procesorul sa salveze starea proceselui
ce ruleaza (de exemplu , un program Java). Procesorul sare apoi la codul
manipulatorului de intreruperi, si evita planificatorul pentru a creste viteza.
Manipulatorul de intreruperi copiaza pachetul in nucleu, intr-o coada
pentru pachete IP in asteptarea servirii, si face o cerere pentru un fir de
executie al sistemului de operare (numita intrerupere software), si iese.
Presupunand ca nu mai apar alte intreruperi, la iesirea din intrerupere se
cedeaza controlul planificatorului, care probabil va ceda procesorul
intreruperii software, care are prioritate mai mare decat procesele
utilizator.
Firele de executie ale nucleului fac o procesare de tip TCP si IP si
memoreaza pachetul in coada de aplicatii adecvata, numita coada socket
(fig. 2.12). Sa presupunem ca aplicatia este un browser cum ar fi Netscape.
Netscape ruleaza ca un proces adormit in asteptarea datelor si acum este
considerat de planificator ca fiind in rulaj pe procesor. Dupa ce se termina
38
intreruperea software si controlul revine planificatorului, planificatorul
poate decide sa ruleze Netscape in locul programului initial Java.
Daca reteaua e foarte incarcata, calculatorul poate intra in starea numita
receiver livelock, in care petrece tot timpul procesand pachetele care
sosesc, numai pentru a le arunca mai tarziu deoarece aplicatiile nu ruleaza
niciodata. In exemplul nostru, daca pachetele sosesc unul dupa altul,
planificatorul pentru intreruperi va trata doar prioritatea cea mai mare, si
este posibil sa nu mai ramina timp pentru intreruperi software si sigur nu
ramine deloc timp pentru procesul de navigare. Deci se vor umple ori
cozile IP ori cele socket, si pachetele vor fi aruncate, dupa ce au fost
investite resurse in prelucrarea lor.
Intarzierea si debitul codului de retea din nodul terminal al unei retele
depind de timpii de activare ai proceselui. De exemplu, datele actuale arata
ca pentru procesoarele Pentium IV, latenta intreruperii este in jur de 2µs
pentru un apel nul de intrerupere, in jur de 10µs pentru un comutator
Context Process de pe o masina Linux cu doua procese, si mult mai mult
timp pentru Windows si Solaris pe aceeasi masina. Acesti timpi par mici,
dar in 10 µs pot ajunge 30 de pachete de dimensiune minima (40-bytes)
pe o legatura Gigabit Ethernet.

2.4.2 Memoria infinita folosind memoria virtuala


La memoria virtuala (fig.2.13), programatorul lucreaza cu o
abstractizare a memoriei care consta dintr-o zona liniara, in care
compilatorul asigneaza diferite locatii. Variabila X poate fi stocata in
locatia 1010 in aceasta zona imaginara (sau virtuala). Abstractizarea
memoriei virtuale este implementata folosind mecanismul dublu, de
asociere de tabele de pagini si cerere de pagini. Intelegerea ambelor
mecanisme este cruciala pentru optimizarea costulului transferului datelor
prin nodul terminal.
Orice adresa virtuala trebuie sa corespunda unei adrese fizice de
memorie. Cea mai usoara mapare este folosirea unei deplasari (offset) in
memoria fizica. De exemplu, o zona virtuala de 15.000 de locatii, poate fi
mapata in memoria fizica de la locatia 12.000 pana la 27.000. Aceasta are
doua dezavantaje: mai intai, cand programul ruleaza, trebuie gasite 15.000
de locatii alaturate. Apoi, programatorul nu are voie sa foloseasca o
memorie mai mare decat cea fizica.

39
Figura 2.13 Programatorul are iluzia unei memorii virtuale continue; in realitate este
asociata din parti ale memoriei principale si pagini de pe discul de memorie, folosind
tabele de pagini

Ambele probleme pot fi evitate prin maparea bazata pe o tabela de


cautare (table lookup). Deoarece implementarea maparii dintre orice
locatie virtuala catre orice locatie fizica consuma prea multa memorie, se
foloseste o mapare mult mai resctrictiva bazata pe pagini este folosita.
Consideram ca pentru orice adresa virtuala, bitii cei mai semnificativi(de
exemplu 20 de biti) dau numarul paginii iar cei mai putin semnificativi
(de exemplu 12 de biti) dau pozitia din interiorul paginii. Toate locatiile
din interiorul unei pagini virtuale sunt mapate fata de aceeasi locatie
relativa, dar paginile virtuale individuale pot fi mapate la locatii arbitrare.
Memoria principala este de asemenea divizata in pagini fizice, astfel ca
fiecare grup de 212 cuvinte de memorie constituie o pagina fizica.
Pentru a asocia o adresa virtuala cu una fizica, pagina virtuala
corespunzatoare (in exemplu, cei 20 de biti cei mai semnificativi) este
asociata numarului unei pagini fizice, dar mentinand aceeasi locatie in
interiorul paginii. Asocierea se face prin cautarea in tabela de pagini,
indexata cu numarul paginii virtuale. O pagina virtuala poate fi localizata
in orice pagina a memoriei fizice. Mai general, unele pagini pot sa nu fie
rezidente in memorie (de exemplu, pagina virtuala 2 in fig. 2.13) si pot fi
marcate ca fiind pe disc. Cand este accesata o astfel de pagina, hardware-
ul va genera o exceptie, determinand sistemul de operare sa citeasca
pagina de pe disc intr-o pagina a memoriei principale. Acest mecanism
secundar se numeste cerere de pagini/demand paging.
Asocierea/cererea de pagini rezolva impreuna doua probleme: de
alocare si de limitare a alocarii memoriei . In loc sa rezolve problema mai
dificila de alocare a informatiilor stocate in locatii de marime variabila, OS
are nevoie doar sa tina minte o lista cu paginile libere de marime fixa si sa
asigneze noului program cateva pagini libere. Astfel, programatorul poate
lucra cu o memorie abstracta, cu dimensiunea limitata doar de capacitatea
discului si de numarul de biti de adresa ai instructiunii.
Maparea suplimentara poate incetini considerabil fiecare instructiune. O
citire/Read de locatie virtuala X, poate necesita doua accesari ale memoriei
principale: un acces la tabelul de pagini pentru a translata X catre o adresa
40
fizica P, urmat de o citire/Read a acestei adrese P. Procesoarele moderne
evita aceasta activitate suplimentara prin memorarea cache a celor mai
recente asocieri adrese virtuale-adrese fizice intr-un buffer TLB (traslation
lookaside buffer), care este o memorie cache rezidenta in procesor.
Translatia curenta (fig.2.8) o face o componenta hardware specializata
pentru managementul unitatii de memorie, numita MMU (memory
management unit).
Maparea tabelului de pagini ofera un mecanism de protejare inter-
procese. Cand un proces face o citire/Read a unei locatii virtuale X, daca
nu este o intrare prevazuta in tabelul de pagini, hardware-ul va genera o
exceptie de pagina defecta (page fault exception). Daca se prevede faptul
ca doar sistemul de operare sa poata schimba intrarile in tabelele de
pagini, sistemul de operare poate asigura ca nici un proces sa nu poata
citi/scrie neautorizat memoria altui proces.
In timp ce la transmiterea prin ruter se lucreaza direct cu memoria
fizica, toate nodurile finale si codurile serverelor de retea lucreaza cu
memoria virtuala. Dar desi memoria virtuala reprezinta un potential cost
(e.g.pierderi TLB), ea ofera si o oportunitate. Astfel e posibil sa se faca
mai eficienta copierea pachetelor intre sistemul de operare si aplicatii
folosind tabelele de pagini (exemplul 8).

2.4.3 I/O simple prin Apeluri de Sistem


Persoana care programeaza aplicatiile nu poate fi constienta de varietatea
si complexitatea fiecarui dispozitiv de I/O. De aceea sistemele de operare
ofera programatorului o abstractizare a dispozitivelor ca piese de memorie
care pot fi citite si scrise (fig.2.14).
Codul care asociaza apelul interfetei I/O simple unei citiri fizice a
dispozitivului (cu toti parametrii completati), este numit driver de
dispozitiv (device driver). Daca singura preocupare ar fi abstractizarea,
codul driverului de dispozitiv poate fi instalat intr-o biblioteca de coduri
comune disponibile, care pot fi “verificate” de fiecare aplicatie. Totusi, cat
timp dispozitive precum discul trebuie sa fie partajate intre toate
aplicatiile, daca aceste aplicatii ar controla direct discul, un proces eronat
ar putea strica discul. Proiectarea unui sistem de operare securizat necesita
insa ca numai aplicatiile proaste sa dea gres.
Astfel, are sens ca apelurile de I/O sa fie manevrate de catre driverele de
dispozitiv care se afla intr-o portiune securizata a sistemului de operare
care nu poate fi afectata de catre procesele care dau gres. Aceasta portiune
securizata, numita nucleu/kernel , furnizeaza cateva servicii esentiale, ca
I/O si actualizarea tabelelor de pagini, operatii pentru care nu exista
suficienta incredere in aplicatii, incat acestea sa le realizeze direct.
Cand un browser, ca Netscape, vrea sa acceseze discul pentru a citi o
pagina Web, trebuie sa faca un asa numit apel de sistem/system call peste
granitele nucleului de aplicatii. Apelarea sistemului este o forma protejata
de apelare a unei functii. Se spune ca instructiunea hardware este o
“capcana” pentru a ajunge la un nivel mai privilegiat (modul nucleu), care
permite accesul in interiorul sistemului de operare. Cand apelul functiei
revine dupa incheierea I/O, codul aplicatiei ruleaza cu un nivel normal de
privilegiu. Un apel de sistem este mult mai costisitor decat un apel de
functie, din cauza ca nu mai beneficiem de avantajul implementarii in
41
hardware si sunt necesare cautari suplimentare de parametri incorecti. Un
apel sistem simplu poate fi executat in cateva microsecunde pe masinile
moderne.

Figura 2.14 Programatorul vede dispozitivele, ca discul sau adaptorul de retea, ca piese
disparate ale memoriei, care pot fi citite/scrise utilizand apelul de sistem, dar de fapt
nucleul administreaza o gazda cu detalii specifice dispozitivului.

Relevanta pentru retele este ca atunci cand un browser doreste sa trimita


un mesaj prin retea (de exemplu procesul 2 din fig. 2.14), trebuie sa faca
un apel la sistem pentru a activa procesarea TCP. Cateva microseconde
pentru o apelare a sistemului poate parea putin, dar de fapt produce o
incarcare foarte mare pentru un Pentium rapid. Vom vedea daca aplicatiile
pot sa mareasca viteza retelei renuntand la apelurile la sistem, si in caz
afirmativ, daca se poate renunta si la protectia OS.

2.5 Sumar
S-a lucrat pe patru niveluri de abstractizare care afecteaza performanta:
hardware-ul, arhitectura, sistemul de operare si protocoalele. Acest fapt
este util deoarece viteza de procesare a pachetelor poate fi limitata: de
traseele cu tranzistoare care implementeaza procesarea pachetelor, de
limitarile arhitecturale ca viteza magistralei, de suprasolicitarea sistemului
generata de abstractizarile OS ca apelurile la sistem, si in final chiar de
mecanismele protocolului.
Proiectantii, care vor incerca sa tina cont de simultan de toate cele
patru nivele de abstractizare pentru fiecare problema, se vor pierde in
detalii. Totusi, sunt cateva rezultate importante referitoare la performanta
si decizii arhitecturale majore pentru care intelegerea simultana a tuturor
nivelelor de abstractizare este esentiala. De exemplu, modelele simple
prezentate in acest capitol permit lucrul impreuna al arhitectilor,
proiectantilor de circuite, de logica, de microcodoare si implementatorilor
de protocoale software pentru a crea rutere de clasa mondiala. Ele permit
proiectantilor de sisteme de operare, expertilor in algoritmi si celor care
creaza aplicatii sa lucreze impreuna pentru a concepe un server Web de
clasa. La viteza de 40 Gps pe legatura, echipele interdisciplinare devin
extrem de importante.
Exista un alt aspect al creativitatii. Am vorbit despre mari contributii
individuale, dar atunci cand creezi tehnologie este necesara o activitate in
grup, deoarece astazi tehnologia este atat de complexa incat trebuie sa fie
interdisciplinara…, in esenta, fiecare isi vorbeste propriul limbaj, chiar
42
daca despre aceleasi concepte. Stabilirea unui limbaj comun dureaza luni
de zile… o tehnica pentru a face oamenii sa-si depaseasca limitele
gandirii ar fi : se da o problema, de procesare a semnalelor lingvistilor, si
vice versa, fiind lasati sa aplice cunostintele cu care sunt familiarizati unei
probleme complet diferite. Abordarea rezultata este in majoritatea
cazurilor una complet noua, la care expertii in domeniul original nici nu s-
ar fi gandit. Munca in echipa da creativitatii o noua dimensiune.
2.6. Exercitii
1. Protocoalele TCP si atacuri de tip refuz de serviciu DOS (Denial-
Of-Service Attacks ). De obicei, un hacker incearca sa atace un serviciu
cunoscut, precum Yahoo, printr-un atac de tip DOS. Un atac DOS simplu
este cel de tip TCP Syn-flooding, in care hacker-ul trimite cateva pachete
SYN catre destinatia aleasa D (ex. Yahoo), folosind adrese sursa alese la
intamplare. D trimite inapoi un SYN-ACK catre presupusa sursa S si
asteapta raspunsul de la S. Daca S nu este o adresa IP activa, atunci nu va
primi un raspuns de la S. Din pacate, starea lui S este mentinuta in coada
de asteptare a conexiunilor lui D, pana la expirarea sa. Trimitand periodic
incercari false de conectare, pretinzand ca sunt de la surse diferite,
atacatorul se poate asigura ca la D, coada de asteptare finita va fi
intodeauna plina. Prin urmare, o cerere legitima de conectare spre D va fi
refuzata.
Presupunand ca exista un monitor care supravegheaza tot traficul, ce
algoritm poate fi folosit pentru a detecta atacurile de tip DOS ?
Presupunem ca programul monitor realizeaza ca are loc un atac prin
inundare TCP. De ce e greu sa deosebseti traficul legitim de cel prin
inundare?
2. Proiectarea numerica: Multiplexoarele si Barrel Shifters (*) sunt
foarte folositoare in partea hardware a retelei, deci lucrul la aceasta
problema poate ajuta chiar si un softist sa construiasca un hardware in mod
intuitiv.
(*) Barrel shifter este un dispozitiv hardware care poate depasa sau roti un cuvant de
date cu oricate pozitii intr-o singura operatie. E implementat ca un multiplexor, fiecare
iesire iesire putand fi conectata cu fiecare intrare
• Creati un multiplexor cu 2-intrari cu porti primare (SI,SAU,NU).
• Generalizati ideea creand un multiplexor cu N intrari din multiplexoare
cu N/2 intrari. Cu acestea descrieti un model cu log N intarzieri de porti si
O ( N ) tranzistoare.
• Aratati cum se creaza un barrel shifter folosind o reductie catre
multiplexoare (i.e. folositi cate multiplexoare aveti nevoie). In solutia
propusa, care sunt complexitatile timpului si portilor?
• Incercati sa creati un barrel shifter direct la nivel de tranzistor. Care
este complexitatea de tranzistor si de timp a lui? Creatia directa e mai buna
decat reducerea simpla folosita anterior?
3. Proiectarea memoriei: Pentru proiectarea schemei seriala/pipelined
de cautare a ID-ului descrisa anterior, desenati diagramele in timp pentru
cautarile din pipeline. Folositi numerele descrise in capitol, si schitati clar
o mostra de arbore binar cu 15 noduri si arata cum poate fi inchis dupa
patru cautari in patru blocuri diferite. Presupune un arbore binar nu un
arbore tertial. De asemenea, calculati numarul de chei care poate fi

43
suportat folosind 16 blocuri de RAMBUS daca primele k nivele al
arborelui sunt depozitate in SRAM-ul de pe chip.
4. Memorii si arbori pipeline: Aceasta problema studiaza cum sa faci
un pipeline unei stive/heap. O stiva/heap este importanta pentru aplicatii
precum QoS, unde un ruter doreste sa transmita primul, pachetul cu
eticheta de timp cea mai recenta. In consecinta are sens sa ai o stiva
aranjata dupa eticheta de timp. Pentru a o face eficienta stiva/heap, trebuie
sa fie serializata/pipelined in aceeasi maniera ca si exemplul de arbore
binar de cautare din capitol; totusi sa faci o stiva asa de mare este greu.
Figura 2.15 arata un exemplu de P-stiva capabila sa stocheze 15 chei. O P-
stiva este un arbore binar complet, ca si o stiva/heap standard, exceptand
faptul ca nodurile de oriunde din stiva/heap pot fi vide cat timp toti copiii
nodului sunt de asemenea vizi (ex. nodurile 6, 12, 13).
(*) colectie de obiecte, plasate una peste alta ; arbore binar cu n-noduri complet, in care
toate nodurile, altele decat nodul radacina, satisfac proprietatea de ’’heap’’ .

Figura 2.15 Un exemplu de insiruire in cinci poze care se citesc de la stanga la dreapta
si apoi de sus in jos. In fiecare poza indexul reprezinta adancimea a subarborelui si
pozitia este numarul nodului asa ca valoarea este adaugata lui.

Pentru urmatoarele explicatii consultati figurile 2.15 si 2.16. Considerati


adaugarea cheii 9 in stiva/heap. Presupunand ca fiecare nod N are un
numarul care reprezinta numarul de noduri goale din subarbore (care
pleaca din nodul radacina N). De vreme ce 9 este mai mic decat valoarea
radacina 16, 9 trebuie sa fie dedesubt. De vreme ce atat ramurile dreapta
cat si stanga au noduri goale in subarborii lor, am ales arbitrar sa adaugam
9 la subarborele stang (nodul 2). Indexul, valoarea si pozitia valorilor
aratate in figura la stanga fiecarui arbore sunt registrele folosite pentru a
arata starea operatiei curente. Astfel in figura 2.15, partea (b), cand 9 este
44
adaugat subarborelui stang, indexul reprezinta adancimea subarborelui
(adancimea 2) si pozitia este numarul nodului (i.e. nod 2) la care valoarea
9 este adaugata..
Pe urma, din moment ce 9 este mai putin dacat 14 si de vreme ce
doar copilul drept are spatiu in subarborele sau, 9 este adaugat
subarborelui inradacinat la nodul 5. Acum 9 este mai mare dacat 7, deci 7
este inlocuit cu 9(la nodul 5) si 7 este impins in jos la nodul liber 10. Chiar
daca in Figura 2.15, part(d), valoarea indexului este 4 (i.e. operatia este la
adancimea 4) si pozitia este 10. Desi in figura 2.15 doar unul dintre
registrii la index/adancime are informatie nevida, si tine registrii separati
pentru fiecare index este permis pipelining-ul.

Figura 2.16 Inlaturarea celui mai mare element

• Consideram operatia de scoatere din coada (indepartarea celui mai


mare element). Indeparteaza 16 si incearca sa impingi in jos golul creat
pana cand un subarbore vid este creat. Deci in pasul, 3 golul este mutat la
nodul 2 (deoarece valoare sa, 14, este mai mare decat fratele cu valoare
10), apoi la nodul 4 si in final la nodul 9. De fiecare data cand un gol este
mutat in jos, valoarea nevida de mai jos inlocuieste vechiul gol.
• Pentru a face operatia de incarcare in coada sa mearga corect, trebuie
mentinuta numararea nodurilor subarborilor goi. Explica sumar cum
numararea trebuie mentinuta pentru fiecare operatie de
introducere/extragere din coada (structura va fi serializata/pipelined la un
moment dat, deci numararea valorilor respecta scopul).
• Logic este sa serializam/pipeline per nivel. Totusi, e o problema. La
fiecare nivel (sa zicem, inserarea lui 9 in radacina ) operatia trebuie sa
consulte cei doi copii, de la nivelul urmator. Astfel, cand prima operatie
coboara la nivelul 2, nu se poate aduce a doua operatie la nivelul 1, sau va
45
fi o coliziune de memorie. Asteptarea ca operatia sa se termina complet va
functiona, dar se va reduce la procesarea secventiala a operatiilor. Care
este rata maxima la care poate fi serializata/pipeline o stiva/heap?
• Considera operatiile “introduce in coada 9; introduce in coada 4,5;
extrage din coada”serializte/pipelinedanterior. Deseneaza 6 stari
consecutive ale arborelui in timpul acestor 3 operatii.
• Presupune ca fiecare nivel de memorie este pe un SRAM pe-chip, care
are nevoie de 5 nanosecunde pentru accesarea memoriei. Presupune ca
poti citi, scrie valoarea si numerota campurile, impreuna intr-un singur
acces. Tine minte ca unele memorii pot fi interogate in paralel. Care este
valoarea stationara a debitului stivei/heap in operatiuni pe secunda?
• Poate fi imbunatatit numarul de referiri la memorie folosind un acces
de memorie mai extins si o plasare adecvata a arborelui?
• Inainte de a folosind o memorie mai extinsa si arborele adecvat,
proiectarile anterioare foloseau un element de memorie pentru fiecare
element din stiva/heap precum si logica aferenta. Astfel logica necesara
este direct proportionala cu marimea stivei/heap, care se adapteaza prost in
termeni de densitate si putere.In aceasta proiectare, memoria este
proportionala cu numarul de elemente ale stivei si deci cu densitatile si
puterea SRAM, dar logica este mult mai redusa. Explicati.
5. Arhitecturi, memorii-caches si functii hash rapide: Memoria cache
L1 din procesor furnizeaza in esenta o functie hash rapida, care mapeaza
de la adrese fizice de memorie spre continutul sau, prin memoria cache
L1. Presupunem ca vrem sa invatam un caine batran (memoria cache L1)
cu un nou truc (sa faca cautari de IP). Scopul este sa folosim memoria
cache L1 ca o tabela hash, ca sa mapam adrese IP de 32 de biti la numere
de porturi de 7 biti. Presupunem o memorie cache L1 de 16-KB, cu primii
4KB rezervati tabelei hash, si o marime a blocului cache de 32 bytes.
Presupune o masina adresabila pe octet, adrese virtuale de 32-biti si
marimea paginii de 4KB. Asadar sunt 512 blocuri de 32 bytes in cache.
Presupunem ca memoria cache L1, este indexata direct/direct mapped.
Deci bitii 5-13 ai unei adrese virtuale sunt folositi pentru a indexa unul din
cele 512 blocuri, cu bitii 0- 4 identificand octetul din fiecare bloc.
• In ipoteza ca paginile sunt de 4 KB si masina este adresabila pe byte,
cati bytes din adresa virtuala identifica pagina virtuala? Cati biti din
numarul paginii virtuale se intersecteaza cu bitii 5-13 folositi pentru
indexarea memoriei cache L1?
• Singura cale de a asigura ca tabela hash nu este aruncata afara din
memoria cache L1 cand sosesc alte pagini virtuale, este marcarea la
inceput a oricarei pagini, care s-ar putea pozitiona in aceiasi portiune a
memoriei cache L1, ca nedepozitabila in cache. Conform cu raspunsurile
anterioare si cu faptul ca tabelele hash folosesc primi 4KB a memoriei
cache L1, identifica precis care pagini trebuie marcate ca nedepozitabile.
• Pentru a face o cautare a unei adrese de IP de 32-byte, prima oara
convertiti adresa ca o adresa virtuala prin setarea pe 0 a tuturor bitilor
exceptand bitii 5-11 (bitii 12 si 13 sunt zero deoarece doar sfertul de sus a
L1 cache este folosit). Presupune ca aceasta este translatata in exact
aceeasi adresa fizica.Cand se face o Citire de la aceasta adresa, hardware-
ul L1 cache va returna continutul primelor cuvintelor de 32-biti ale
blocului cache corespunzator. Fiecare cuvant de 32-bit va contine o
46
eticheta de 25-biti si un port numeric de 7-biti. Pe urma, compara toti bits
din adresa IP, altii decat bitii 5-11, cu eticheta, si fa asta pentru fiecare
intrare de 32-biti din bloc.Cat de multe accesuri L1 cache sunt necesare in
cel mai rau caz pentru o cautare hash? De ce ar fi asta mai rapida decat o
cautare standard hash in software?
6. Sisteme de operare si procesarea relaxata a receptoarelor
lenese/lazy receiver processing: Exemplul 8 descrie cum protocoalele de
procesare BSD pot conduce la receptorul livelock. Procesarea relaxata a
receptoarelor combate aceasta problema prin doua mecanisme.
• Primul mechanism este sa inlocuim coada de asteptare de procesare IP
unica partajata, prin cate o coada separata per socket destinatie.De ce
aceasta ajuta? De ce aceasta nu ar putea fi usor de implementat?
• Mecanismul secund este implementarea procesarii de protocol la
prioritatea procesului de receptie, si ca parte a contextului procesului
receptionat (si nu ca o intrerupere software separata). De ce aceasta ajuta?
De ce aceasta nu poate fi usor de implementat?

47
CAPITOLUL 3

PRINCIPII DE IMPLEMENTARE
I- Principiile sistemelor
II- Cresterea eficientei, cu mentinerea modularitatii
III-Accelerarea

3.1. Motivarea folosirii principiilor

Actualizarea memoriilor ternare cu continut adresabil


Numim un sir ternar daca contine oricare din caracterele: 0, 1 sau *,
unde * denota un „wildcard” care se potriveste si cu un 0 si cu un 1.
Exemple de siruri ternare de lungime 3 sunt: S1=01* si S2=*1*; de fapt,
sirul binar 011 se potriveste si cu S1 si cu S2, in timp ce 111 se potriveste
doar cu S2.
Nu- Principiu Folosit in
mar
P1 Evitarea „risipei evidente” Interfete Zero-copy
P2 Schimbarea calcului in timp
P2a Precalculare Canale cu aplicatie pe dispozitiv
P2b Evaluare treptata Copiere pentru scriere
P2c Impartirea costurilor,a pachetelor Procesarea integrata in niveluri
P3 Cerinte pentru destinderea sistemului
P3a Compromis certitudine/timp Servirea stohastica corecta a
P3b cozilor de asteptare
P3c Compromis precizie/timp Comutare functie de incarcare
Schimbarea calcului in spatiu Fragmentare IPv6
P4 Egalizarea componentelor sistemului
P4a Exploatarea localizarii Receptor actionat local
P4b Compromis memorie - viteza Procesare; cautare de tip
P4c Exploatarea hardware-ului existent Lulea IP
Calcul rapid al sumei de
control TCP
P5 Suplimentare hardware
P5a Folosirea metodei de intretesere si Citire IP pipelined
pipeline in memorie
P5b Folosirea paralelismului extins Comutare cu memorie
P5c Combinarea eficace DRAM si partajata
SRAM Mentinerea contoarelor
Fig. 3.1 Sumarul principiilor 1-5 – Gandirea/conceptia sistemului

48
Numar Principiu Exemple de procese in retele
P6 Creare de rutine speciale eficiente Control UDP
P7 Evitarea generalitatilor inutile Fbufs
P8 Fii flexibil in implementare Upcalls
P9 Paseaza indicatii interfetelor dintre Filtre de pachet
niveluri
P10 Paseaza indicatii antetelor protocolului Comutarea etichetelor
Fig. 3.2 Sumarul principiilor 6-10 – Refacerea eficientei pastrand modularitatea

Numar Principiu Exemple de procese in retele


P11 Optimizarea cazului probabil Predictia antetului
P11a Folosirea memoriilor Cache Buffere rapide Fbufs
P12 Suplimentarea starilor pentru a creste Lista de circuite virtualeVC active
viteza
P12a Calcul incremental Recalcularea CRC- urilor
P13 Optimizarea „gradelor libertatii” Tentativa de cautare IP
P14 Folosirea sortarii bucket, a bitmap- Sincronizare
urilor
P15 Crearea de structuri de date eficiente Comutarea de nivel 4
Fig. 3.3 Sumarul principiilor 11-15 – accelerarea rutinelor cheie

Prefix Pasul
urmator
Liber Liber
010001* P5
110001* P5 P1
● ● 110001 P2
● ● P3
110* P3 P4
111* P2 P5
00* P1
01* P3
10* P4 Ruter
0* P4

Fig. 3.4. Exemplu utilizarii unei CAM ternare pentru cautari de prefixe

O memorie ternara cu continut adresabil(CAM) este o memorie


continand siruri ternare de lungime specifica, cu informatie asociata; cand
i se prezinta un sir la intrare, CAM va cauta in toate locatiile sale de
memorie in paralel pentru a genera la iesire(intr-un ciclu) locatie de
memorie cu cea mai mica dresa, al carei sir ternar se potriveste cu cheia de
intrare specicata.
Figura 3.4 arata o aplicatie de CAM ternara referindu-se la problema
celei mai extinse potriviri de prefix pentru router-ele de Internet. Pentru
fiecare pachet primit, fiecare router de Internet trebuie sa extraga o adresa
IP destinatie de 32 biti din pachetul de intrare si sa caute potrivirea intr-o
baza de date de prefixe IP pentru urmatorul nod la care sa fie trimis
pachetul. Un prefix IP este un sir ternar de lungime 32, in care toate
wildcard-urile se afla la sfarsit.Vom schimba notatia treptat si vom lasa *

49
sa indice orice numar de caractere wildcard astfel ca 101* se potriveste cu
10100 si nu doar cu 1010.
Astfel, in fig. 3.4, un pachet trimis la o adresa destinatie care incepe cu
010001 se potriveste prefixelor 010001* si 01* dar ar trebui trimis la
portul P5 deoarece expedierea pachetelor prin Internet cere ca acestea sa
fie trimise utilizand cea mai lunga potrivire.Vom vorbi mai mult despre
aceasta problema in Capitolul 11. Deocamdata, este de retinut faptul ca
prefixele sunt aranjate intr-o CAM ternara astfel incat toate prefixele mai
lungi sa fie inaintea tuturor prefixelor mai scurte(ca in fig. 3.4); CAM
ternara sustine potrivirea saltului urmator intr-un ciclu de memorie.
Desi CAM-urile ternare sunt extrem de rapide in trimiterea mesajului,
ele cer ca prefixele mai lungi sa apara inaintea prefixelor mai scurte. Dar,
protocoalele de rutare/dirijare adauga sau sterg prefixe deseori.
Sa presupunem ca in fig. 3.4 un nou prefix, 11* , cu urmatorul nod
indicat la portul P1 trebuie sa fie adaugat in baza de date a ruter-ului.
Calea cea mai simpla de a-l insera ar produce spatiu in grupul de prefixe
de lungime 2 prin inaintarea cu o pozitie a tuturor prefixelor de lungime 2
sau mai mare.
Din pacate, pentru o baza de date mare cu aproximativ 100000 prefixe
detinuta de un router tipic, ar dura 100000 de cicluri de memorie, care ar
determina o adaugare lenta a unui prefix. Am putea obtine sistematic o
solutie mai buna prin aplicarea urmatoarelor doua principii(descrise mai
tarziu in acest capitol, ca si principiile P13 si P15).
Intelegerea si exploatarea „gradelor libertatii”
Privind tabelul din stanga fig. 3.4, vedem ca toate prefixele de aceeasi
lungime sunt aranjate impreuna si toate prefixele de lungime i apar dupa
toate prefixele de lungime j>i.

Spatiu liber

Prefixe de lungime (i+1)

X Crearea unui gol aici


prin mutarea lui X in
pozitia lui Y
Prefixe de lungime i

Fig. 3.5 Cautarea unui loc pentru noul prefix mutand X in pozitia lui Y , ne cere repetat
sa gasim un loc pentru a-l muta pe Y.

50
Totusi in figura toate prefixele de aceeasi lungime sunt si ele sortate
dupa valoare. Astfel, 00* apare inainte de 01*, care apare inainte de 10*.
Dar acest lucru nu impune CAM-ului sa returneze corect potrivirea de
prefixe cele mai lungi; se cere numai ordonarea prefixelor de lungimi
diferite, nu ordonarea prefixelor de aceeasi lungime.
Din fig. 3.4 abstractizata ca fig. 3.5, se vede ca daca vrem sa adaugam o
inregistrare la inceputul grupului de prefixe cu lungime i, trebuie sa cream
un spatiu liber la sfarsitul grupului de prefixe cu lungime (i+1). Astfel
trebuie sa mutam inregistrarea X, care exista deja in aceasta pozitie la alta
pozitie. Daca mutam X cu un pas mai sus, vom fi fortati la solutia
anterioara ineficienta.
Oricum, observatia despre „gradele libertatii” ne permite sa plasam pe
X oriunde intre alte prefixe adiacente de lungime (i+1). Astfel, o alta idee
este sa mutam X la pozitia detinuta de Y, ultimul prefix de lungime
(i+2),ceea ce ne forteaza sa gasim o noua pozitie pentru Y. Cum facem?
Avem nevoie de al doilea principiu.

Folosirea tehnicilor algoritmice


Problema recurentei se impune de la sine: rezolvam o problema prin
impartirea ei in etape mai mici. In acest caz, noua problema de asignare a
lui Y pe o noua pozitie este „mai redusa” deoarece grupul de prefixe de
lungime (i+2) este mai aproape de spatiul liber din varful CAM-ului decat
grupul de prefixe de lungime (i+1). Astfel mutam Y la sfarsitul grupului
de prefixe de lungime (i+3), etc.
Deoarece recursivitatea este solutia fireasca de a gandi,o implementare
mai buna este sa incepem din varful CAM-ului si sa o parcurgem
descendent creand un gol la sfarsitul grupului de prefixe de lungime 1,
apoi creand un gol la sfarsitul grupului de prefixe de lungime 2, etc. pana
cand cream un gol la sfarsitul grupului de prefixe de lungime i. Astfel,
cazul cel mai defavorabil de accesari ale memoriei este 32-i, care este in
jur de 32 pentru un i foarte mic.
Putem sa exploatam mai departe gradele libertatii. In primul rand, in fig.
3.5 am presupus ca spatiul liber este in varful CAM-ului. Dar spatiul liber
poate fi plasat oriunde. In particular, poate fi plasat dupa prefixele de
lungime 16. Aceasta reduce la jumatate cazul cel mai defavorabil de
accesari ale memoriei.
Un grad de libertate mai sofisticat este urmatorul. Pana acum,
specificatia algoritmului de inserare in CAM cerea ca „un prefix de
lungime i trebuie sa apara inaintea unui prefix de lungime j daca i>j”. O
asemenea specificatie este suficienta pentru corectitudine dar nu si
necesara. De exemplu, 010* poate aparea inainte de 111001* deoarece nu
exista nici o adresa care sa se poata potrivi ambele prefixe.
Astfel o specificatie mai putin exacta este: „daca doua prefixe P si Q se
pot asocia aceleasi adrese, atunci P trebuie sa apara inaintea lui Q in CAM
doar daca P este mai lung decat Q”. Aceasta este folosita in Shah si Gupta
[SG01] pentru a reduce mai mult numarul cel mai defavorabil de accesari
ale memoriei prin inserarea unor baze de date practice.
Desi ultima imbunatatire e discutabila din cauza complexitatii, ea
conduce spre un alt principiu important. Noi, deseori impartim o problema
mare in subprobleme si transmitem altora subproblema pentru rezolvare
51
insotita de anumite specificatii. De exemplu, proiectantul de hardware a
CAM-ului poate transmite problema de actualizare a unui microcoder,
specificand ca prefixele mai lungi sa fie plasate inaintea celor mai scurte.
Dar, ca si mai inainte, o asemenea specificatie poate sa nu fie singura
cale de rezolvare a problemei originale. Astfel schimbarile in
specificatii(principiu P3) pot oferi o solutie mai eficienta. Bineinteles,
aceasta cere persoane curioase si increzatoare in fortele proprii, care
inteleg problema in ansamblul ei si au curajul sa puna intrebari riscante.

3.2. Algoritmi si studiul algoritmilor


Ar fi posibil sa argumentezi faptul ca exemplul anterior este o metoda
esentialmente algoritmica esentiala si nu necesita o gandire sistematica?
Problema analizei securitatii judiciare. In multe sisteme de detectie de
intruziune, un manager deseori gaseste ca un flux (definit prin antetul
pachetelor, spre exemplu o sursa de adrese IPare o comportare
necorespunzatoare, lucru constatat prin metode probabilistice. Spre
exemplu, o sursa care face o scanare a unui port poate fi identificata dupa
ce a trimis 100000 de pachete daunatoare la diferite masini intr-o subretea.
Desi exista metode de identificare a unor asemenea surse, o problema
este faptul ca evidenta (celor 100000 de pachete trimise de sursa) a
disparut in timpul in care sursa vinovata este identificata. Problema este
ca, controlul probabilistic cere acumularea unor stari(sa zicem, un tabel de
suspiciune) pentru fiecare pachet primit intr-o anumita perioada de timp,
inainte ca o sursa sa fie declarata suspecta. Daca o sursa e considerata
suspecta dupa 10 secunde, cum poate cineva sa revina in timp si sa
recupereze pachetele trimise in timpul celor 10 secunde?
Pentru aceasta, in fig. 3.6 tinem un sir cu ultimele 100000 pachete care
fusesera trimise de catre router. Cand un pachet este trimis mai departe,
vom adauga o copie a acestui pachet (sau doar un pointer spre pachet) in
capul sirului. Pentru a pastra sirul limitat, atunci cand acesta e plin, vom
sterge pachetele de la coada. Principala dificultate la aceasta schema este
atunci cand un flux vinovat este detectat ,pot fi multe alte fluxuri de
pachete in sir(fig. 3.6). Toate aceste pachete trebuiesc plasate in rezerva
judiciara pentru transmisia lor la un manager. Metoda naiva de cautare
printr-un buffer DRAM mare este foarte lenta.

52
Forward P
Pachetul P Test rapid
soseste pe probabilistic
fluxul F de suspiciune Adauga
copia lui P in
antet
Daca e stare de alerta, adauga F in tabel; F
Daca F e in tabel, stare de update Şir cu
ultimele
Tabel de F N
suspiciune pachete

Raport catre
manager periodic
sau dupa detectia Memoria
Mod căutare memorie
unui flux suspect judiciara
pentru pachete trimise cu
fluxul cu ID F, de adăugat
la memoria judiciara

Fig. 3.6 Pastrarea unui sir cu ultimele 100000 de pachete care contine informatii
judiciare despre fluxurile suspecte care fusesera trimise in trecut.

Abordarea didactica permite adaugarea unor structuri de indecsi pentru


cautarea rapida a ID-urilor fluxurilor. Spre exemplu, am putea mentine un
tabel de dispersie(hash-table) cu ID-urile fluxurilor, in care fiecare flux
este asociat cu o lista de pointeri ale tuturor pachetelor avand ID-ul in sir.
Cand un nou pachet este introdus in sir, ID-ul fluxului este cautat in
tabelul de dispersie si adresa noului pachet din sir este plasata la sfarsitul
listei fluxului. Bineinteles, atunci cand pachetele parasesc coada,
inregistrarile lor trebuiesc eliminate din lista, si lista poate fi lunga. Din
fericire, inregistrarea ce trebuie stearsa este sigur in capul sirului pentru
acel ID de fluxului.
In ciuda acestui fapt, abordarea didactica are cateva dificultati. Ea
adauga mai mult spatiu pentru a mentine aceste extra-siruri per ID si
spatiul poate fi crucial pentru implementari rapide. De asemenea, adauga
complexitate in procesarea pachetelor pentru a mentine tabelul de
dispersie si cere inregistrarea tuturor pachetelor fluxului in rezerva
judiciara inainte ca pachetul sa fie suprainscris de un pachet care soseste
cu alte 100000 de pachete mai tarziu. Mai degraba urmatoarea solutie
sistematica poate fi mai eleganta.
Solutie. Nu incercati imediat sa identificati toate pachetele fluxului F,
cand F este identificat, dar identifica-le relaxat atunci cand ajung la
sfarsitul sirului de pachete (fig. 3.7). Cand adaugam un pachet in capul
sirului, trebuie sa inlaturam un pachet de la sfarsitul sirului( atunci cand
sirul este plin).
Daca acel pachet ( sa zicem Q, vezi fig. 3.6) apartine fluxului F care
este in Tabelul de Suspiciuni si fluxul F a depasit niste praguri de
suspiciune, atunci adaugam pachetul Q la Rezerva judiciara. Rezerva
poate fi trimisa la un manager. Avem de facut doi pasi pentru procesarea
pachetului, unul pentru trimiterea pachetului mai departe si unul pentru
53
inlaturarea lui din sir. Dar acesti doi pasi pentru procesarea pachetului sunt
ceruti si in cazul abordarii didactice; pe de alta parte, schema eleganta cere
sa nu facem dispersarea si sa utilizam un minim de memorie(fara pointeri
intre 100000 pachete).

Test rapid probabi- Forward P


listic de suspiciune
Pachetul P
soseste pe Adauga o copie a lui
fluxul F P in cap

Daca e stare de alerta, adauga F in tabel; Sirul


Daca F e in tabel, stare de update ultimelor N
pachete
Tabel de
suspiciune

Q
Raport catre
manager periodic Memoria
sau dupa detectia Judiciara Daca pachetul Q
unui flux suspect al fluxului F este declarat
suspect in Tabelul de
suspiciune, adauga Q

Fig. 3.6 Pastrarea unui sir cu ultimele 100000 de pachete care contine informatii
judiciare despre fluxurile suspecte care fusesera trimise in trecut.

(proprietate P) (proprietate Q)
Subsistem 2 Subsistem 2

Specificaţii S Specificaţii W
mai slabe

Subsistem 1 Subsistem 1

Fig. 3.7 Pastrarea unui sir cu ultimele 100000 de pachete care contine informatii
judiciare despre fluxurile suspecte care fusesera trimise in trecut.

3.3 -Principiile sistemelor


I. Primele cinci principii folosesc faptul ca orice sistem este construit
din subsisteme. Abordarea intregului sistem ca o entitate unica poate duce
la imbunatatirea performantelor.
P1. Evitarea risipei evidente in situatii comune
In sisteme exista foarte multe cazuri de risipire a resurselor, ca de
exemplu atunci cand apar in mod repetat anumite secvente de operatii.
Unul din exemple este repetarea anumitor expresii, in cazul
compilatoarelor, altul este crearea de copii multiple, in cazul retelelor, in
diverse etape de procesare a pachetelor. De remarcat este faptul ca fiecare
operatie in sine nu constituie o risipa evidenta, ci doar repetarea
54
secventelor de operatii. Identificarea situatiilor care conduc la risipe este
mai mult o problema de intuitie, dar optimizarea poate fi facuta utilizand
programe de test specializate (benchmarks).
P2. Deplasarea in timp a calculelor
Sistemele pot fi abordate in timp si in spatiu. In spatiu, sistemele pot fi
privite ca fiind compuse din subsisteme, eventual distribuite geografic. In
spatiu, sistemele pot fi caracterizate la momente diferite de timp, incepind
cu momentului fabricatiei, momentul compilarii momentul initializarii
parametrilor, respectiv momentul rularii propriu zise. Exista trei metode de
baza referitoare la deplasarea in timp a calculelor:
P2a. Calculul anticipat (precalculul): anumite calcule se pot face
anticipat, pentru a realiza o economie de timp la momentul in care e
nevoie de ele.Un exemplu este cautarea in tabele (lookup table)
precalculate. Astfel, in loc sa calculam la momentul necesar o functie f, al
carui calcul dureaza mult, se creaza in avans o tabela de valori ale lui f
pentru fiecare din argumentele sale. Operatia de cautare in tabela este
oricum mai rapida. La retele se pot precalcula antetele IP sau TCP pentru
pachetele unei conexiuni; efortul de scriere a antetelor se reduce, deoarece
de la pachet la pachet se schimba doar putine dintre campurile antetului.
P2b. Evaluarea relaxata : se refera la aminarea in momentele critice, a
unor operatii consumatoare de timp, in speranta ca fie nu va mai fi nevoie
de ele, fie executia lor se va face intr-un moment in care sistemul nu mai
este atit de ocupat.
Un exemplu de evaluare relaxata este copiere pentru scriere (copy-on-
write) din sistemul de operare Mach. Presupunem ca avem un spatiu de
memorie ce contine o copie la adresa virtuala A si ca mai avem nevoie la
adresa B de un spatiu cu aceeasi copie, pentru o inscriere viitoare. Solutia
generala ar fi sa avem doua copii si la adresa A si la B, permitind astfel
inscrierea lor independenta. Metoda insa aloca pentru B o tabela care
indica paginile corespunzatoare din A. Doar cand procesul lui B are nevoie
sa faca o scriere intr-o anumita pagina, se creaza copia respectivei pagini
pentru B si are loc operatia de scriere propriu zisa. Sunt evitate astfel
copiile inutile, deoarece e de asteptat ca numarul de pagini scrise in B sa
fie mai mic decat numarul total de pagini.
Un exemplu din retele este sosirea unor octeti de date in alta ordine decat
cea asteptata de nodul X de destinatie.Este preferabil sa se modifice doar
acele secvente care vor fi de fapt citite, si nu toate secventele.
P2c. Impartirea efortului, se refera la plasarea operatiilor cu consumuri
mari in alte parti ale sistemului. Un exemplu important de operatii de
mare consum este prelucrarea in loturi (batching) unde este executia
comasata a unor instructiuni de consum mare este mai economica decat
executia lor separata. Procesarea in loturi se practica de multa vreme in
calculatoare, mai ales pe vremea calculatoarelor de capacitate si viteza
foarte mari (mainframes), inainte chiar de a aplica tehnicile de divizare a
timpului.
P3. Relaxarea conditiilor impuse initial sistemului
Cand se incepe proiectarea unui sistem, de sus in jos, functiile se
impart unor subsisteme. Dupa ce se impun conditii sistemelor si se
stabilesc interfetele, se trece la proiectarea separata a subsistemelor. Daca

55
apar dificultati la implementare, trebuie refacuta structura sistemului (vezi
fig. 3.8).
Uneori dificultatile pot fi rezolvate relaxand conditiile impuse unuia
dintre subsisteme. Se relaxeaza de exemplu conditia impusa subsistemului
1 (conditia S mai severa devine conditia W mai putin severa), dar in
schimb conditia impusa sistemului 2 devine mai severa (P mai putin
severa devine Q mai severa).
Pot fi puse in evidenta trei tehnici ce rezulta din principiul relaxarii
conditiilor impuse initial sistemului.
P3a. Compromisul siguranta-timp. Proiectantii de sisteme se leagana in
iluzia ca sistemele lor ofera o comportare determinista, cand de fapt
comportarea este probabilista. Astfel ca, atunci cand algoritmii
deterministi sunt prea lenti e bine sa apelam la metode aleatoare.
In sisteme, milioane de utilizatori Ethernet folosesc aleatorizarea pentru
transmiterea de pachete dupa coliziuni. In retele un exemplu de
aleatorizare este software-ul de masurare a traficului NetFlow al firmei
Cisco. Daca ruteru nu are capacitatea sa contorizeze tot traficul de sosire,
el va lucra cu esantioane aleatoare de trafic fiind capabile sa identifice
fluxurile mari prin mijloace statistice. Al dolilea exemplu in retele este
servirea corecta stohastica a cozilor de asteptare (stochastic fair queuing),
in care in loc sa se urmareasca exact conversatiile care trec prin ruter, ele
sunt urmarite probabilistic, folosind dispersarea (hashing).
P3b. Compromisul precizie - timp. La fel, analiza numerica ne induce
iluzia preciziei calculatoarelor. Astfel am putea relaxa conditia preciziei de
dragul vitezei. In sisteme, multe tehnici folosesc compresia cu pierderi
prin interpolare (vezi MPEG).
Pentru a inlocui impartirile cu deplasari se folosesc praguri
aproximative. In retele, anumiti algoritmi de planificare a pachetelor
folositi in rutere implica sortarea pachetelor dupa termenle de timp de
plecare ; in retelele de viteze mari exista propuneri de reducere a antetelor
de sortare printr-o sortare aproximativa, care desi mai scade putin din
calitatea serviciului QOS, reduce din timpul de procesare.
P3c. Deplasarea in spatiu a calculelor. Exemplele de pina acum se
refereau la relaxarea conditiilor : esantionarea duce la omiterea unor
pachete, si astfel imaginea transmisa poate sa nu mai fie identica cu cea
originala. Dar alte parti ale sistemului, de exemplu subsistemul 2, trebuie
sa se adapteze acestor conditii ce conduc la pierderi. De aici rezulta si
ideea de deplasare in spatiu a calculului. De exemplu, recent,in retele, se
evita fragmentarea pachetelor in rutere obligand sistemele terminale sa
calculeze dimensiunile de pachete permise de rutere.
P4. Egalizarea componentelor sistemului
Proiectarea sistemului la nivel de schema bloc presupune
descompunerea sistemului in subsisteme si proiectarea separata a fiecarui
subsistem.desi aceasta abordare de sus in jos e convenabila din punct de
vedere al modularitatii, in practica, anumite componente critice ca
performanta sunt deseori create, cel putin partial si de jos in sus.Ca de
exemplu, algoritmii sunt adapatati la hardware-ul existent. Sunt date mai
jos cateva tehnici conforme acestui principiu :
-P4a : Exploatarea localizarii ; in cap.2 s-au aratat avantajele exploatarii
memoriilor atunci cand datele sunt continue/succesive-de exemplu acelasi
56
sector de disc, sau aceeasi pagina de DRAM. Algoritmii de cautare pe disc
folosesc acest lucruprin cautarea in arbore a radacinii superioare, ca la
arborii de tip B. Algoritmii de cautare in tabele IP folosesc acelasi artificiu
pentru a reduce timpul de cautare, plasind cateva chei in cuvintul extins.
-P4b : Compromisul memorie-viteza ; tehnica evidenta este extinderea
memoriei, ca de exemple tabelele de cautare (lookup tables), pentru a
economisi timpul de procesare. O tehnica mai putin evidenta este
comprimarea structurilor de date ca sa incapa in memoria cache, deoarece
accesul la cache este mai rapid decat la memoria obisnuita.
-P4c: Exploatarea facilitatilor hardware ; compilatoarele folosesc
reducerea complexitatii pentru optimizarea inmultirilor in bucla; de
exemplu, intr-o bucla in care adresele au 4 octeti si indexul i creste cu 1 la
fiecare pas, in loc sa se calculeze produsul 4*I, compilatorul calculeaza
noul index crescindu-l cu 4 fata de pasul precedent, deoarece inmultirile
sunt mai lungi decat adunarile. La fel se manevreaza datele, in multiplii ai
cuvintelor calculatorului, ca de ex. algoritmul sumei ciclice de control IP.
Evident ca daca se exagereaza cu aplicarea acestui acest principiu se
pune in pericol modularitatea sistemului. Pentru a evita acest lucru se
folosesc doua tehnici. Mai intai trebuie avut in vedere ca daca se folosesc
anumite caracteristici ale sistemului doar pentru a imbunatati
performantele sistemului, atunci schimbarile acestor caracteristici
afecteaza doar performanta si nu corectitudinea. Apoi, aceasta tehnica se
foloseste doar pentru componentele sistemului care se prevad a fi
strangulari .
P5 : Suplimentarea harware-ului pentru imbunatatirea
performantei
Cand totul esueaza, foloseste forta bruta. Adaugarea de hardware
suplimentar, ca de exemplu cumpararea de procesoare mai rapide poate fi
pina la urma mai simpla si mai economica decat implementarea de tehnici
sofisticate. Pe linga abordarea de tip forta bruta, prin folosirea unor
infrastructuri mai rapide (procesoare,memorii,magistrale si legaturi),
exista cateva compromisuri inteligente. Dar daca hardware-ul este mai
putin flexibil si implica costuri mari de proiectare, atunci este preferabil
ca suplimentarea de hardware sa fie minima.Pe masura cresterii dramatice
a vitezelor procesoarelor si densitatii memoriei sugereaza algoritmi noi in
software si dotarea cu procesoare mai rapide pentru cresterea vitezei.
Sistemele de calculatoare abunda de compromisuri hardware-software
stralucite. De exemplu, in sistemele multiprocesor, daca un procesor
doreste sa scrie date, el trebuie sa informeze toate celelalte procesoare care
acceseaza respectivele date din memoria cache. Aceasta interactiune poate
fi evitata, daca fiecare procesor are o componenta hardware care
supravegheaza magistrala pentru operatiile de scriere a altor procesoare si
cand e nevoie invalideaza automat locatiile respective din memoria cache.
Acest controler simplu de spionare a memoriei cache permite restului
algoritmului dedicat memoriei cache sa-si pastreze eficienta software.
Descompunerea functiilor intre harware si software este insa o arta in
sine. Hardware-ul ofera anumite avantaje. In primul rand nu e necesar
timp de aducere a instructiilor, deoarece instructiile sunt codate hard. In al
doilea rand, secvente de calcul commune (care ar necesita cateva
instructiuni in software) pot fi facute intr-un singur ciclu harware de ceas.
57
De exemplu, gasirea primului bit dintr-un cuvant de 32 de biti, ar necesita
cateva instructii pe o masina RISC, dar poate fi facuta cu un simplu codor
de prioritati (vezi capitolul precedent). In al treilea rand, haedware-ul ne
permite sa beneficiem de aplicarea paralelismului inerent problematicii.In
fine, productia hardware de masa poate fi mai ieftina decat a unui
procesor specializat. De exemplu un Pentium poate costa 100$ in timp ce
un ASIC cu performante similare pote costa doar 10$.
Pe de alta parte, proiectarea software poate fi usor translatata spre
urmatoarea generatie de chip-uri mai rapide. Hardware-ul, in ciuda
folosirii chip-urilor programabile, ramine mai putin flexibil. Totusi,
aparitia unor mijloace de proiectare ca VHDL pentru sintetizarea
hardware-ului, face ca timpul alocat pentru proiectarea hardware-ului sa
scada considerabil. Astfel, in ultimii ani, au fost proiectate chip-uri care
realizeaza functii complexe, ca de exemplu compresie de imagini sau
verificare de tabele IP.
Pe linga imbunatatirea unor anumite performante, noile tehnologii pot
determina abordari complet noi. Un proiectant vizionar ar putea sa
reproiecteze complet un sistem in vederea noilor aplicatii. De exemplu,
inventarea tranzistorului si a memoriilor rapide ne-a permis sa folosim
digitizarea vocii in retelele telefonice.
Cresterea densitatii chip-urilor a facut proiectantii de calculatoare sa
chibzuiasca asupra facilitatilor de calcul adugate memoriilor pentru a evita
strangularea procesor-memorie. In retele, aparitia liniilor de mare viteza in
anii ’80, a condos la folosirea adreselor si antetelor extinse . Ca o ironie,
folosirea calculatoarelor portabile in anii ’90, a dus la folosirea de banda
ingusta a legaturilor fara fir, si in consecinta a aparut problema
comprimarii antetelor.Tendintele tehnologice sunt oscilante.
-P5a:Folosirea pentru memorii a tehnicilor de intretesere si
serializae/pipelining.Tehnici similare sunt folosite la verificarea tabelelor
IP, la clasificare si in algoritmii de planificare care implementeaza QOS.
Bancurile multiple pot fi implementate folosind cateva memorii externe,
asu o memorie externa cum este RAMBUS, sau un SRAM pe un singur
chip care contine si logica de procesare.
-P5b : Folosirea paralelismului pe cuvinte extinse. O tema comuna in
multe proiectari de retele, ca de exemplu schema vectorului de biti de la
Lucent, este folosirea unor cuvinte de memorie extinse, care pot fi
procesate in paralel. Acest lucru se implementeaza folosind DRAM si
modul page-mode, sau folosind SRAM-uri si extinzand fiecare cuvint de
memorie.
-P5c : Combinarea DRAM cu SRAM. Stiind ca SRAM-urile sunt mai
scumpe si mai rapide, iar DRAM-urile sunt mai ieftine si mai lente, are
sens sa le combinam astfel incat sa obtinem un optim pentru amandoua.
Folosirea SRAM-urilor ca si memorii cache si a DRAM-urilor pentru
baze de date este clasica deja, dar exista multe plicatii mult mai creative
pentru ierarhizarea memoriilor. Pe moment se analizeaza efectul unui
volum redus de de SRAM la proiectarea chipurilor pentru tabelele de
verificare a fluxurilor ID. In capitolul 16 sunt descrise tehnici mai non-
uzuale de aplicare a acestei tehnici la implementarea unui numar mare de
numaratoare, unde bitii cei mai putini semnificativi sunt memorati in
SRAM.
58
II. Principii pentru modularitate si eficienta
Lucrari de profil (Dave Clark) au aratat ineficienta implementarilor
stratificate si a modularitatii, dar raspunsul altora (Radia Perlman) a fost ca
acest lucru nu arata decat faptul ca s-a ajuns in faza in care ne putem
plinge de ceva. Ideea este desigur, ca sitemele complexe cum sunt
protocoalele de retea nu pot fi implementate decat folosind stratificarea si
modularitatea. Urmatoarele principii arata cum poate fi redobindita
eficienta, pastrind modularitatea.
P6. Crearea unor rutine specializate eficiente si inlocuirea rutinelor
cu scop general
Folosirea abstractizarilor in proiectarea sistemelor de calcul poate face
sistemul mai compact, ortogonal si modular. Totusi cu timpul, folosirea
rutinelor cu scop general a dus la ineficienta. In cazurile importante trebuie
proiectate si optimizate rutine specializate.
Un exemplu poate fi gasit in cazul memoriilor cache pentru baze de
date. Cea mai generala strategie de utilizare a memoriilor cache prevede
mutarea pe disc a celor mai vechi inregistrari. Totusi sa consideram o
rutina a procesului de interogare (query-processing), care proceseaza in
bucla o secventa de tuple ale bazei de date. In acest caz, cea mai recenta
inregistrare va fi cel mai tirziu folosita in viitor, deci e candidatul ideal
pentru inlocuire. Astfel ca aplicatiile mai noi ale bazelor de date inlocuiesc
rutinele sistemului de memorare in cache cu ritine mai specializate. E bine
ca aceste specializari sa se faca doar pentru rutinele cheie, pentru a evita
extinderea peste masura a codului. Un exemplu din retele sunt rutinele
rapide de procesare UDP.
P7. Evitarea generalizarilor inutile
Tendinta de a proiecta subsisteme abstracte si generale poate duce la
crearea unor facilitati inutile sau rar folosite. Astfel, in loc sa mai
trebuiasca sa facem rutine specializate suplimentare (e.g. P6) cu care sa
inlocuim rutinele generale, am putea sa eliminam diverse facilitati
crescand astfel performanta. Ca si in cazul P3, eliminarea acestor rutine
impune utilizatorilor sa se dezobisnuiasca sa le foloseasca. De exemplu, la
procesoarele RISC, eliminarea instructiunilor complexe cum sunt
inmultirile, implica emularea lor prin firmware. Un exemplu din retele este
cazul fbuf-urilor care ofera un serviciu specializat memoriilor virtuale, ce
asigura o copiere eficienta intre spatiile de memorie virtuala.
P8. Nu te incurca cu implementarile de referinta.
Specificatiile sunt scrise pentru claritate, nu ca sa sugereze o
implementare eficienta. Deoarece limbajele abstracte ale specificatiilor
sunt nepopulare, multe specificatii contin indicatii ferme despre care
anume functie trebuie calculata si cum sa fie implementat calculul. Acest
fapt are doua efecte secundare.
Mai intai exista o tendinta reala spre specificarea exagerata. Apoi, multi
implementatori copiaza implementarea de referinta in specificatii, ceea ce
constituie o problema cand implementarea de referinte a fost aleasa pentru
claritate si nu pentru eficienta. Implementatorii sunt liberi sa schimbe
implementarea de rferinta atita timp cat efectele externe ramin aceleasi. De
fapt pot fi structurate alte implementari, care sa fie si eficiente si modulare.

59
In sisteme, Clark a sugerat folosirea la sistemele de operare a apelurilor
spre nivelurile superioare (upcalls). La aceste upcalls-uri un nivel inferior
poate apela unul superior, pentru avizare sau a cere date, incalcand
apparent regulile de decompozitie ale proiectarii de sisteme. Aceste apeluri
sunt astazi utilizate frecvent in implementarile protocoalelor.
P9. Paseaza indicatii interfetelor modulelor
O indicatie (hint) sau o sugestie este o informatie pasata de la client la
serviciu, care poate scuti serviciul de volume mari de calcul, daca este
corecta. Cuvintele cheie aici sunt paseaza si daca este corecta. Prin
pasarea indicatiei in cererea sa serviciul poate evita necesitatea unei citiri
asociate in tabele, necesare pentru accesul la memoria cache. De exemplu,
o indicatie poate fi folosita pentru a furniza receptorului un index direct in
starea de procesare. Dar, spre deosebire de memoria cache, nu se
garanteaza ca indicatia este corecta, astfel ca trebuie verificata cu alte
informatii a caror corectitudine este verificabila. In majoritatea cazurilor,
indicatiile imbunatatesc performanta, daca sunt corecte
Din insasi definitia hint-ului rezulta necesitatea verificarii corectitudinii
sale, scutind astfel receptorul de aceasta etapa. Un hint verificat se
numeste tip (sfat). Sfaturile sunt greu de utilizat, tocmai din cauza
conditiei de a le asigura corectitudinea. Un exemplu din sisteme este
sistemul Alto-File care are fiecare bloc de fisiere dotat cu un pointer
indicand urmatorul bloc de fisiere. Acest pointer este tratat doar ca hint,
si este verificat cu numele fisierului si numarul blocului memorate chiar
in interiorul blocului. Hinturile incorecte nu trebuie sa pericliteze
corectitudinea sistemului ci doar sa scada din performante.
P10. Paseaza indicatiile in antetele protocolului
Pentru sistemele distribuite, extensia logica a principiului P9 este sa se
paseze informatia ca indicatii in antetul mesajului. Acest lucru poate sa
constituie un principiu separat, pentru sistemele distribuite. Pentru
sistemele paralele acest principiu e folosit de arhitecti pentru a evita
ineficientele in pasarea mesajelor. Una din ideile de la mesajele active
este sa avem un mesaj ce transporta adresa handlerului de intreruperi
pentru expedierea rapida. Alt exemplu este comutarea etichetelor (tag
switching), in care pachetele transporta un indice suplimentar pe linga
adresa destinatie, pentru a permite verificarea rapida a acestei adrese.
Etichetele sunt folosite ca sugestii, deoarece nu este verificata
corectitudinea etichetelor ; pachetele pot fi dirijate spre o destinatie gresita
si acolo ele trebuie verificate.

III Principii pentru accelerarea rutinelor


Principiile anterioare se refereau la structura sistemului, iar cele ce
urmeaza se refera la accelerarea rutinelor vazute izolat.

P11. Optimizarea cazurilor probabile


Sistemele pot sa manifeste o gama larga de comportamente, care pot fi
impartite in mici categorii, numite ”cazuri probabile”. De exemplu, chiar
si cele mai bine proiectate sisteme pot functiona in regimul ” fault” sau
”exception-free”. Alt exemplu este cel al unui program care manifesta o
localizare spatiala, datorata accesarii in principal a unor zone mici de

60
memorie. Astfel, merita sa facem mai eficiente comportarile uzuale, chiar
daca devin mai costisitoare comportarile neuzuale.
Optimizarea euristica a cazurilor probabile nu da satisfactie
teoreticienilor, ei preferind mecanisme ale caror beneficii pot fi cuantizate
cu precizie, fie in medie fie pentru cazul cel mai defavorabil. In apararea
metodelor euristice, trebuie spus insa ca fiecare calculator in functie astazi,
optimizeaza cazurile cele mai probabile de cel putin un milion de ori pe
secunda.
De exemplu, la folosirea ”paging”-ului, cel mai defavorabil numar de
referiri la memorie pe care le poate avea o instructiune de PC este de
patru, (extragerea nstructiei din memorie, citirea primului nivel al
tabelului de pagini, citirea celui de al doilea nivel al tabelului de pagini si
extragerea operandului din memorie). Dar, folosind memoriile cache
numarul de accese la memorie poate fi redus la zero. In general, memoriile
cache permit proiectantilor sa foloseasca structuri modulare si adresari
indirecte, castigand astfel in flexibilitate si in final in performanta pentru
cazurile cele mai probabile. Deci merita sa tratam si sa subliniem
importanta memorarii in memorii cache.
P11a : Folosirea memoriilor cache
Principiul cazului probabil are utilizari subtile, asociat cu folosirea
memoriilor cache. De exemplu, daca in editorul EMACS dorim sa
schimbam bufferele, editorul ne ofera numele ultimului buffer folosit
(default buffer name). Acest lucru economiseste timpul de tiparire in cel
mai probabil caz, adica atunci cand se comuta mereu intre doua buffere.
Folosirea predictiei antetului este un alt exemplu de optimizare in retele a
cazului cel mai probabil. Costul procesarii pachetului poate fi mult redus
daca se presupune ca urmatorul pachet ce va fi receptionat e strans legat de
ultimul pachet procesat (de exemplu este urmatorul pachet din secventa),
si nu necesita exceptii la procesare.
Trebuie precizat ca determinarea cea mai buna a cazurilor cele mai
probabile se face prin masuratori si prin scheme care inregistreaza automat
cazurile comune. Dar depinde de multe ori si de intuitia proiectantului.
Cazul cel mai probabil poate fi incorect in situatiile speciale sau se poate
schimba in timp.
P12. Adauga sau foloseste starile pentru a castiga viteza
Daca o operatie este costisitoare, trebuie luata in considerare o stare
suplimentara dar redundanta, pentru accelerarea operatiei. La bazele de
date, un exemplu este adaugarea unui indice suplimentar. Bancurile de
inregistrari pot fi memorate si cautate folosind o cheie primara, ca de
exemplu numarul asigurarii sociale a clientului. Daca mai apar chestionari
suplimentare referitoare la numele acelui client (balanta de cont, etc.), se
poate plati folosind un index suplimentar (de exemplu o tabela hash sau un
arbore B). Dar starea suplimentara implica prevederea posibilitatii de
schimbarea a sa atunci cand apar modificari.
Totusi, uneori, acest principiu poate fi aplicat fara adaugarea de stari
suplimentare, doar folosind starile existente, fapt subliniat de principiul
P12a.
P12a. Calculul incremental
La sosirea sau plecarea unui client trebuie actualizata tabela de asteptare
pentru clienti. La fel este si in compilatoare, la siruri (vezi exemplul de la
61
P4c) unde se calculeaza incremental noua tabela de indecsi din cea veche,
si nu se folosesc inmultiri pentru calculul valorii absolute a indecsilor. In
retele, un exemplu de calcul incremental este calculul sumelor ciclice de
control IP, unde se modifica doar o mica parte din campurile pachetelor.
P13. Optimizarea gradelor de libertate
E utila la determinarea caror variabile se afla sub control si a celor care
le controleaza, precum si la determinarea criteriilor de obtinere a unei bune
performante. Apoi se trece la optimizarea acestor variabile pentru
maximizarea performantei. De exemplu clientii sunt inscrisi in tabele de
asteptare pe masura ce apar locuri libere, dar optimizarea asteptarii se
poate face prin asignarea la un set de tabele continue a fiecarui client in
asteptare.
Un alt exemplu, din compilatoare, este algoritmul coloring algorithm,
prin care se face asignarea registrelor in timp ce se minimizeaza depasirea
capacitatii registrelor. Un exemplu din retele este algoritmul de testare
multibit a tabelelor IP de cautare. In acesta un grad de libertate care poate
fi prezentat este ca numarul de biti care poate fi utilizat intr-un nod de test
poate fi variabil, si depinde de calea de test, si nu este fix la fiecare nivel.
Numarul de biti utilizati poate fi optimizat prin programare dinamica
pentru a cere cel mai mic volum de memorie la o viteza impusa.
P14: Folosirea unor tehnici specifice universurilor finite (cum sunt
numerele intregi)
Cand avem de-a face cu universuri mici, ca de exemplu intregii de
valori moderate, se folosesc tehnici specifice ca bucket sorting, array
lookup, bitmaps, care sunt mai eficiente decat algoritmii generali de
sortare si cautare.
Pentru a translata o adresa virtuala intr-p adresa fizica procesorul
incearca mai intai o memorie cache numita TLB. Daca nu reuseste,
procesorul trebuie sa caute in tabela de pagini.Se foloseste un prefix al
bitilor de adresa cu care tabela de pagini este indexata direct. Folosirea
cautarii in tabele evida folosirea tabelelor de dispersare (hash) sau cautarea
binara, dar duce la dimensiuni mari ale tabelelor de pagini. Un exemplu
din reteleal acestei tehnici sunt contoarele de timp,(vezi capitolul 7) unde
este construit un algoritm eficient pentru un ceas de timp cu domeniu
limitat, folosind zone circulare.
P15: Folosirea tehnicilor din teoria algoritmilor pentru crearea unor
structuri eficiente de date
Chiar in prezenta unor strangulari majore, ca translatarea adreselor
virtuale, proiectantii sistemelor resping algoritmii inteligenti cum sunt
transmiterea de indicatii, folosirea memoriilor cache sau a tabelelor de
cautare performante. Algoritmii eficienti pot imbunatati mult performanta
sistemelor. Primele 14 principii trebuie sa fie aplicate inainte ca oricare
dintre rezultatele teoriei algoritmilor sa duca la strangulari, cum se
intimpla in multe cazuri.
Abordarea algoritmica include folosirea structurilor standard de date la
fel ca si a tehnicilor algoritmice cele mai generale, ca de exemplu
aleatorizarea sau dezbina si cucereste. Pe de alta parte, proiectantul unui
algoritm trebuie sa fie pregatit sa faca fata si situatiei in care sa vada ca
algoritmul sau a devenit perimat, din cauza schimbarilor de tehnologie sau
de structura. Progresul real poate aparea mai degraba din aplicarea
62
gandirii algoritmice decat din reutilizarea vechilor algoritmi. In
calculatoare exista numeroase exemple stralucite de folosire a unor
algoritmi ca de exemplu: algoritmul de compresie Lempel-Ziv folosit in
utilitarul gzip din UNIX, algoritmul Rabin-Miller de test a proprietatii de
a fi numar prim din sistemele cu chei publice, si folosirea arborilor B
(datorat lui Bayer-McCreight) din bazele de date. In retele, exemple deja
prezentate includ algoritmul Lulea-IP-lookup si schema RFC pentru
clasificarea pachetelor.

63
CAPITOLUL 4

PRINCIPIILE IN ACTIUNE

În capitolul precedent s-au evidenţiat 15 principii pentru implementarea


eficientă a protocoalelor de reţea. Aici se dau exemple de aplicare a
principiilor.

4.1. Validarea buffer-elor pentru canalele dispozitivului aplicaţie


ADC(Application Device Channel)
De obicei, programele aplicaţiilor pot trimite date în reţea numai prin
nucleul sistemului de operare, şi doar nucleul are dreptul să comunice cu
adaptorul de reţea. Această restricţie previne ca diferite aplicaţii
(răutăcioase sau accidentale) să citească sau să scrie în datele celorlalte
aplicaţii. De altfel, comunicarea prin intermediul kernel-ului aduce
supraîncărcări, sub forma apelurilor de sistem. Ideea la ADC-uri
(Application Device Channels), să permită unei aplicaţii să trimită date
către/dinspre retea prin scrierea directă în memoria adaptorului de reţea.
Un mecanism de protecţie, în locul medierii de către kernel, este ca
nucleul să seteze adaptorul cu un set de pagini valide de memorie pentru
fiecare aplicaţie. Adaptorul de reţea trebuie apoi să asigure că datele
aplicaţiei pot fi trimise şi recepţionate doar prin memoria din setul valid.

Memorie
CPU Pagina X
Recepţionarea
Aplicatia P următorului ...
pachet în A
Kernel/nucleu Pagina A

ADAPTOR
Lista valida X, Y, ..., L, A
pt P

RETEA
Figura 4.1. În APDs, adaptorul de reţea primeşte un set de pagini valide (X, Y, L, A, etc.)
pentru o anumită aplicaţie P. Când aplicaţia P face o cerere de primire a datelor în pagina
A, adaptorul trebuie să verifice dacă A este în lista paginilor valide, înainte de a permite
recepţia lui.

În figura 4.1, aplicaţiei P i se permite să trimită/primească date din/in


setul de pagini valide X, Y, ..., L, A. Presupunem că aplicaţia P încarcă în
coadă o cerere spre adaptor să-şi primească următorul pachet, destinat lui
P, într-un buffer din pagina A. Deoarece cererea este trimisă direct
adaptorului, nucleul nu poate verifica dacă acest buffer este valid pentru P.
În schimb, adaptorul trebuie să valideze această cerere asigurându-se că A
64
este în setul de pagini valide. Dacă adaptorul nu face această verificare,
aplicaţia P ar putea oferi o pagină invalidă, aparţinând altei aplicaţii, şi
adaptorul ar scrie informaţia destinată lui P în pagina greşită. Necesitatea
acestei verificări conduce la următoarea problemă.
Problemă: Când aplicaţia P face o receptie, adaptorul trebuie să
valideze că pagina aparţine setului de pagini valide pentru P. Dacă setul de
pagini este organizat ca o lista liniară, atunci validarea poate costa O(n),
unde n este numărul de pagini din set. In figura 4.1, deoarece A este la
sfârşitul listei de pagini valide, adaptorul trebuie să parcurgă întreaga listă
până îl găseşte pe A. Dacă n este mare, atunci acest lucru poate fi scump şi
poate duce la încetinirea ratei cu care adaptorul poate trimite sau
recepţiona pachete. Cum poate fi crescută viteza procesului de validare?
Indicaţie: O abordare bună pentru a reduce complexitatea validării
este folosirea unei structuri de date mai bune decât o listă (P15). Poate fi
îmbunătăţită comportarea în cazul cel mai defavorabil, şi pe viitor, şi găsiţi
factori constanţi mai mici, folosind o gândire sistemică şi pasând indicaţii
interfeţelor.
O gândire algoritmică ar conduce imediat la ideea implementării setului de
pagini valide ca un tabel de dispersie, hash table, si nu ca o listă. Acest
lucru ar oferi un timp mediu de căutare de O(1). Dispersarea are două
dezavantaje: (1) funcţiile perfomante de dispersie cu o probabilitate mică
de coliziune sunt scumpe computaţional; (2) dispersarea nu oferă o limită
bună pentru cazul cel mai defavorabil. Căutarea binara asigură un timp de
căutare logaritmic pentru cazul cel mai defavorabil, dar e scumpă (necesită
şi păstrarea unor seturi sortate) dacă setul de pagini e mare şi rata de
transmisie a pachetelor e mare. In schimb, putem înlocui verificarea
tabelei de dispersie cu o tabelă de verificare indexată, după cum urmează.
Soluţie: Adaptorul va stoca un set de pagini valide pentru fiecare
aplicaţie într-un tablou, (fig.4.2). Acest tablou este actualizat numai când
nucleul actualizează setul de pagini valide pentru aplicaţie. Când aplicaţia
face o recepţie în pagina A, transmite adaptorului un indicator. Acest
indicator este indexul poziţiei din tablou unde este stocat A. Adaptorul
poate să folosească această informaţie ca să confirme rapid dacă pagina
din cererea de recepţie se potriveşte cu pagina din indicator. Costul
validării e mărginit, dat de o căutare în tablou (verificarea că indexul e
valid) şi o comparaţie.

65
Memorie
Page X
Recepţionarea
CPU următorului ...
pachet în A
Aplicatie P Page A
Kernel

Lista valida X, Y, ..., L, A


pt P ADAPTOR

RETEA

Figura 4.1. În APDs, adaptorul de reţea primeşte un set de pagini valide (X, Y, L, A,
etc.) pentru o anumită aplicaţie P. Când aplicaţia P face o cerere de primire a datelor în
pagina A, adaptorul trebuie să verifice dacă A este în lista paginilor valide, înainte de a
permite recepţia lui.

Figura 4.2: Evitarea necesităţii actualizării tabelei de dispersie, prin pasarea manevrei
prin interfaţa aplicaţie-adaptor

Exercitii
• Este ghidajul/handle o indicaţie/hint sau un tip ? să revenim la P1:
daca e un handle de ce să pasăm numărul paginii (e.g.A) interfeţei? De ce
să eliminăm accelerarea uşoară a vitezei task-ului de confirmare a
numărului paginii?
• Pentru a găsi tabela corespunzătoare aplicaţiei P e necesară o căutare
în tabelele de dispersie, folosind P ca şi cheie. Acest lucru atenuează
argumentul de a scăpa de căutarea în tabelele de dispersie ca să verificăm
dacă o pagină e validă sau nu, cu excepţia rafinării căutării în tabelele de
dispersie. Cum poate fi făcut acest lucru ?

4.2. Planificator pentru controlul fluxului la ATM


In modul ATM ( Asynchronous Transfer Mode), adaptorul ATM poate
avea sute de circuite virtuale,VC(Virtual Circuit) care pot transmite
simultan date (numite celule). Deseori, se controlează fluxul fiecărui VC,
într-o formă sau alta, pentru a limita rata de transmisie a acestuia. De
exemplu, la controlul fluxului bazat pe rata de transmisie, un circuit virtual
VC poate primi credite, ca să transmită celulele la momente de timp fixe.
66
Pe de altă parte, la controlul fluxului bazat pe credite, creditele pot fi
transmise de nodul următor de pe cale când se eliberează buffer-ele.
Astfel (fig.4.3), adaptorul are un tabel care păstrează starea VC. Sunt
patru VC setate (1, 3, 5, 7). Dintre acestea, doar VC1,5 şi 7 au celule de
transmis. Dar VC5 e inactiv, astfel că numai VC1 şi 7 au credite pentru a
transmite celule. Astfel următoarea celulă trimisă de adaptor ar trebui să
fie dintr-un VC disponibil: 1 sau 7. Selecţia dintre VC disponibile ar trebui
făcută corect, de exemplu prin tehnica round-robin. Dacă adaptorul alege
să transmită o celulă din VC7, el va decrementa la 1numărul de credite al
VC7. Cum nu mai sunt alte celule de transmis, VC7 devine acum
nedisponibil. Alegerea următorului VC disponibil duce la următoarea
problemă.
Problemă: Un planificator naiv ar putea parcurge ciclic tabloul cu
circuite virtuale căutând unul eligibil. Dacă multe dintre VC sunt
nedisponibile, atunci planificatorului îi va lua mult timp până va găsi unul
disponibil, lucru care este ineficient. Cum poate fi evitată această
ineficienţă?
Indicaţie: Apelăm la P12, suplimentarea stărilor pentru accelerarea
buclei principale. Ce stare trebuie adăugată pentru a evita parcurgerea
stărilor VC neeligibile ? Cum poate fi menţinută eficient această stare ?
Soluţie: Se ţine o listă a circuitelor virtuale eligibile, în plus faţă de
tabelul de VC (fig. 4.3). Singura problemă este menţinerea eficientă a
acestei stări. Aceasta e dificultatea majoră a principiului P12. Dacă această
evidenţă este prea scump de menţinut, adăugarea altor stări este o calitate
nu o necesitate. Ştim că un VC este disponibil dacă are atât celule de
transmis cât şi credite. Astfel un VC este scos din listă dacă devine inactiv
sau nu mai are credite; altfel e adăugat la coada listei pentru asigurarea
corectitudinii; adăugarea se face fie când soseşte o celulă la coada de
aşteptare vidă a unui VC sau când VC fără credite primeşte credite.
Exercitii
• Cum puteţi fi siguri că un VC nu e adăugat de mai multe ori listei
eligibile?
• Poate fi generalizată schema pentru a permite unor VC mai multe
posibilităţi de a transmite decât altora, pe baza unor ponderi alocate de
manager?

4.3 Calcularea rutei folosind algoritmul lui Dijkstra


Cum decide un ruter S asupra rutei/traseului unui pachet spre destinaţia
D? Fiecare legătură într-o reţea are un cost, şi ruterele trebuie să calculeze
drumul cel mai scurt/de cost minim către destinaţie în domeniul local.
Costul e un număr întreg, de valoare mică. Cel mai folosit protocol de
dirijare intradomeniu este OSPF (Open Shortest Path First), bazat pe starea
legăturilor.
În cadrul rutării după starea legăturii, fiecare ruter dintr-o subreţea
trimite un pachet cu starea legăturii LSP (Link State Packet) care conţine
lista legăturilor cu toţi vecinii săi. Fiecare LSP este transmis spre toate
celelalte rutere din subreţea folosind un protocol de inundare. Odată ce
fiecare ruter a primit câte un LSP de la oricare din celelalte rutere, atunci
fiecare ruter are harta întregii reţele. Presupunând că topologia ramâne
stabilă, fiecare ruter poate acum să calculeze cel mai scurt drum către
67
fiecare nod din cadrul reţelei, folosind un algortim standard de cale
minimă, ca de exemplu algoritmul lui Dijkstra.
În figura 4.5, sursa S doreşte să calculeze arborele de drum minim către
restul nodurilor (A, B, C, D) din reţea. Reţeaua este prezentată în stânga
figurii cu legăturile şi costurile lor. În algoritmul Dijkstra, S începe prin a
se plasa doar pe el în arborele de cost minim. S actualizează costul pentru
a ajunge la vecinii adiacenţi (ex. B, A). La fiecare iteraţie, algoritmul
Dijkstra adaugă la arborele curent, nodul cel mai apropiat arborelui curent.
Costurile vecinilor noului nod adăugat sunt actualizate. Procesul se repetă
până toate nodurile din reţea aparţin arborelui.
De exemplu, în figura 4.5., după ce a adăugat S, algoritmul alege nodul
B şi apoi A. La aceasta iteraţie, arborele arată ca cel din dreapta figurii 4.5.
Liniile continue arată arborele existent, iar cele întrerupte arată cele mai
bune conexiuni curente spre nodurile care nu sunt încă în arbore. Cum A
are un cost de 2 şi este o legatură de cost 3 de la A la C, C este marcat cu
5. Asemănător, D este marcat cu un cost egal cu 2 pentru drumul prin B.
La următorul pas, algoritmul alege nodul D ca nod cu costul cel mai mic
ce nu aparţine arborelui. Costul legăturii către C este actualizat folosind
ruta prin D. În final, C este adăugat în ultimul pas.
Această soluţie necesită determinarea nodului cu cel mai mic cost care
nu se află în arbore la fiecare pas. Structura de date standard, pentru a ţine
evidenţa elementului de valoare minimă, într-un mediu care se modifică
dinamic, este o coadă de aşteptare cu priorităţi. Acest lucru conduce la
următoarea problemă.
Problemă: Algoritmul Dijkstra necesită o coadă de aşteptare cu
priorităţi la fiecare din cele N iteraţii, unde N este numărul nodurilor de
reţea. Cele mai bune cozi de priorităţi cu scop general, ca depozitele/heap
au un cost de O ( log N ) pentru a găsi elementul minim. Acest lucru
implică un timp total de rulare de O ( N log N ) . Rezultă, pentru o reţea de
dimensiuni mari, un timp de răspuns mare la defecţiuni şi modificări de
topologie.
Indicaţie: Trebuie exploatat situaţia că legăturile au ca şi costuri numere
întregi mici (P14), folosind un tabel cu costurile legăturilor. Cum puteţi
găsi eficient următorul nod de cost minim pentru a-l include în arborele
căilor minime?
Soluţia: Faptul că costurile legăturilor sunt valori mici întregi poate fi
exploatat pentru construcţia cozii de aşteptare cu priorităţi, folosind
sortarea de tip găleată/bucket sorting (P14). Presupunem că cel mai mare
cost de legătură este MaxLinkCost. Astfel costul maxim al unei căi nu
poate fi mai mare de Diam ∗ MaxLinkCost, unde Diam este diamterul
reţelei. Presupunem că Diam este un număr întreg de valoare mică. Astfel
putem folosi un tablou cu o poziţie pentru fiecare cost posibil c în gama
1...Diam*MaxLinkCost. Dacă în timpul algoritmului Dijkstra costul curent
al nodului X este c, atunci nodul X poate fi plasat în lista indicată de
elementul c alt tabloului. (fig.4.6). Acesta ne conduce la următorul
algoritm.
De fiecare dată când un nod X îşi schimbă costul de la c la c’ , nodul
X este scos din lista pentru c şi adăugat în lista pentru c’. Dar cum se
găseşte elementul minim în tablou? Se foloseşte un indicator numit

68
CurrentMin iniţial pus pe 0 (ce corespunde costului lui S). De fiecare dată
când algoritmul doreşte să găsească nodul de cost minim care nu aparţine
arborelui, CurrentMin este incrementat cu 1 până este găsita o locaţie de
tablou care conţine o listă nevidă. Orice nod din această listă poate fi
adăugat la arbore. Costul algoritmului este O ( N + Diam ∗ MaxLinkCost )
deoarece acţiunea care dă în avans CurrentMin poate fi cel mult de
dimensiunea tabloului. Aceasta este mult mai bine dect N log N ,dacă N
este mare şi Diam şi MaxLinkCost sunt mici.
Factorul crucial care permite folosirea eficientă a cozii de aşteptare cu
sortare prin găleată a priorităţilor este faptul că costurile nodurilor sunt
întodeauna înaintea valorii lui CurrentMin. Aceasta este condiţia de
monotonie. Dacă nu ar fi aşa algoritmul ar începe de la 1 la fiecare iteraţie,
în loc sa înceapă de la ultima valoare a lui CurrentMin, şi să nu mai revină
niciodată. Această condiţie de monotonie este evidentă pentru algoritmul
Dijkstra deoarece costul nodurilor care nu aparţin arborelui trebuie să fie
mai mari decât costurile nodurilor din arbore.
Figura 4.6. arată starea cozii de aşteptare cu sortarea priorităţii cu găleată
după ce A a fost adăugat arborelui (cadrul din dreapta în fig.4.5). La acest
moment CurrentMin=2, care este egal cu costul lui A. La următorul pas,
CurrentMin va deveni 3, şi D va fi adăugat arborelui. Acest lucru duce la
reducerea costului lui C la 4. Astfel C va fi scos din lista din poziţia 5 şi
adăugat în lista vidă din poziţia 4. CurrentMin devine 4 şi abia atunci C
este adăugat arborelui.

Exerciţii:
• Algoritmul cere ca un nod să fie extras dintr-o listă şi adăugat altei
liste, precedentă. Cum poate fi făcut acest lucru eficient ?
• In fig.4.6 cum ştie algoritmul că a terminat, după adăugarea nodului C
arborelui, şi să nu mai parcurgă tabloul pîna la capăt ?
• In reţelele unde apar defectări, conceptul de diametru este foarte
suspect, deoarece el se poate modifica mult în caz de defectare.
Considerăm o topologie circulară, unde toate cele N noduri au diametrul 2
prin nodul zis central; dacă acesta se defectează, diametrul scade la N/2.
Deobicei, în practică, diametrul este mic. Acest lucru poate crea probleme
în dimensionarea tabloului ?
• Putem evita complet problema diametrului înlocuind tabloul liniar din
fig.4.6 cu unul circular de dimensiune MaxLinkCost ? Explicaţi. Soluţia
rezultată se numeşte algoritmul Dial.

4.4 Monitor Ethernet cu o punte hardware


Alice Hacker a lucrat la Acme Network cu o punte Ethernet a firmei
Acme. O punte este dispozitivul care face legătură între mai multe reţele
Ethernet. Pentru a transmite pachete de la o reţea Ethernet la alta, puntea
caută adresa destinaţie pe 48 biţi din pachetul Ethernet, la viteze mari.
A. Hacker a decis să transforme puntea într-un monitor de trafic
Ethernet, care să asculte pasiv reţeaua, pentru a obţine statistici asupra
structurii traficului Ethernet. Monitorizarea se face între perechi sursă-
destinaţie arbitrare, astfel că pentru fiecare pereche activă sursă-destinaţie,
ca A şi B, se reţin în variabilele PA,B numărul de pachete trimise de la A
spre B, de la începutul monitorizării. Când un pachet este trimis de la A la
69
B, programul de monitorizare (care ascultă toate pachetele transmise) va
prelua o copie a pachetului. Dacă sursa este A şi destinaţia B se
incrementează PA,B. Operaţia trebuie facută în 64 μ sec, timpul minim între
pachete în Ethernet. Strangularea apare la verificarea stării PA,B asociate
unei perechi de adrese pe câte 48 biţi A,B.
Din fericire, puntea hardware are un motor de căutare ce poate verifica
starea asociată unei singure adrese de 48 biti în 1,4 μ sec. Un apel spre
hardware poate fi făcut de forma Lookup(X,D), unde X este cheia pe 48
biţi şi D este baza de date în care se face căutarea. Apelul returnează starea
asociată cu X în 1,4 μ sec pentru baze de date cu un număr mai mic de
64.000 chei. Problema ce trebuie rezolvată este următoarea:
Problemă: Programul de monitorizare trebuie să actualizeze starea pentru AB
când ajunge la B un pachet de la A. Programul are un motor de căutare care
poate găsi doar adrese singulare şi nu perechi.Cum poate motorul actual să
caute perechi de adrese? (figura 4.7).
Indicaţie: Trebuie folosit P4 pentru a exploata puntea hardware existentă.
Deoarece 1,4 μ sec << 64 μ sec putem folosi în proiectare mai mult de o
căutare hardware. Cum poate fi redusă o căutare de 96 de biţi la o căutare de
48 de biţi, folosind trei căutări ?
Soluţia naivă este folosirea a două căutari pentru a converti sursa A şi
destinaţia B în indici mai mici (<24 biti), I A şi I B . Aceşti indici pot fi
apoi folosiţi pentru căutare într-un tablou bidimensional care memorează
starea pentru AB. Aceasta pretinde doar două căutari hardware şi un acces
la memorie, dar poate implica un volum mare de memorie. La 1000 de
surse posibile şi tot 1000 destinaţii posibile, tabelul trebuie să aibă un
milion de intrări. In practică sunt active deobicei doar 20.000de perechi
sursă-destinaţie. Cum poate fi adaptat volumul necesar de memorie
proporţional cu numărul actual al perechilor sursă- destinaţie ?
Soluţie: Intâi se face câte o căutare pentru a converti sursa A şi destinaţia B în
indici mai mici (<24 biti), IA si IB. Apoi se foloseşte o a treia căutare pentru a
face asocierea între IAIB şi starea AB. Soluţia este ilustrată în figura 4.8. Cea de-
a treia căutare face compresia tabloului bidimensional din soluţia naivă. Soluţia
are ca autori pe Mark Kempf şi Mike Soha.
Exerciţii
• Poate fi problema rezolvată cu doar două căutări ale punţii hardware şi
fără memorie suplimentară ?
• Setul perechilor sursă-destinaţie se poate schimba în timp, deoarece
anumite perechi de adrese devin inactive lungi perioade de timp. Cum
poate fi manevrată situaţia fără a ţine câte o stare pentru fiecare pereche
posibilă de adrese, care are de comunicat de la pornirea monitorului ?

4.5 Demultiplexarea în nucleul-X


Nucleul-x asigură o infrastructură software pentru implementarea de
protocoale în staţii/gazde. Sistemul nucleului-x asigură baza pentru un
număr de funcţii de protocoale necesare. O funcţie folosită este protocolul
de demultiplexare. De exemplu, când IP, nivelul reţea Internet, primeşte
un pachet, trebuie să folosească câmpul de protocol pentru a vedea dacă
pachetul trebuie trimis la TCP sau la UDP.

70
Majoritatea protocoalelor fac demultiplexarea pe baza unor identificatori
în antetul/header-ul protocolului. Aceşti identificatori pot să varieze ca
lungime în diferite protocoale. De exemplu, câmpurile pentru Ethernet pot
fi de 5 octeţi, iar numerele portului TCP au 2 octeţi. Astfel x-kernel-ul
permite demultiplexarea pe baza identificatorilor de protocol cu lungime
variabilă. Cand se iniţializează sistemul, rutina protocolului înregistrează
legăturile între identificatori şi protocolul destinaţie cu nucleul-x. In timpul
rulării, când un pachet soseşte, rutina protocolului poate extrage
identificatorul protocolului din pachet şi apelează rutina nucleului-x de
demultiplexare pentru protocolul destinaţie. Cum pachetele pot veni la
viteze mari, rutina de demultiplexare trebuie să fie rapidă. Rezultă
următoarea problemă.
Problemă: In medie, cea mai rapidă cale de a face o căutare este folosirea
unui tabel de dispersie. Cum este prezentat în figura 4.9, sunt necesare folosirea
unor funcţii de dispersie asupra identificatorului K pentru generarea unui index
de dispersie, folosirea acestui index pentru a accesa tabelul de dispersie, şi
compararea cheii L, reţinută la intrarea în tabelul de dispersie, cu K. Dacă se
potrivesc, rutina de demultiplexare poate recupera protocolul destinaţie asociat
cheii L. Presupunem că funcţia de dispersie a fost aleasă să facă coliziunile rare.
Deoarece lungimea identificatorilor este un numar arbitrar de octeţi,
rutina pentru comparaţie pentru cele două chei trebuie, în general, să facă
comparaţia octet cu octet. Oricum, presupunem cazul cel mai des întâlnit a
identificatorilor pe 4 octeţi, care este dimensiunea unui cuvânt. In acest
caz, este mult mai eficient să facem comparatia la nivel de cuvânt. Astfel
scopul este (P4) să exploatăm comparaţia eficientă pe cuvânt pentru
optimizarea cazului cel mai probabil. Cum putem opera, daca se
manevrează diferite protocoale ?
Indicaţie: Dacă x-kernel-ul are de demultiplexat un identificator de 3
octeţi, trebuie să folosească rutina de comparaţie octet cu octet; dacă
identificatorul e pe 4 octeti, şi maşina lucrează cu cuvinte de 4 octeţi,
poate fi făcută comparaţia pe cuvânt. Primul grad de libertate care poate fi
folosit este să avem rutine de comparaţie diferite pentru cele mai comune
cazuri (comparare pe cuvânt, comparare pe cuvinte lungi) şi o rutină de
avarie/default pentru compararea pe octet. Acesta este un compromis
spaţiu-timp (P4b). Pentru corectitudine, trebuie ştiut care rutină de
comparaţie trebuie folosită în cazul fiecărui protocol. Trebuie invocate
principiile P9 de pasare de indicaţii interfeţelor şi P2a de realizare a
precalculelor.
Soluţie: Fiecare protocol trebuie să declare identificatorul şi protocolul
destinaţie nucleului-x când se iniţializează sistemul. Cand s-a realizat acest
lucru, fiecare protocol poate să-şi predeclare lungimea identificatorului, ca
nucleul-x să folosească rutine de comparaţie specializate pentru fiecare
protocol. Efectiv, informaţia este transmisă între protocolul client şi nucleul-x
mai devreme. Presupunem că nucleul-x are un tabel de dispersie pentru fiecare
protocol client şi că nucleul-x ştie contextul pentru fiecare client astfel încât să
folosească codul specializat pentru acel client.
Exerciţii
• Codaţi comparaţiile octet-cu-octet şi pe cuvânt pe calculatorul vostru,
faceţi un număr mare de comparaţii şi apoi comparaţi timpul global
necesar fiecărui tip.
71
• In primele soluţii ADC cautarile tabelelor hash au fost rafinate prin
pasarea unui index (în loc de lungimea identificatorului, ca la început). De
ce poate fi această soluţie dificilă în acest caz ?

4.6 Arbori cu prefix cu compresia nodului


Un arbore cu prefix sau arbore ordonat/trie este o structură de date de
forma unui arbore de noduri, unde fiecare nod este un tablou de M
elemente. Figura 4.10 prezintă un exemplu simplu cu M=8. Fiecare tablou
poate reţine fie o cheie (ex. KEY 1, KEY 2, sau KEY 3 în figura 4.11) sau
un pointer către alte noduri ale arborelui cu prefix (ex. primul element în
nodul test cel mai de sus din figura 4.10, care este rădăcina). Arborele cu
prefix este folosit pentru a căuta potriviri exacte (şi potriviri de cele mai
lungi prefixe) cu un şir de intrare. Arborii cu prefix sunt utili în reţele
atunci când se caută o adresă IP, o punte, şi filtre de demultiplexare.
Nu ne interesează acum algoritmul exact al arborelui cu prefix. Singurul
lucru ce trebuie ştiut este cum se face căutarea în arborele cu prefix.
Presupunem că c=log2M este dimensiunea unei bucăţi de arbore cu prefix.
Pentru a căuta în arborele cu prefix,întâi se separă şirul de caractere
introdus, în bucăţi de dimensiunea c. Căutarea foloseşte bucăţile succesive,
începând cu cea mai semnificativă, indexând nodurile unui arbore cu
prefix, începând cu nodul rădăcină. Cand căutarea foloseşte bucata j pentru
a indexa în pozitia i a nodului curent al arborelui cu prefix , pozitia i poate
conţine un pointer sau o cheie. Dacă poziţia i conţine un pointer nenul
către nodul N, căutarea continuă din nodul N cu bucata j+1; altfel căutarea
ia sfârşit.
Pe scurt, fiecare nod este un tablou de pointeri sau chei, şi procesul de
căutare trebuie să indexeze în aceste tablouri. Oricum, dacă multe din
nodurile arborelui cu prefix sunt împrăştiate, se face risipă de spaţiu (P1).
De exemplu, în figura 4.10, doar 4 din 16 locaţii conţin informaţie utilă. In
cel mai rău caz, fiecare nod al arborelui cu prefix poate conţine un pointer
sau o cheie şi ar putea fi un factor M în memorie nefolosită. Presupunem
M ≤ 32 în cele ce urmează. Chiar daca M este aşa mic, o creştere cu 32 a
memoriei poate duce la mărirea semnificativă a costului proiectării.
O abordare evidentă este înlocuirea fiecarui nod al arborelui cu prefix
cu o listă liniară de perechi de forma (i,val), unde val este o valoare nevidă
(pointer sau cheie) în poziţia i a nodului. De exemplu, nodul arborelui cu
prefix rădăcină în figura 4.10 poate fi înlocuit cu o listă (1,ptr);(7,KEY1),
unde ptr1 este un pointer la nodul arborelui cu prefix de jos. Din păcate,
acest lucru poate încetini căutarea arborelui cu prefix cu un factor M,
deoarece căutarea fiecarui nod al arborelui cu prefix duce la căutarea intr-o
lista de M locaţii, în locul unei simple operaţii de indexare. Asta ne
conduce la următoare problemă.
Problema:Cum pot nodurile arborelui cu prefix să fie comprimate prin
eliminarea pointerilor nuli fără a încetini căutarea decât cu un factor mic?
Indicatie: Deşi nodurile sunt comprimate, indexarea tabelelor trebuie să
fie eficientă. Dacă nodurile sunt comprimate, cum ar putea fi reprezentată
informaţia despre nodurile care au fost eliminate ? Considerăm o egalizare
faptul că M este mic, respectând P14 (exploatarea dimensiunii întregilor
mici) şi P4a (exploatarea localizării).

72
Soluţie: Cum M<32, un bitmap de dimensiune 32 poate uşor să încapă într-
un cuvânt. Astfel pointerii nuli sunt scoşi după ce se adaugă un bitmap cu zero
biţi indicând pozitia originală a pointerilor nuli. Acest lucru este arătat în figura
4.11. Nodul arborelui cu prefix poate fi acum înlocuit cu un bitmap şi un nod al
arborelui cu prefix comprimat. Un nod al arborelui cu prefix comprimat este un
tablou ce conţine doar valorile nenule din nodul original. Astfel în figura 4.11
nodul rădăcina al arborelui cu prefix original (de sus) a fost înlocuit cu nodul
arborelui cu prefix comprimat (de jos). Bitmapul contine un 1 în prima şi a
şaptea pozitie, unde nodul rădăcină conţine valori nenule. Tabloul necomprimat
conţine acum doar 2 elemente, primul pointer şi KEY 3.
Având în vedere că atât nodurile comprimate cât şi cele necomprimate
sunt tablouri şi procesul de căutare începe cu un index I în nodul
necomprimat, procesul de căutare trebuie să consulte bitmapul pentru a
converti indexul necomprimat I într-un index comprimat C în nodul
comprimat. De exemplu, dacă I este 1 în figura 4.11, C ar trebuie să fie 1;
daca I este 7, C ar trebui să fie 2. Dacă I este oricare altă valoare, C ar
trebui să fie 0, indicând că este un singur pointer nul.
Conversia din I la C poate fi realizată uşor ţinând cont de următoarele.
Daca poziţia I în bitmap conţine un 0, atunci C=0. Altfel, C este un număr
de 1-uri în primii I biţi ai bitmapului. Astfel dacă I=7, atunci C=2,
deoarece sunt doi biţi setaţi în primii şapte biţi ai bitmapului. Acest calcul
necesită cel mult două referiri la memorie: una pentru a accesa bitmapul
(deoarece bitmapul e mic, P4a) şi una pentru a accesa tabloul comprimat.
Calculul numarului de biţi setaţi într-un bitmap poate fi făcut folosind
registre interne (în software) sau logica combinaţională (în hardware).
Astfel încetinirea efectivă este cu un factor ceva mai mare de 2 în software
şi exact 2 în hardware.
Exerciţii
• Cum puteţi folosi căutarea tabelelor (P14, P2a) pentru accelerarea
contorizării numărului de biţi din set, în software? Ar fi necesar al treilea
apel la memorie ?
• Presupunem că bitmap-ul e mare (sa zicem,M=64K). Ar părea că
contorizarea numărului de biţi din set cu asemenea bitmap mare e
imposibil de lentă, în software sau hardware. Puteţi găsi o cale de
accelerare a contorizării biţilor în bitmap-urile mari (P12 si P2a) ?

4.7 Filtrarea pachetelor in rutere


Există protocoale care setează resursele la rutere pentru trafic care
necesită garantarea performanţelor, cum e traficul video sau audio. Astfel
de protocoale folosesc concepte ca filtrarea pachetelor, numite şi
clasificatoare. Astfel, în figura 4.12 fiecare receptor ataşat unui ruter poate
specifica un filtru de pachete descriind pachetele pe care doreşte să le
recepţioneze. De exemplu, în fig.4.12 receptorul 1 poate fi interesat de
recepţia NBC, care e specificat de filtrul 4. Fiecare filtru e conform unor
specificaţii ale câmpurilor care descriu pachetele video trimise de NBC.
De exemplu, NBC poate fi specificat de pachetele care folosesc adresa
sursă a transmiţătorului NBC din Germania şi utilizează o anumită
destinaţie TCP şi numărul de port sursă.
La fel, în fig.4.12 receptorul m poate fi interesat în recepţionarea ABC-
Sport şi CNN, care sunt descrise de filtrul 1şi 7. Pachetele sosesc la ruter
73
la viteze mari şi trebuie trimise tuturor receptoarelor care cer pachetul. De
exemplu, receptorul 1 şi receptorul 2 pot dori ambele să recepţioneze
NBC, fapt ce duce la următoarea problemă.
Problemă Fiecare pachet recepţionat trebuie încercat la fiecare filtru şi trimis
la toţi receptorii care îl lasă să treacă. O scanare liniară simplă a tuturor filtrelor
e scumpă dacă numărul de filtre e mare. Presupunem numărul de filtre e peste o
mie. Cum poate fi grăbit un pic acest proces?
Indicaţie Ne-am putea gândi să optimizăm cel mai probabil caz
utilizând cache-ul pentru memorare (P11a). Totuşi caching-ul e dificil de
folosit aici, şi de ce ? Considerăm adăugarea unui unui câmp (P10) la
antetul pachetului pentru a face cachingul mai usor. Care nivel de protocol
ar trebui ideal adăugat ?Adăugând un câmp fix bine cunoscut pentru
fiecare tip de video posibil nu e un panaceu, deoarece implică o
standardizare globală, şi filtrele trebuie să fie bazate pe alte câmpuri, ca de
exemplu adresa sursei. Presupunem că câmpurile adăugate de noi nu
necesită identificatori standardizaţi global. Ce proprietăţi trebuie să asigure
sursa acestor câmpuri?
Soluţia Folosirea memorilor cache (P11a), vechiul cal de bataie al
proiectanţilor de sisteme, nu e directă pentru această problemă.In general în
cache se memorează asocierile dintre o intrare a şi o ieşire oarecare f(a).
Cache-ul constă deci dintr-un set de perechi de valori, de forma (a,f(a)),. Baza
de date poate fi implementată ca un tabel de dispersie (software) sau o memorie
cu conţinut adresabil (hardware). Având valoarea a şi trebuind să calculăm f(a),
se consultă baza de date ca să vedem dacă a nu a fost deja introdus. Dacă da
atunci se poate da direct, rapid valoarea lui f(a). Dacă nu atunci se va face
calculul folosind o altă metodă de calcul (posibil scumpă) şi perechea (a,f(a))
este introdusă în baza de date cache. Pe urmă intrările de valoare a pot fi
calculate foarte rapid.
In problema filtrării pachetelor, scopul este să calculăm setul de
receptoare asociate unui pachet P. Problema e că ieşirea este o funcţie de,
posibil, un număr mare de câmpuri antet ale pachetului P. Astfel pentru a
folosi memoria cache, trebuie stocate o parte mare de antetele lui P
asociate setului de receptoare pentru P. Memorarea asocierilor dintre cei
64 de octeţi ai antetului pachetului e o propunere foarte costisitoare.Este
costisitoare în timp, deoarece căutarea cache-ului poate fi mai lungă
deoarece cheile sunt vide.Este costisitoare şi ca spaţiu de memorare.
Stocările masive implică un număr mai mic de asocieri care pot fi stocate
într-o memorie cu o capacitate dată, adică în final un hit-rate mai prost.
Se preferă reţinerea în cache a asocierilor dintre unul/două câmpuri de
pachete şi setul de receptoare de la ieşire. Acest lucru ar accelera timpul
de căutare prin cache şi ar îmbunătăţi hit-rate-ul. E preferabil că aceste
câmpuri să fie plasate în antetul de rutare, verificat oricum de
ruter.Problema e că acolo ar putea să nu fie un asemenea câmp care să
amprenteze unic pachetul P ?
Totuşi, presupunând că suntem proiectanţi de sisteme, şi avem de
proiectat un protocol de rutare.Am putea adăuga un câmp antetului de
rutare. Problema pare banală dacă am putea asigna fiecărui flux de
pachete posibil un identificator global unic.De exemplu, dacă am putea
asigna lui NBC identificatorul 1, lui ABC identificatorul 2, iar lui CNN
identificatorul 3, atunci am putea memora în cache folosind identificatorul
74
ca şi cheie. O asemenea soluţie ar implica o formă oarecare de comitete
de standardizare globală responsabile cu botezarea fiecărui flux aplicaţie.
Chiar dacă s-ar putea face acest lucru, filtrul receptor ar putea cere toate
pachetele NBC de la o anumită sursă, iar filtrul ar putea depinde şi de alte
câmpuri ale pachetului.Acest lucru a condus la următoarea idee finală.
Soluţia la care s-a ajuns este modificarea antetului de rutare prin
adăugarea unui identificator de flux F (fig.4.13), cu semnificaţie
dependentă de sursă. Cu alte cuvinte, surse diferite pot folosi acelaşi
identificator de flux deoarece este o combinaţie, a sursei şi a
identificatorului de flux, care este unică. Aşa nu e necesară standardizarea
globală a identificatorilor de flux (şi nici altă coordonare
globală).Identificatorul de flux e doar un contor local menţinut de sursă
Ideea este că la transmiţător o aplicaţie care transmite poate cere nivelului
de rutare un identificator de flux. Acest identificator este adăugat antetului
de rutare la toate pachetele aplicaţiei.
Ca deobicei, când un pachet aplicaţie soseşte prima dată, ruterul face o
căutare liniară (lentă) pentru a determina setul de receptoare asociate cu
antetul pachetului. Deoarece identificatorii nu sunt unici de la o sursă la
alta, ruterul reţine în cache asocierile folosind concatenarea adreselor sursă
a pachetului si identificatorul de flux ca şi cheie. Evident că corectitudinea
depinde de condiţia ca aplicaţia transmiţătorului să nu schimbe câmpurile
care ar putea afecta filtrul, fără să schimbe deasemenea şi identificatorul
de flux din pachet.
Exerciţii
• Ce poate merge prost când cade sursa şi revine fără să-şi mai
amintească ce identificatori au fost asignaţi diferitelor aplicatii? Ce poate
merge prost când receptorul adaugă un filtru nou? Cum pot fi aceste
probleme rezolvate?
• In soluţia curentă identificatorul de flux e folosit ca un tip şi nu ca un
hint. Ce costuri suplimentare ar presupune tratarea perechii identificatorul
de flux-adresa sursei ca un hint şi nu ca un tip?

4.8 Evitarea fragmentarii pachetelor de stare a legaturii


Problema care urmeaza a aparut in timpul proiectarii protocoalelor de
dirijare conform cu starea legaturii, OSI si OSPF. Ea se refera la
proiectarea si nu la implementarea protocolului, odata ce proiectarea a fost
facuta. Se vede acum cat de tare afecteaza performanta protocolului
solutiile adoptate in timpul proiectarii.
La dirijarea conform cu starea legaturii, ruterul trebuie sa transmita un
pachet cu starea legaturii LSP(Link State Packet) tuturor vecinilor sai.
Protocolul de dirijare conform cu starea legaturii consta din doua procese
separate. Primul este procesul de actualizare care transmite pachete cu
starea legaturii in siguranta de la ruter la ruter, folosind protocolul cu
inundare, care aloca un numar de secventa unic fiecarui pachet de stare a
legaturii. Numarul de secventa e folosit pentru a rejecta copiile duplicate
ale unui LSP. De cate ori un ruter primeste un nou LSP numerotat cu x de
la sursa S, el va memora valoarea x, si va rejecta orice LSP-uri urmatoare
avand numarul de secventa x. Dupa ce procesul de actualizare isi termina
treaba, procesul de decizie din fiecare ruter aplica algoritmul lui Dijkstra
tabelei de retea formate de pachetele de stare a legaturilor.
75
Un ruter poate avea putine rutere vecine, sau poate avea vecine multe
calculatoare gazda/host-uri (noduri terminale), conectate direct la ruter in
acelasi LAN. De exemplu, in fig.4.14, ruterul R1 are 500 de noduri
terminale vecine, E1...E500. Marile LAN-uri pot avea si mai multe noduri
terminale vecine, ceea ce conduce la problema urmatoare.
Problema:La cei 8 bytes alocati fiecarui nod terminal (6 bytes pentru
identificarea nodului in sine, iar 2 bytes reprezinta informatia de cost adica
LSP-uri), LSP-ul poate fi foarte mare (pentru 5000 de noduri terminale,
dimensiunea poate ajunge la 40,000 bytes). Aceasta valoare este mult prea
mare pentru ca LSP-ul sa incapa intr-un cadru chiar si de dimensiune
maxima din majoritatea legaturilor de date obisnuite. De exemplu,
protocolul Ethernet are dimensiune maxima a cadrelor de 1500 bytes, iar
protocolul FDDI, 4500 bytes. Acest lucru implica fragmentarea LSP-urilor
mari intr-un numar mare de cadre de nivel legatura de date, la fiecare nod,
si apoi reasamblarea lor de catre urmatorul nod, s.a.m.d. Pentru a
determina daca toate fragmentele de LSP au fost receptionate, este
necesar, din pacate, un proces de reasamblare costisitor in fiecare nod.

Figura 4.14 Pachetul de stare a legaturii a ruterului R1 (cu 500 de noduri vecine) poate
fi prea mare ca sa incapa intr-un cadru de nivel legatutra de date. Acest lucru implica
fragmentari si reasamblari ineficiente, la fiecare salt.

Figura 4.15 Evitarea fragmentarii hop-by-hop impartind ruterele mari in pseudorutere

De asemenea, creste latenta propagarii LSP-ului. Presupunem ca


fiecare LSP incape in M cadre de nivel legatura de date, ca diametrul
retelei este D, iar timpul de transmitere pe legatura al unui cadru de nivel
legatura de date este de o unitate de timp. Atunci, timpul de propagare al
unui LSP, tinind cont de reasamblarea in fiecare nod va fi D ⋅ M . Daca
ruterele in cauza nu ar fi avut de asteptat pentru reasamblarea LSP-urilor
pas cu pas, intarzierea datorata propagarii ar fi fost doar M + D . Cand a
fost proiectat protocolul pentru starea legaturii, aceste probleme au fost
detectate de cei care au revizuit ulterior specificatiile.
Pe de alta parte, pare imposibil ca fragmentele sa fie transmise
independent unul de celalalt, deoarece LSP-ul poarta un singur numar de
secventa care este crucial in procesul de actualizare. Simpla copiere a
numarului de secventa in fiecare fragment nu ajuta, deoarece duce la
76
rejectarea a fragmentelor ulterioare, pe motiv ca au acelasi numar de
secventa cu primul. Rezolvarea cestei probleme aparent imposibila se face
prin deplasarea calculului in spatiu, pentru a evita nevoia de fragmentare
pas cu pas. Sunt permise schimbari in protocolul de rutare LSP.
Sugestie: trebuie oare ca toti cei 5000 de octeti de informatie de la
nodurile finale sa se afle in acelasi LSP ? Vezi principiul P3c, deplasarea
calculului in spatiu (nodurile terminale vor fi astfel proiectate incat sa
calculeze dimensiunile maxime ale fragmentelor care pot trece prin orice
retea si sa faca ele insele fragmentarea).
Solutie. Daca fragmentele individuale ale LSP-ului original al ruterului
R1 s-ar propaga independent unul de altul, fara reasamblarea pas cu pas,
atunci fiecare fragment trebuie sa fie un LSP in sine, cu un numar de
secventa separat. Aceasta observatie extrem de importanta duce la o idee
eleganta.
Se modifica protocolului de rutare a informatiilor de stare LSP pentru a
permite oricarui ruter R1 sa apara ca mai multe pseudo-rutere R1a , R1b , R1c
(figura 4.15). Astfel, grupul initial de noduri terminale este impartit intre
aceste pseudo-rutere, si in consecinta LSP-ul fiecarui pseudo-ruter va
incapea in majoritatea cadrelor de nivel legatura de date fara a mai fi
nevoie de fragmentare. De exemplu, daca majoritatea dimensiunilor de
nivel legatura de date este de minim 576 bytes, intr-un cadru de nivel
legatura de date pot incapea ≈ 72 de noduri terminale.
Cum ar fi implementat acest concept de pseudo-ruter? In propagarea
initiala a LSP-ului, fiecare ruter avea un ID de 6 bytes, introdus in LSP-ul
global transmis de ruter. Pentru a permite existenta pseudoruterelor
schimbam protocolul si permitem ca LSP-ul sa aiba 7 bytes de ID (6 bytes
ID + 1 byte pseudoruter). ID-ul pseudoruterului poate fi asignat de catre
ruterul actual care contine toate pseudoruterele. Permitand 256 de
pseudorutere pe un singur ruter, atunci acest ruter va putea suporta in jur
de 18,000 de noduri terminale.
Propagarea LSP-urilor trateaza pseudoruterele separat, dar pentru
calculul rutei este crucial ca pseudoruterele separate sa fie tratate ca un
singur ruter. In exemplul nostru toate nodurile terminale sunt de fapt
conectate direct la acelasi ruter R1. Acest lucru este usor de realizat,
deoarece toate LSP-urile cu primii 6 bytes identici pot fi recunoscute ca
provenind de la acelasi ruter.
Pe scurt, ideea de baza este aceea de a deplasa calculul in spatiu (P3c),
asigurand ca procesul de fragmentare a LSP-ului original in LSP-uri
independente, sa fie facut de catre sursa S si nu de fiecare legatura de date.
Acest exemplu permite intelegerea gandirii sistemice. Bineinteles,
implementatorii au preferat solutiei originale, aceasta solutie, inventata de
Radia Perlman.
Exercitii:
• Cum poate un ruter asigna noduri terminale unui pseudoruter? Ce
se intampla daca un ruter are la inceput o multime de noduri terminale (si
deci o multime de pseudorutere) iar la un moment dat, majoritatea
nodurilor terminale dispar? Aceasta poate genera situatia in care raman o
multime de pseudorutere, fiecare avand doar cateva noduri terminale. De
ce nu e bine asa si cum putem remedia situatia ?

77
• La fel ca in exemplele de reducere a severitatii conditiilor, aceasta
solutie poate genera conditii incorecte neasteptate (temporare deobicei si
fara sa fie foarte grave). Tinand cont de solutia gasita la exercitiul
precedent, descrieti un scenariu in care un anume ruter, de exemplu R2,
poate gasi (la un moment dat) eroarea in care, in baza sa de date pentru
LSP-uri, acelasi nod terminal (de ex E1) apartine la doua pseudorutere,
R1a si R1b. De ce nu este acest caz mai rau decat rutarea obisnuita a LSP-
urilor ?

4.9 Supravegherea structurii traficului


Anumite protocoale de retea pretind ca sursele sa nu transmita niciodata
datele mai rapid decat cu o rata impusa. In loc de a specifica rata medie pe
o perioada lunga de timp, protocolul poate deasemenea specifica volumul
maxim de trafic, B, in biti pe care sursa-i poate transmite in orice perioada
de T secunde. Acest lucru limiteaza sursa la rata medie de B / T biti pe
secunda. De altfel, acest lucru limiteaza si “gradul de rafala” a traficului
utilizatorului la maxim o rafala de dimensiune B la fiecare T unitati de
timp. Alegand, de exemplu, o valoare mica a parametrului T, se limiteaza
considerabil rafalele de trafic. Rafalele fac probleme retelei, deoarece
perioadele cu trafic mare si pierderi de pachete sunt urmate de perioade de
inactivitate.
Daca fiecare sursa isi indeplineste contractul (nu transmite peste volumul
de trafic specificat pe o perioada data), reteaua poate oferi garantii de
performanta si sa asigure ca nimic din trafic sa nu fie pierdut, precum si
livrarea la timp a traficului. De cele mai multe ori regulile sunt respectate,
fie pentru ca sunt considerate corecte de catre unii, fie de teama
penalizarilor, de catre altii. Dar acest lucru nu se intampla intotdeauna,
astfel ca urmarirea traficului e foarte importanta. Fara ea, unii utilizatori ar
putea profita, incorect, de banda retelelor.
Presupunem ca fluxurile de trafic sunt identificate prin adresa sursei si a
destinatiei, precum si prin tipul de trafic. Fiecare ruter trebuie sa sa se
asigure ca nici un flux sa nu transmita mai mult de B biti in nici o perioada
de T secunde.Pentru ruter, cea mai simpla solutie este sa foloseasca un
singur timer care ticaie la fiecare T secunde, si sa numere bitii trimisi in
fiecare perioada, folosind cate un contor per flux. Daca la sfarsitul fiecarei
perioade contorul depaseste B, ruterul detecteaza o violare de contract.
Dar folosind un singur timer pot fi supravegheate doar anumite perioade.
Presupunem de exemplu, fara a pierde din generalitate, ca timer-ul
porneste la momentul 0 de timp.Atunci singurele perioade verificate vor fi
[0, T ] , [T , 2T ] , [ 2T ,3T ] ,.... , ceea ce nu asigura ca fluxul sursei nu violeaza
contractul de trafic in perioade ca [T / 2,3T / 2] , suprapuse peste perioadele
supravegheate. De exemplu, in stanga figurii 4.16, fluxul transmite o
rafala de lungime B, chiar inainte ca ceasul/timerul sa ticaie la momentul
t, si trimite a doua rafala de lungime B chiar dupa ticaitul de la momentul
T.
O solutie ar fi utilizarea timer-elor si contoarelor multiple. De exemplu,
avem un ruter care poate utiliza un timer ce incepe la momentul 0, iar
urmatorul timer, la momentul T/2. Din pacate, sursa poate viola in
continuare contractul, trimitand legal, B biti in fiecare perioada
78
supravegheata, dar ilegal, mai mult de B biti intr-o perioada ce se
suprapune celor supravegheate. In partea dreapta a figurii 4.16 avem o
sursa ce nu se incadreaza in contract. Aceasta trimite o rafala de B bits la
sfarsitul primei perioada, si o a doua rafala la inceputul celei de-a treia
perioade, in total trimitand 2B biti intr-o perioada cu putin mai mare decat
T/2. Din pacate nici unul dintre timer-e nu vor vedea acest lucru drept un
viol. Acest lucru duce la urmatoarea problema.

Figura 4.16 Folosirea naiva a unuia sau a mai multor timer-e (pentru a verifica daca
sursa nu transmite un volum de trafic mai mare decat B biti la fiecare T secunde)

T Aleator T Aleator

Figura 4.17 Daca se iau pauze aleatoare intre intervalele de supraveghere ruterele vor
detecta violarile de contract cu o probabilitate mare

Problema. Folosirea timerelor multiple este scumpa si nu garanteaza ca


traficul de date va respecta contractul. Este usor de vazut faptul ca,
utilizand chiar si un singur timer, sursa nu poate trimite mai mult de 2B
biti in orice perioada de T secunde. Un mod de rezolvare ar fi sa
consideram ca un viol de gradul 2 nu merita efortul de a supraveghea
traficul. Dar in cazul in care avem o legatura transcontinentala in care
banda este extrem de importanta, violul de gradul 2 este o reala problema.
Cum ar putea fi prins un viol de trafic cu un singur timer?
Indiciu: Considerand exploatarea unui grad de libertate (P13), presupus
fixat in solutia naiva. Intervalele de supraveghere ar trebui sa inceapa la
intervale fixate de timp? Considerati folosirea lui P3a.
Solutie: Intervalele de supraveghere nu trebuie sa fie fixe, deci pot
exista spatii goale distribuite arbitrar intre acestea. Cum ar putea fi
localizate spatiile goale? Deoarece o sursa ce nu respecta contractul poate
sa isi aleaga in orice moment inceputul perioadei de viol a contractului, o
idee simpla (P3a) conduce la urmatoarea solutie (figura 4.17)
Ruterul utilizeaza un singur timer de T unitati si un singur contor, la fel
ca in cazul precedent. Un interval de supraveghere se termina cu un tact de
timer; daca contorul este mai mare decat B, a fost detectat un viol. Apoi,
este setat un indicator sa arate ca timerul este folosit acum doar pentru a
introduce un spatiu aleator. Dupa care timerul este resetat pentru un
interval aleator de timp, cuprins intre 0 si T. Cand timerul indica
79
momentul, indicatorul este pus pe valoare nula, contorul va fi initializat,
iar timerul resetat pentru o perioada T.
Exercitiu: Presupunem contorul initializat si mentinut in aceasta stare
atat in timpul perioadelor de pauza, cat si in timpul intervalelor de
supraveghere. Poate ruterul sa faca o deductie corecta in timpul unei
asemenea perioade, chiar si in cazul in care perioada pauzei este mai mica
de T unitati?

4.10 Identificarea celor care monopolizeaza resursele


Sa presupunem ca un dispozitiv doreste sa urmareasca resursele, cum ar
fi memoria de pachete alocata diferitelor surse din ruter. Dispozitivul
respectiv ar dori sa localizeze simplu sursa ce consuma cea mai mare
parte de memorie, astfel incat, ulterior, sa poata recupera memoria “furata”
de acest monopolizator de resurse. Figura 4.18 arata cinci surse diferite,
avand fiecare propriul consum de banda: 1, 9, 30, 24 si 7 unitati.
Monopolizatorul de resurse este sursa S3.
O solutie simpla de a identifica un monopolizator de resurse ar fi aceea
de a utiliza o gramada dezorganizata/heap.Dar, la viteze ridicate si daca
numarul surselor depaseste numarul 1000, solutia ar fi prea scumpa.
Indicarea gradului de folosire al memoriei se face folosind numere intregi
de la 1 la 8000, caz in care sortarea cu galeti/bucket sorting nu mai
functioneaza bine, deoarece trebuie cautate 8000 de intrari pentru a gasi
monopolizatorul de resurse.

Figura 4.18 Gasirea sursei care monopolizeaza resursele

Daca dispozitivul nu doreste sa gaseasca neaparat sursa cu debit maxim,


ci se multumeste si cu cea de pe locul doi (corectitudinea maxima nu este
importanta in P3b), in exemplul de mai sus, raspunsul va fi S4 (24) in loc
de S3 (30). Aceasta duce la urmatoarea problema.
Problema. Un modul software sau hardware trebuie sa urmareasca
resursele necesare diferitilor utilizatori. De asemenea, modulul are nevoie
ca, printr-o metoda ieftina, sa gaseasca utilizatorul ce consuma cele mai
multe resurse. Deoarece gramezile dezordonate simple sunt de viteza
mica, inginerii de protocoale de retea sunt dispusi sa mai scada cerintele
de sistem (P3b) si sa se multumeasca si cu al doilea consumator d.p.d.v.
ierarhic. Cum se poate ca aceasta diminuare a exactitatii cautarii sa fie
transpusa intr-un algoritm mai eficient?
Indiciu: Sa consideram utilizarea a trei principii: cea a compromisului
volum de calcul - in defavoarea preciziei (P3b), sortarea prin metoda
galetilor si utilizarea tabelelor de corespondenta (P4b, P2a).
Solutie: Deoarece rezultatul cautarii poate fi gasit cu o inexactitate de
ordinul 2, este normal sa includem utilizatorii a caror resurse consumate se
afla pe locul 2, in acelasi grup de utilizatori de resurse. Acest lucru ar
80
putea fi un castig daca numarul rezultant de grupuri este mult mai mic
decat numarul initial de utilizatori; deci, gasirea celui mai mare grup va fi
mai rapida decat gasirea celui mai mare utilizator de resurse. Aceasta idee
este foarte asemanatoare cu cea de la asamblarea din rutarea ierarhica, in
care numarul destinatiilor sunt grupate sub un prefix comun; acest lucru
poate face ca procesul de rutare sa fie unul mai putin precis dar poate sa
reduca numarul intrarilor de rutat. Rezulta urmatoarea idee.

Figura 4.19 Agregarea utilizatorilor, cu factor 2 de consum, duce la un numar mic de


agregari, ai caror membri pot fi reprezentati folosind un bitmap

Gruparea binomiala poate fi folosita (figura 4.19) toti utilizatorii fiind


plasati in grupe/galeti conform cu consumul de resurse folosit; galeata i
contine toti utilizatorii a caror consum de resurse este intre 2i si 2i+1 − 1 . In
figura 4.19, utilizatorii S3 si S4 se afla in intervalul [16, 31], deci se gasesc
in aceeasi galeata.
Fiecare galeata contine o lista nesortata a inregistrarilor resurselor
tuturor utilizatorilor ce intra in limitele intervalului sau. De aceea, in figura
4.19, S3 si S4 sunt in aceeasi lista. Structura de date contine si o
harta/bitmap, cu un bit corespunzator pentru fiecare galeata, care este setat
sa arate daca galeata asociata lui este plina. In consecinta, bitii
corespunzatori galetilor [1, 1], [4, 7], [8, 15], si [16, 31] sunt setati pe
valoarea 1, in timp ce bitul corespunzator lui [2, 3] este setat pe 0. Deci,
pentru a gasi monopolizatorul de resurse, algoritmul trebuie doar sa caute
bitul cu pozitia i aflat in pozitia extrema dreapta a hartii. Apoi, algoritmul
va returna utilizatorul aflat in capul listei galetii corespunzatoare pozitiei i.
In consecinta, (fig.4.19), algoritmul va returna valoarea acceptabila S4, in
loc de valoarea corecta S3.
Exercitii:
• Cum este mentinuta structura datelor? Ce se intampla daca un
utilizator isi reduce nivelul necesar de resurse de la 30 la 16? Ce fel de
liste sunt necesare pentru o mentenanta eficienta ?
• Cat de mare e fiecare harta? Cum sa gasm eficient cel mai din dreapta
bit?

4.11 Debarasandu-ne de lista de conexiuni TCP deschise


Un protocol de transport ca TCP, aflat in calculatorul X, va tine seama de
absolut toate conversatiile simultane pe care statia de lucru respectiva le
are cu alte PC-uri. O conexiune reprezinta starea partajata intre doua
puncte terminale ale unei conversatii. Daca un utilizator doreste sa trimita
un mail de pe statia X pe statia Y, atunci programul de mail de pe statia X
trebuie sa stabileasca o conexiune (stare partajata) cu programul de mail

81
din statia Y. Un server ocupat, ca cel de Web, poate avea o multime de
conexiuni concurente.
Starea conexiunii consta din lucruri ca numarul de pachete trimise de
statia X, neconfirmate de statia Y. Orice pachet de date neconfirmat de
statia Y intr-un anumit interval de timp, trebuie retransmis de X. Pentru a
face retransmisia, protocoalele de transport obisnuiesc sa aiba timere
periodice ce declanseaza retransmisia oricarui pachet a carui confirmare
intarzie sa apara dupa o anumita perioada de timp.

Figura 4.20 Implementarea x-kernel-ului foloseste o tabela hash de asociere a


conexiunilor pentru stare (pentru expedierea pachetelor)si o lista inlantuita de conexiuni
(pentru timere)

Codul TCP Berkeley (BSD), disponibil gratis, tine o lista a conexiunilor


deschise (fig. 4.20) pentru a examina, la fiecare declansare a timerului,
daca e nevoie sa faca o retransmisie. Oricum, daca soseste un pachet la
statia X, TCP-ul lui X trebuie sa stabileasca rapid carei conexiuni apartine
pachetul, pentru a reactualiza starea conexiunii. Fiecare conexiune este
identificata de un identificator de conexiune, transportat de fiecare pachet.
Pentru determinarea conexiunii careia apartine un anumit pachet, ar fi
necesara cautarea lui in intreaga lista (cazul cel mai defavorabil), ceea ce
ar fi prea lent pentru serverele cu multe conexiuni. In consecinta,
implementarea x-kernel a adaugat un tabel de dispersie implementarii
BSD (P15) pentru a asocia eficient identificatorii de conexiune din
pachetele de date cu starea conexiunii corespunzatoare. Tabela de
dispersie este un vector de indicatori, indexati cu valoarea dispersiei care
indica spre listele de conexiuni, ce disperseaza catre aceeasi valoare. In
plus, lista originala de conexiuni inlantuite a fost retinuta pentru procesari
de timer, in timp ce tabela de dispersie ar fi trebuit sa mareasca viteza
procesarii de pachete.
Ciudat, dar masuratorile efectuate in cazul noii implementari au aratat o
incetinire a vitezei. Dupa masuratori riguroase, s-a constatat faptul ca
problema ar fi stocarea redundanta, a informatiilor despre conexiuni, lucru
ce a dus la reducerea eficientei memoriei cache din procesoarele moderne.
Acesta este un exemplu despre cum o imbunatatire evidenta a unei
anumite componente poate afecta alte parti din sistem. Trebuie mentionat
ca memoria principala poate fi ieftina, dar memoria rapida precum
memoria de date cache este deseori limitata. Structurile folosite uzual, ca
listele de conexiuni, ar trebui sa ramana in memoria cache de date, daca
sunt destul de mici ca sa incapa in ea.
Solutia evidenta este evitarea redundantei. Tabelul de dispersie e necesar
pentru o gasire rapida. De asemenea, rutina de timer trebuie sa caute

82
periodic si eficient prin toate conexiunile existente. Acest lucru duce la
urmatoarea problema.
Problema. Se poate scapa de pierderea cauzata de lista de conexiuni
explicita, dar sa se retina si tabela de dispersie? E rezonabila adaugarea
unei mici informatii suplimentare tabelei de dispersie. Cand facem acest
lucru, se poate observa ca lista originala de conexiuni a fost creata cu
dublu inlantuita, pentru a permite la terminare o stergera facila a
conexiunilor. Dar acest lucru duce la depozitare si dilueaza memoria de
date cache. Cum ar putea fi utilizata o lista simplu inlantuita, fara
incetinitirea procesului de stergere?
Indiciu: Prima parte este usor de reparat, inlantuind intrarile valabile din
tabela de dispersie intr-o lista. Cea de-a doua parte este mai dificil de
realizat (evitarea listei cu dubla inlantuire, care ar necesita doi indicatori
per intrare a tabelei de dispersie). O lista de legatura consta din noduri,
fiecare din ele contine o identitate a conexiunii (96 de biti pentru IP), plus
doi indicatori (sa zicem 32 biti fiecare) pentru o stergere facila. Din
moment ce tabela de disperse este necesara pentru o demultiplexare
rapida, lista de legaturi poate fi indepartata daca intrarile valide din tabela
de dispersie sunt conectate ca in figura 4.21, iar un indicator este pastrat in
capul listei. La un ticait de timer, rutina de retransmitere va scana periodic
lista. Scanarea intregii tabele de dispersie are o eficienta redusa, deoarece
pot exista o multime de locatii goale in interiorul sau.
Solutia implicita ar fi aceea de a adauga doi indicatori la fiecare intrare
valida a tabelei de dispersie in scopul de a implementa o lista cu dubla
legatura. Din moment ce acesti indicatori pot fi indecsi ai tabelei de
dispersie in loc de indicatori arbitrari catre memorie, indecsii nu trebuie sa
aiba dimensiunea mai mare decat dimensiunea tabelei de dispersie. Chiar
si cea mai mare tabela de dispersie ce contine conexiuni, nu ar avea nevoie
de mai mult de 16 bits (in general , mult mai putin). Solutia implicita da
rezultate bune, adaugand cel mult 32 de bits, in loc de 160 per intrare, deci
o economie de 128 de bits. In orice caz, este posibil de a face mai mult, in
sensul de a adauga numai 16 bits per intrare. Considerati utilizarea
evaluarii de viteza redusa (P2b) si specificatiile mai putin severe (P3).

Figura 4.21 Inlantuirea intrarilor tabelei valide de dispersie cu pointeri de inaintare si


stergere relaxata. Cu linie intrerupta s-a u notat inregistrarile marcate ca sterse dar care
vor fi procesate doar in urmatoarea iteratie
83
Solutie: O lista dublu inlantuita este utila doar pentru stergeri eficiente.
Cand o anumita conexiune este terminata, ar fi indicat ca rutina de stergere
sa gaseasca intrarea valida anterioara celei sterse, pentru a putea lega lista
precedenta de urmatoarea. Acest lucru ar necesita ca fiecare intrare din
tabela de dispersie sa memoreze un indicator catre intrarea valida
anterioara din lista.
In schimb, considerati principiul P3, relaxarea conditiilor impuse
sistemului. In mod normal, se presupune ca atunci cand o conexiune este
terminata, locul ei din memorie este recuperat imediat. Pentru a recupera
un loc de depozitare, intrarea din tabela de dispersie ar trebui pusa intr-o
lista goala, din care sa poata fi folosita de catre o alta conexiune. In orice
caz, daca tabela de dispersie este mai mare decat este strict necesar, nu
este esential ca locul de depozitare utilizat de conexiunea ce tocmai a fost
terminata sa fie reutilizat imediat.
Fiind date cerinte mai putin severe, implementarea poate sa stearga cu
incetineala starea conexiunii. Cand o conexiune este terminata, intrarea
respectiva trebuie marcata ca fiind libera. Acest lucru necesita un extrabit
de stare, la fel ca in P12, fiind ieftin. Stergerea intrarii nefolosite din tabela
de dispersie E, implica legatura dintre intrarea aflata inaintea lui E cu cea
aflata dupa E. de asemenea, necesita returnarea lui E unei liste goale.
Aceasta stergere poate fi facuta la urmatoarea trecere prin lista, atunci
cand este intalnita o intrare nefolosita.
Exercitii
• Scrieti un pseudocod pentru adaugarea unei noi conexiuni, terminarea
unei conexiuni si terminarea sa (bazata pe timer).
• Cum putem face sa avem liste cu legaturi simple pentru listele de
conexiuni in fiecare lista a tabelelor dispersie?
• Cineva, interesat in trucuri inteligente si care nu gandeste niciodata
pana la capat o problema anume, sugereaza o cale pentru a evita folosirea
indicatorilor inapoi, in orice lista cu dubla legatura. Presupunem ca nodul
X trebuie sters. In mod normal, rutinei de stergere i se indica manevra de
regasire a lui X, deobicei un indicator spre X. In schimb, manevra ar
putea fi un indicator catre nodul respectiv inaintea lui X in lista inlantuita
(cu exceptia cazului cand X se afla in fruntea listei si manevra este un
pointer nul). Acest mod va permite implementarii sa localizeze eficient
atat nodul anterior lui X, cat si cel de dupa X, utilizand doar
indicatoriinspre inainte ? Prezentati un contra exemplu care invalideaza
acest mod de lucru.

4.12 Retinerea confirmarilor


Protocoalele de transport, ca TCP, asigura ca datele sa fie livrate la
destinatie cu confirmarea (ack) primirii fiecarui fragment de date
receptionat. Aceasta actiune este similara cu mailul de confirmare.
Pachetele si confirmarile sunt numerotate. Confirmarile sunt, de obicei
cumulative, un “ack” pentru pachetul N confirma implicit toate pachetele
primite anterior, numere mai mici sau egale cu N.
Confirmarile cumulative permit receptorului flexibilitatea de a nu
transmite un ack de fiecare data cand primeste un pachet. In schimb,
confirmarile pot fi comasate (P2c). In figura 4.22 programul de transmisie
84
a fisierelor trimite blocuri de fisiere, unul in fiecare pachet. Blocurile 1 si 2
sunt confirmate individual, dar blocurile 3 si 4 printr-un singur ack pentru
blocul 4.
Reducerea confirmarilor este un lucru bun atat pentru emitator, cat si
pentru receptor. Desi confirmarile au dimensiuni reduse, ele contin antete
ce trebuie procesate de fiecare ruter, de sursa si de destinatie. Mai mult,
fiecare pachet primit, oricat de mic ar fi, poate cauza o intrerupere,scumpa,
la destinatie. Ideal ar fi ca receptorul sa comaseze cat de multe confirmari
poate. Dar care ar fi criteriul dupa care receptorul comaseaza confirmarile?
Rezulta urmatoarea problema.
Problema. Retinerea confirmarilor este dificil de realizat la un receptor
care nu are un software bine pus la punct. In figura 4.22, de exemplu, daca
blocul 3 este receptionat primul si procesat rapid, cat ar trebui sa mai
astepte receptorul dupa blocul 4, inainte de a confirma blocul 3 ? Exista
sansa ca blocul 4 sa nu mai ajunga, deci retinerea confirmarii pentru blocul
3 ar cauza o functionare incorecta. Solutia clasica ar fi aceea de a crea un
contor de timp pentru confirmarile retinute; la expirarea sa, va fi trimisa o
confirmare cumulativa. Astfel se limiteaza timpul maxim cat o confirmare
oarecare va putea fi retinuta. Dar acest contor de timp creaza anumite
probleme. Unele aplicatii sunt sensibile la intarzieri. Adaugarea acestui
contor de timp poate creste intarzierea in cazurile in care emitatorul nu mai
are date de transmis. Daca protocolul de transport ar putea fi modificat, ce
informatie trebuie adaugata pentru a evita intarzierile nedorite, permitand
totusi, comasarea efectiva a confirmarilor?
Indiciu : Intr-o aplicatie ca FTP, care modul software “stie“ ca mai
exista date de trimis ? Pentru retinerea confirmarilor, care modul ar fi ideal
sa stie ca ar mai fi date de trimis? Considerati utilizarea lui P9 si P10.

Figura 4.22 Ack-urile cumulative permit receptorului sa confirme cateva pachete cu


un singur ack.(ex : blocurile 3 si 4), dar pretind monitorizarea ack-urilor la receptor

Solutie : Intr-o aplicatie ca transferul de date, aplicatia de la emisie stie


ca mai exista date ce trebuiesc transmise (de exemplu: va urma blocul 4
dupa blocul 3). Aplicatia de la emisie va fi dispusa si sa tolereze latenta
datorata comasarii confirmarilor. In orice caz, trebuie ca aceasta informatie
sa fie cunoscuta de nivelul transport la receptorului. Aceasta observatie
duce la urmatoarea propunere.
Aplicatia de la emisie va transmite un bit catre nivelul transport al
emitatorului (interfetei de transport a aplicatiei) care este pus pe 1 atunci
cand aplicatia mai are de transmis date. Presupunem ca protocolul de
transport poate fi modificat astfel incat sa aiba un bit de retinere. Nivelul

85
transport de la emisie poate utiliza informatia data de aplicatie pentru a seta
un bit w de retinere, in fiecare pachet pe care il transmite; bitul w va fi
anulat cand emitatorul doreste o confirmare imediata.
De exemplu, in figura 4.23 nivelul transport de la emisie este informat de
catre aplicatia transfer de fisiere, ca fisierul de trimis are patru blocuri.
Astfel, nivelul transportde la emisie va valida bitul de retinere pentru
primele trei pachete, respectiv il va invalida pentru al patrulea pachet.
Receptorul va trimite in consecinta, o singura confirmare, in loc de patru.
Dar o aplicatie care este sensibila la intarzieri, poate sa nu trimita nici o
informatie privitoare la datele de transmis. Bitul de retinere este un indiciu,
pe care receptorul poate sa-l ignore si sa trimita oricum o confirmare. Desi
solutia pare ingenioasa, nu e o idee buna pentru protocolul TCP actual.
Exercitii:
• O tehnica de reducere a numarului de confirmari este returnarea
confirmarilor in pachetele de date dinspre receptor spre emitator. In acest
scop, majoritatea protocoalelor de transport, ca TCP, au campuri
suplimentare in pachetele de date pentru confirmarile in sens opus.
Returnarea confirmarilor in pachete de date e compromisul clasic
latenta/eficienta. Cat timp ar trebui sa astepte nivelul transport datele din
sens opus? Dar, exista aplicatii uzuale in care aplicatia de la emisie
cunoaste aceasta informatie. Cum poate fi extinsa solutia precedenta pentru
a permite si returnarea confirmarilor prin pachete de date, si comasarea lor?

Figura 4.23 Telegrafierea intentiei transmitatorului, folosind bitul w de retinere

Exercitii:
• O tehnica de reducere a numarului de confirmari este returnarea
confirmarilor in pachetele de date dinspre receptor spre emitator. In acest
scop, majoritatea protocoalelor de transport, ca TCP, au campuri
suplimentare in pachetele de date pentru confirmarile in sens opus.
Returnarea confirmarilor in pachete de date e compromisul clasic
latenta/eficienta. Cat timp ar trebui sa astepte nivelul transport datele din
sens opus? Dar, exista aplicatii uzuale in care aplicatia de la emisie
cunoaste aceasta informatie. Cum poate fi extinsa solutia precedenta pentru
a permite si returnarea confirmarilor prin pachete de date, si comasarea lor?
• Evaluarea imbunatatirilor pretinse se poate face cu intrebari de test. De
exemplu, schimbarea poate afecta restul sistemului ? De ce ar putea ca

86
retinerea confirmarilor sa afecteze alte parti ale protocolului de transport,
cum ar fi controlul fluxului si al congestiilor ?

4.13 Citirea incrementala a bazelor mari de date


Presupunem ca un utilizator citeste continuu o baza de date mare de pe
un site web. Continutul paginii web se poate schimba, dar cititorul doreste
doar actualizarile incrementale fata de ultima citire. In figura 4.24 avem o
baza de date ce contine cateva produse alimentare foarte raspandite, citite
constant de catre cititorii din lumea larga, ce doresc sa fie la curent cu
moda culinara. Din fericire, moda culinara variaza lent.
2pm 3pm 6pm

Figura 4.24 Schimbari lente a bazei de date cu alimente la orele 2pm, 3pm, 6pm. Cel
care urmareste baza de date vrea sa vada doar schimbarile (aici, de la 2pm la 3pm s-a
schimbat pretul la bauturi nealcoolice, si de la 3pm la 6pm, la cereale)

Un cititor vrea la ora 18 sa vada doar diferenta fata de ora 14 : Coke fata
de Pepsi, Wheaties fata de Cheerios. Alt cititor vede pagina la ora 15:00 si
apoi la ora 18:00 vrea sa vada ce s-a schimbat la Wheaties fata de
Cheerios. Acest lucru genereaza urmatoarea problema.
Problema. Cum sa realizeze eficient baza de date asemenea interogari
incrementale (incremental queries) ? Ar fi dificil ca baza de date sa-si
aminteasca ce a citit fiecare utilizator ultima data, avand milioane de
utilizatori.
Indiciu: Daca baza de date nu memoreaza nici o informatie referitoare la
ultima citire a unui utilizator, solicitarea de citire a utilizatorului trebuie sa
contina ceva informatie (P10) despre ultima sa citire. Transmiterea
detaliilor despre ultima citire inseamna un efort urias si e total ineficienta.
Care ar fi partea de informatie care sa caracterizeze succint ultima cerere a
utilizatorului? Putem adauga o stare redundanta suplimentara (P12) la
baza de date ce permite usor indexarea, folosind informatia de la utilizator
pentru a facilita interogarea incrementala.
Solutie: Cererile de vizualizare ale utilizatorului trebuie sa contina o
anumita cantitate de informatie (P10) despre ultima solicitare de
vizualizare facuta de acesta. Cea mai succinta si relevanta informatie este
timpul la care a fost facuta ultima cerere. Daca cererea utilizatorului
contine timpul ultimei citiri, atunci baza de date trebuie organizata astfel
incat sa-si actualizeze eficient continutul la intervale date de timp.
Memorarea unor copii ale bazei de date din momentele anterioare este o
metoda ineficienta, si poate fi evitata daca se memoreaza doar
modificarilor incrementale (P12a). Acest lucru duce la urmatorul algoritm.
Se poate adauga bazei de date o lista a actualizarilor, care are in capul
listei ultimele actualizari. Cererile de citire contin momentul T al ultimei

87
citiri, deci cererea poate fi procesata prin scanarea inceputului listei, pentru
a gasi toate actualizarile ulterioare lui T.
De exemplu, in figura 4.25 in capul listei de actualizari se afla ultimele
modificari (comparati cu figura 4.24) la ora 18:00 de la Wheaties la
Cherios, si modificarea precedenta, de la ora 15:00 de la Coke la Pepsi.
Consideram o cerere de citire efectuata la ora 17:00. Cand scanam lista din
varf, procesarea cererilor va gasi actualizarea de la ora 18:00 si se va opri
cand va ajunge la actualizarea de la ora 15:00, deoarece 15:00 < 17:00,
deci cererea va returna doar prima actualizare.
Ultima reactualizare
Wheaties Æ Cheerios Coke Æ Pepsi 6 pm
6 pm 3 pm
Pepsi
Mere
Placinte
Actualizarea listei Cheerios

Figura 4.25: Rezolvarea problemei reactualizării incrementale folosind o listă de


reactualizare
Exercitii:
• Daca o intrare se modifica de mai multe ori, atunci modificarea acelei
intrari poate fi memorata redundant in lista, dar cu costuri suplimentare de
timp si spatiu. Ce principiu poate fi folosit pentru a evita redundanta?
Presupunem că baza de date este doar o colecţie de înregistrări şi se doreşte
ca fiecare înregistrare să apară cel mult o dată în această listă incrementală.
Dacă numărul înregistrărilor este mare sau metoda de mai sus nu este
aplicată, atunci dimensiunea listei incrementale va creşte foarte mult.
Sugeraţi o metoda de reducere periodică a dimensiunii listei incrementale.
• Dacă numărul înregistrărilor este mare sau metoda de mai sus nu este
aplicată, atunci dimensiunea listei incrementale va creşte foarte mult.
Sugeraţi o metoda de reducere periodică a dimensiunii listei incrementale.

4.14 Cautarea binara a identificatorilor lungi


Generaţia viitoare de Internet (IPv6) îşi propune să folosească adrese pe
128biţi pentru a putea găzdui mai multe terminale. Dacă se doreşte
identificarea unei adrese pe 128 biţi, şi algoritmul rulează pe un procesor
cu 32 biţi pe cuvant, atunci fiecare comparaţie a două adrese lungi va dura
128/32=4 operaţii În cazul general presupunem că fiecare identificator este
compus din W cuvinte (în exemplu W=4), atunci căutarea binară naiva va
avea nevoie de W log N comparaţii, ceea ce e scump. Dacă toţi
identificatorii au identice primele W − 1 cuvinte, atunci sunt suficiente doar
log N comparaţii. Problema e de modificare a algoritmului de căutare
binară a.î. să se ajungă la un număr de log N + W comparaţii. Strategia este
de a lucra pe coloane, începând cu cel mai semnificativ cuvânt şi efectuând
căutarea binară în acea coloană, până când se gaseste o egalitate în acea
coloană. În acest punct, algoritmul trece la următorul cuvânt din dreapta şi
continuă căutarea binară de unde a rămas.

Å cuvinte de dimensiune W Æ

A C E

88
A D C

Å cuvinte de dimensiune W Æ

A C E
A D C
A Testul 3 Æ M Testul 4 Æ W
B M W
Testul 1Æ B Testul 2 Æ N X
B N Y
B N Z
A Testul 3 Æ M Testul 4 Æ W
B M W
Testul 1Æ B Testul 2 Æ N X
B N Y
B N Z
C N D
Figura 4.26 Cautarea binara a identificatorilor lungi poate duce la un factor de multiplicare
W (numarul de cuvinte din identificator). Metoda naiva, de reducere la un factor aditiv
prin mutare la dreapta in egalitate, esueaza.

Generaţia viitoare de Internet (IPv6) îşi propune să folosească adrese pe


128biţi pentru a putea găzdui mai multe terminale. Dacă se doreşte
identificarea unei adrese pe 128 biţi, şi algoritmul rulează pe un procesor
cu 32 biţi pe cuvant, atunci fiecare comparaţie a două adrese lungi va dura
128/32=4 operaţii În cazul general presupunem că fiecare identificator este
compus din W cuvinte (în exemplu W=4), atunci căutarea binară naiva va
avea nevoie de W log N comparaţii, ceea ce e scump. Dacă toţi
identificatorii au identice primele W − 1 cuvinte, atunci sunt suficiente doar
log N comparaţii. Problema e de modificare a algoritmului de căutare
binară a.î. să se ajungă la un număr de log N + W comparaţii. Strategia este
de a lucra pe coloane, începând cu cel mai semnificativ cuvânt şi efectuând
căutarea binară în acea coloană, până când se gaseste o egalitate în acea
coloană. În acest punct, algoritmul trece la următorul cuvânt din dreapta şi
continuă căutarea binară de unde a rămas.
În figura 4.26 se consideră W=3, şi se doreşte căutarea identificatorului
BMW. Se consideră că fiecare literă este un cuvânt. Comparaţia începe cu
elementul central de pe coloana din stânga (Testul 1). Deoarece cuvântul
din coloană este identic cu cel căutat se trece la coloana următoare din
dreapta. Testul 2 identifică N>M şi se continuă testul în jumătatea jumătăţii
de sus a coloanei (Testul 3) unde se identifică M. Apoi se trece la coloana
următoare unde se găseşte W (Testul 4), dar din nefericire pe această linie
s-a identificat AMW şi nu BMW, înseamnă că există o problemă.
C N D
Problema: Găsirea unei condiţii care adăugată algoritmului de mai sus
asigura log N + W comparaţii.
Indicatie: Problema apare din cauza ca, atunci cand cautarea binara se
muta la pozitia a patra din coloana 2, se presupune ca acea pozitie incepe
cu B, ipoteza in general falsa. Ce stare trebuie adaugata pentru a evita
aceasta ipoteza falsa, si cum se modifica cautarea in aceasta ipoteza ?
Soluţia: Artificiul consta in adaugarea unei stari fiecarui element al
fiecarei coloane, care impune cautarii binare sa ramina intr-un interval de
89
garda (fig.4.27) Trebuie limitat domeniul de căutare. Pentru fiecare cuvânt
B din coloana cea mai semnificativă se adaugă un pointer care indică
limitele liniilor celorlalte cuvinte care conţin B pe prima poziţie. De data
aceasta, în figura 4.27 se ţine cont şi de domeniul de gardă, care include
rândurile 4 – 7. Acest domeniu este memorat la prima comparaţie a lui B.
Atunci când căutarea trece la coloana 2 şi se găseşte N > M, algoritmul
va încerca să înjumătăţească domeniul de căutare şi deci va încerca să
compare rândul 3. Dar cum 3 < 4 (limita inferioară a domeniului de gardă)
nu se va efectua comparaţia, şi algoritmul va încerca să înjumătăţească din
nou domeniul de căutare binară. Acum se încearcă rândul 4, care e în
domeniul de gardă, se identifică M şi se trece spre coloana din dreapta,
indentificându-se corect BMW.

Å W cuvinte Æ
A C E
A D C
A Tentativă 3 Æ M W
*B Testul 4 Æ *M Testul 5 Æ W
Testul 1Æ B Testul 2 Æ N X
B N Y
*B N Z
C N D

Figura 4.27: Adăugarea unui domeniu de gardă fiecarui element din coloana, pentru
căutarea binară corecta la schimbarea coloanelor

În general fiecare intrare a unui cuvant multiplu W1 ,W2 ,..., Wn va memora


un domeniu de gardă precalculat. Domeniul pentru Wi indică domeniul
care conţine W1 , W2 ,...,Wi în primele i cuvinte. În acest fel se asigură
căutarea pe coloana i+1 doar în domeniul de gardă. Folosind această
strategie se asigură log 2 N + W comparaţii daca avem N identificatori.
Costul suplimentar este adăugarea a doi pointeri de 16 biţi pentru fiecare
cuvânt Wi . Cum majoritatea cuvintelor sunt pe cel puţin 32 biţi înseamnă
ca vor trebui adăugaţi doi pointeri pe 32 biţi pentru ficeare cuvânt, ceea ce
înseamnă dublarea memoriei folosite. O a doua idee pe lângă limitarea
domeniului de căutare este folosirea preprocesării (P2a) care permite
obţinerea unor căutări mai rapide (ideea lui Butler Lampson).
Exerciţiu: Actualizarea structurii de căutare binară necesită reconstruirea
întregii structuri. Totul poate fi reprezentat ca un arbore binar de căutare,
fiecare nod având un pointer „<”, „>” dar şi „=”, care corespunde trecerii la
următoarea coloană din dreapta. Subarborele format din pointerii „=”
reprezintă domeniul de gardă. Folosiţi această observaţie şi tehnicile de
actualizare standard pentru arbori binari echilibraţi şi obţineţi timpi de
reactualizare logaritmici.

4.15 Videoconferinţă prin ATM (Asynchronous Transfer Mode)


În ATM reţeaua stabileşte un circuit virtual, printr-o serie de switch-uri,
înainte ca datele să fie transmise. Standardul ATM permite realizarea unor
circuite virtuale (CV) de la o sursă la mai multe receptoare. Datele
90
transmise de sursă sunt multiplicate şi transmise fiecarui receptor prin
circuitul virtual unu-la-mai mulţi.
Chiar si fara standardizare, e relativ uşor sa avea circuite vituale de la
mai mulţi la mai mulţi, în care fiecare terminal poate fi atât sursă cât şi
receptor. Principala problemă a CV mai mulţi la mai mulţi este atunci când
mai multe staţii vorbesc în acelaşi timp. În acest caz datele de la cele două
surse vor fi întreţesute în mod arbitrar la receptor şi se va naşte confuzie.
În figura 4.28 se prezintă un sistem de videoconferinţă care conectează N
staţii prin intermediul unui comutator. Soluţia din figură foloseşte N
circuite virtuale unu-la mai mulţi, conectând astfel partea audio si video a
fiecărui utilizator cu ceilalţi utilizatori. Stiind care e banda minimă video
Bmin şi banda switch-ului ATM (B) se poate determina numărul maxim de
staţii participante la videoconferinţă: B/Bmin. Nu cumva există o soluţie mai
buna ?

Switch
ATM

N CV 1-la-mai mulţi

Figura 4.28: Sistem de videoconferinţă via ATM

Soluţia este folosirea circuitelor virtuale de la mai mulţi la mai mulţi,


care să înlocuiască cele N circuite unu-la-mulţi. În acest mod se poate scala
banda switch-ului la mai mulţi utilizatori.
Dacă considerăm o singură conexiune mulţi la mulţi pe care o numim C,
atunci o soluţie ar fi folosirea unui protocol de tipul round-robin care
asigură ca la un moment dat doar o singură staţie să-şi conecteze ieşire
video la conexiunea C. Un astfel de protocol necesită coordonare şi deci va
introduce întârzieri. Proiectanţii sistemului au observat, că ar fi suficient ca
doar interlocutorul curent să fie afişat. Din acestă cauză a fost introdus un
detector de voce (P5) la fiecare staţie. Dacă se detectează activitate la o
staţie X, atunci detectorul va conecta intrarea video la conexiunea C. În caz
contrar intrarea video nu este conectată la C. Deoarece această componentă
hard era destul de ieftină, creşterea numărului de utilizatori conectaţi a
implicat un preţ rezonabil al terminalelor.
În continuare s-a observat că dacă se păstrează şi imaginea ultimului
interlocutor se păstrează o continuitate vizuală mai bună, şi deci s-a propus

91
folosirea a două conexiuni virtuale mulţi la mulţi, una C pentru
interlocutorul curent şi una L pentru ultimul interlocutor ca în figura 4.29

Interlocutorul Interlocutorul anterior


Switch
ATM

2 CV mulţi-la- mulţi

Figura 4.29: Folosirea a două CV mulţi la mulţi

Exerciţii:
• Scrieţi pseudocodul părţii hardware a fiecărei staţii care actualizează
conexiunile la C şi L. Se presupune că detectorul de voce este o funcţie.
• Ce se întâmplă daca mai mulţi utilizatori vorbesc deodată? Ce
componentă hard se poate adăuga staţiei a.î. aplicaţia să afişeze ceva
rezonabil? De exemplu, nu ar fi rezonabil ca imaginile celor doi
interlocutori să fie combinate?

92
Tratarea nodurilor terminale

Algoritmii de nod terminal sunt o aplicaţie a algoritmilor de reţea, pentru


a construi implementări de protocoale rapide în nodurile terminale, mai
ales în servere. Aceştia pot fi priviţi ca tehnici de construire a serverelor
rapide, aplicate cu precădere în software. O mare parte dintre ele evită
structurile sistemelor de operare, pentru a permite transferuri de date de
mare viteză. Vom vedea cum să reducem antetele implicate de: copiere,
controlul transferului, demultiplexarea, timerele, şi alte sarcini de baza din
procesarea protocoalelor.

CAPITOLUL5

COPIEREA DATELOR

Intr-un birou, fiecare scrisoare primită este mai întâi trimisă la transport
si recepţie, unde scrisoarea este deschisă şi se decide cărui departament îi
este adresată şi se face o fotocopie pentru îndosariere.Este apoi înmânată
departamentului de securitate care verifică fiecare linie a scrisorii căutând
semne de spionaj industrial. Departamentul de securitate face si el o
fotocopie a scrisorii, pentru o eventuală utilizare ulterioară. Situaţia pare
ridicolă, dar majoritatea serverelor Web şi calculatoarele în general, fac un
număr de copii suplimentare ale mesajelor recepţionate şi transmise.Spre
deosebire de fotocopii, care consumă doar o mică cantitate de hârtie,
energie şi timp, copierea suplimentară în calculator consumă două resurse
preţioase: lăţimea de bandă de memorie şi memoria în sine. În cele din
urmă, dacă sunt k copii implicate în procesarea unui mesaj într-un server
Web, ieşirea serverului Web poate fi de k ori mai înceată.
Deci se trateaza aici eliminarea pierderilor (P1) generate de copiile
inutile. O copie este inutilă dacă nu este impusă de hardware. De exemplu,
hardware-ul cere copierea, biţilor recepţionaţi de un adaptor, în memoria
calculatorului. Totuşi, acesta nu este un motiv esenţial (în afara de cele
impuse de structurarea sistemelor de operare convenţionale) pentru
copierea între bufferul aplicaţie şi bufferul sistemului de operare.
Eliminarea copiilor redundante permite software-ului să se apropie de
capacitatea hardware-ului, unul din ţelurile algoritmilor de reţea.
Sunt expuse pe scurt alte operaţii (ca sumele de control şi criptarea) ce se
referă la toate datele din pachet şi alte tehnici pentru a unifica conditiile
impuse de software-ul protocolului si cele impuse de hardware, ca lăţimea
de bandă a magistralelor şi memoriei cache.Sunt revăzute sistemele de
operare mai reprezentative şi arhitecturile relevante, precum şi tehnicile de
93
reducere a costului manipulării datelor, păstrând modularitatea şi fără
schimbări majore în proiectarea sistemului de operare. În & 5.1. se
arată de ce/cum apar copii în plus ale datelor, &5.2 descrie o serie de
tehnici de a evita copiile prin restructurări locale ale sistemului de operare
şi ale codului reţelei la un nod final (endnode). In &5.3 se vede cum să se
evite copierea şi suprasarcinile din cazul transferurilor mari, utilizând
DMA la distanţă, care implică modificări de protocol. In $5.4 se extinde
problema pentru sistemul de fişiere dintr-un server Web şi se arată cum să
se evite copiile inutile dintre memoria cache pentru fişiere şi aplicaţie. In
&5.5. sunt discutate operaţii care includ toate datele (ca suma de control şi
criptarea) şi se introduce tehnica procesării integrate pe niveluri (integrated
layer processing).În &5.6 se extinde discutia dincolo de copiere, şi se arată
că fără analiza atentă a efectelor memoriei cache, efectele instrucţiunilor
cache pot eclipsa efectele copierii pentru mesaje mici.

Număr Principiul Utilizat în


P13 Localizarea memoriei(in adaptor) ca grad de Afterburner
libertate
P2b Copiere înceată utilizând copierea-la-scriere Mach
P11a Asocierea unei memorii cache virtuale per Solaris fbufs
P7 cale
Uniformizarea pe procese a bufferelor rapide
fbuf

P10 Pasează în pachet numele buffer-ului şi a Sisteme


deplasării RDMA

P4 Asociere de memorie virtuala pentru a evita Flash


copiile din cache şi în aplicaţii
P11a Asocierea unei memorii cache virtuale per Flash-lite
cale

P6 Noi apeluri la sistem îmbinate cu I/O Sendfile()


P1 Evitarea accesului repetat la memorie ILP
în timpul manevrării
P13 Dispunerea codului pentru a minimiza x-kernel
ratările de I cache
P13 Ordonarea pe niveluri a procesării LDRP
(grad de libertate)

Figura 5.1. Tehnici de evitare a copierii şi eficienţa cache discutate în acest capitol,
împreună cu principiile corespunzatoare

5.1. Necesitatea copierii datelor.


In figura 5.2. se arată secvenţa transferurilor de date implicate în citirea
fişierelor de date de pe disc (în cazul cel mai defavorabil) la trimiterea
segmentelor corespunzătoare prin adaptorul de reţea. Principalele
componente hardware din figura 5.2. sunt procesorul, magistrala de
memorie, magistrala de I/O, discul şi adaptorul de reţea. Principalele
componente software sunt aplicaţia de server Web şi kernel-ul. Sunt
implicate două subsisteme principale ale kernel-ului, sistemul de fişiere şi
sistemul de reţea. Pentru simplitate e dat doar un CPU (multe servere sunt
multiprocesor) în server şi se pune accent doar pe cereri cu conţinut

94
static(multe cereri sunt pentru continut dinamic fiind servite de un proces
CGI, computer-generated imagery).
Intuitiv, problema este simplă. Fişierul este citit de pe disc în buffer-ul
aplicaţiei printr-un apel la sistem, read()de exemplu. Combinaţia dintre
răspunsul HTTP şi buffer-ul aplicaţiei este trimisă apoi reţelei prin
conexiunea TCP către client printr-un apel de sistem, de exemplu write().
Codul TCP din subsistemul pentru reţea al kernel-ului împarte datele
răspunsului în segmente de câte un bit şi le trimite adaptorului de reţea
după ce adaugă fiecărui segment o sumă de control TCP.

CPU
Aplicatia server-ului Web Copia 3
write() read() Buffer-ul server-ului
Kernel Copia 2
TCP/IP Sist. de fisiere Buffer-ul socket

Buffer-ul fis. cache

Magistrala
de memorie
Copia 1

Copia 4 Disc

Magistrala I/O
Adaptor de retea

Retea
Figura 5.2. Copii redundante implicate în manipularea unei cereri GET la un server

Detaliile reale sunt mai dificile. În primul rând fişierul este citit într-o
regiune a memoriei kernelului numită file cache (copia 1). Ideea este bună
deoarece cererile succesive la un fişier des utilizat pot fi servite din
memoria principală fără operaţiile lente de I/O la disc. Fişierul este apoi
copiat de serverul Web din file cache în buffer-ul aplicaţiei (copia 2).
Deoarece bufferul aplicaţiei şi file cache sunt în zone diferite a memoriei
principale, copierea se poate face doar de CPU, care citeşte datele din
prima locaţie de memorie şi le scrie în cea de-a doua, prin magistrala de
memorie.
Serverul Web face apoi un apel write() la socketul corespunzător.
Deoarece aplicaţia îşi poate reutiliza bufferul în orice moment (sau chiar
să-l dezaloce) după apelul write(), subsistemul de reţea din kernel nu poate
să retransmită pur şi simplu din buffer-ul aplicaţiei. Software-ul TCP
poate avea nevoie să retransmită unele părţi din fişier după un anumit timp,
timp în care aplicaţia poate reutilizarea buffer-ul în alte scopuri.
Astfel, UNIX şi multe alte sisteme de operare, oferă semantica copierii.
Buffer-ul aplicaţiei specificat în apelul write() este copiat într-un buffer
socket (un alt buffer din kernel la o adresă de memorie diferită atât de file

95
cache cât şi de buffer-ul aplicaţiei) (copia 3). În final, fiecare segment este
transmis reţelei (după ce au fost adăugate antetele de nivel legătura de date
şi IP-ul) copiind datele din buffer-ul socket-ului în memoria adaptorului de
reţea (copia 4).
Înainte de transmisia la reţea software-ul TCP din kernel trebuie să
parcurgă datele pentru a calcula suma de control TCP, pe 16 biţi, pentru
fiecare segment.
Fiecare din cele 4 copii şi suma de control consumă resurse: lăţimea de
bandă a magistralei de memorie. Copierile între locaţiile de memorie
(copiile 2 şi 3) sunt mai dezavantajoase decât celelalte, deoarece necesită
un read şi un write pentru fiecare cuvânt transferat prin magistrală. Suma
de control TCP necesită doar un read pentru fiecare cuvânt şi un singur
write pentru a ataşa suma finală. Copiile 1 şi 4 pot fi la fel de costisitoare
ca şi copiile 2 şi 3 dacă procesorul face o copiere folosind o I/O
programată; totuşi dacă dispozitivele fac singure copierea (DMA), costul
este un singur read sau write per fiecare cuvânt ce trece prin magistrală.
Copiile consumă şi lăţimea de bandă a magistralei de I/O şi lăţimea de
bandă de memoriei. O memorie care furnizează un cuvânt de W biţi la
fiecare x nanosecunde are o limită fundamentală la ieşire de W/x biţi/ns.
Chiar şi dacă folosim DMA aceste copii folosesc magistrala de memorie de
7 ori pentru fiecare cuvânt al fişierului trimis de server. Astfel ieşirea
server-ului Web nu poate depăşi T/7, unde T este cea mai mică dintre
vitezele memoriei şi a magistralei de memorie.
Mai important, copiile suplimentare consumă memorie. Acelaşi fişier
(figura 5.2.) ar putea fi stocat în memoria cache pentru fişiere, buffer-ul
aplicaţiei şi buffer-ul socket. Deşi memoria este ieftină şi multă, ea are
unele limitări şi server-ele Web vor să folosească cât mai multă memorie
cache pentru fişiere ca să evite operaţiile lente de I/O cu discul. Astfel,
exitenţa a trei copii pentru un fişier poate reduce de trei ori memoria cache
de fişiere, care la rândul său poate reduce rata de bit a cache-ului şi deci
performanţa globală a server-ului.
În concluzie, copiile redundante afectează performanţa în două moduri
fundamentale disjuncte. În primul rând, prin utilizarea unei lăţimi de bandă
a magistralei şi a memoriei mai mare decât strictul necesar, server-ul Web
rulează mai încet decât viteza magistralei chiar şi când trimite documente
care sunt în memorie. În al doilea rând, utilizând mai multă memorie decât
ar trebui, server-ul Web va trebui să citească mai multe fişiere de pe disc în
loc să le citească din file cache. A fost descris doar scenariul în care este
servit un conţinut static. În realitate, testele cu SPECweb afirmă că 30%
din cereri sunt pentru conţinut dinamic. Acesta este de multe ori servit de
un proces CGI separat (altul decât aplicaţia server) care comunică acest
conţinut server-ului prin intermediul unor mecanisme de comunicare inter-
proces cum ar fi UNIX pipe care implică de multe ori încă o copie.
Ideal, aceste traversări în plus ale magistralei ar trebui eliminate. Copia 1
nu este necesară dacă datele sunt în cache. Copia 2 pare inutilă: de ce nu
pot fi trimise datele direct la reţea din locaţia de memorie file cache? Copia
3 pare inutilă. Copia 4 nu poate fi evitată.

96
5.2. Reducerea copierii prin restructurări locale
Copia 3 este copia principală făcută de aplicaţie în buffer-ul kernelului
(sau vice versa) când un mesaj este trimis/primit pe reţea. Aceasta este o
problemă fundamentală a reţelelor, independentă de problemele sistemului
de fişiere. Soluţiile generale de eliminare a copiilor I/O redundante se
bazează pe tehnicile prezentate în secţiunea &5.4. Se presupune că
protocolul este fix dar implementarea locală poate fi restructurată (cel puţin
kernel-ul). E de dorit un numar minim de restructurări pentru a echilibra
sarcinile software-ului de aplicaţie şi ale kernelului. In & 5.2.1. sunt
descrise tehnici bazate pe o exploatare mai buna a memoriei adaptorului.
&5.2.2. descrie ideea principală pentru evitarea copiilor (prin reasocierea
paginilor fizice comune) şi pericololele sale. & 5.2.3. prezintă optimizarea
reasocierii paginilor utilizând pre-procesarea şi cache-uri pentru fluxurile
I/O, tehnici ce implică însă modificarea API (interfaţa programabila de
aplicaţie). & 5.2.4. descrie o tehnică ce utilizează memorie virtuală dar nu
modifică API.

5.2.1. Exploatarea memoriei adaptorului


Ideea este folosirea unui grad de libertate (P13) stiind că, într-o
arhitectură cu memorie asociată, memoria poate fi localizată oriunde pe
magistrală. Asocierea memoriei/memory mapping presupune că procesorul
“vorbeşte” cu toate dispozitivele, ca adaptorul şi discul, prin citirea/scrierea
unei locaţii fizice, din spaţiul de memorie alocat dispozitivului.
Deşi memoria kernelului se află deobicei rezidentă pe subsistemul de
memorie, nu există nici un motiv ca o parte din ea să nu fie plasată în
adaptor, care conţine şi el memorie. Prin împartirea echitabilă a memoriei
adaptorului şi folosind acest grad de libertate putem plasa memoria kernel
în adaptor. Asa că, după ce datele au fost copiate din aplicaţie în memoria
kernel, ele se află deja în adaptor şi nu trebuie copiate din nou pentru
transmisia în reţea (fig. 5.3).
Comparând figura 5.3.cu 5.2, observăm că în figura 5.3 nu apare nici un
transfer de la disc la memorie. Copia 3 inutilă (fig. 5.2) e acum combinată
cu copia 4 care este esenţială şi formează o singură copie (figura 5.3).
Ideea principală la suma de control (&5.5), este aplicarea principiului de
partajare a efortului P2c. Când datele sunt mutate, din buffer-ul aplicaţiei
în memoria kernel rezidentă în adaptor, de către procesor (printr-o I/O
programată sau PIO, care este o operaţie de I/O sub controlul
procesorului), oricum procesorul citeşte fiecare cuvânt al pachetului.
Deoarece astfel de citiri ale magistralei sunt laborioase, procesorul ar putea
îmbina calculul sumei de control cu procesul de copiere, ţinând un registru
care să acumuleze sumele cuvintelor pe măsură ce acestea sunt transferate.
Ideea, numită metoda Witless (sau gândirea-simplă), dar expusă prima
dată de Van Jacobson, nu a fost implementată niciodată. Mai târziu Banks
şi Prudence au aplicat-o la laboratoarele Hewlett-Packard creând adaptorul
Afterburner. În abordarea afterburner procesorul nu transferă datele din
memorie în adaptor. Acest transfer este realizat de adaptor utilizând
accesul direct la memorie sau DMA. Dar pentru că procesorul nu mai
participă la procesul de copiere, adaptorul ar trebui să facă suma de
control. În acest scop, adaptorul afterburner are un hardware specializat
simplu, care face suma de control în timpul transferului DMA.
97
Ideea, deşi bună, are trei defecte. În primul rând adaptorul de reţea ar
trebui să aibă multă memorie pentru a manevra mai multe conexiuni TCP,
iar suplimentarea memoriei pate creşte preţul adaptorului prea mult. În al
doilea rând, în metoda Witless, unde CPU calculează suma, copierea
pachetului recepţionat în buffer-ul aplicaţiei în timpul calculării sumei,
poate duce la scrierea unor date eronate în buffer-ul aplicaţiei. Deşi aceasta
poate fi descoperită la sfârşit când suma de control nu corespunde, ea
cauzează dificultăţi în prevenirea citirii de date incorecte de către aplicaţii.
A treia problemă cu confirmările întârziate este studiată în execiţii.

CPU Memorie
Aplicatie
write() Buffer-ul server-ului

Kernel TCP/IP

Magistrala
de memorie

O singura copie

Magistrala I/O

Buffer socket
Adaptor de retea

Retea
Figura 5.3. Metoda Witless (afterburner) elimină nevoia copierii din kernel în adaptor
plasând buffer-ele kernelului în adaptor

5.2.2. Folosirea copierii- la- scriere


La metoda Witless ideea de bază este eliminarea copiei kernel-adaptor,
dar în următoarele trei paragrafe ideea este eliminarea acestei copii prin
reasocierea memoriei virtuale. Unul din motivele copierii separate era
posibilitatea ca aplicaţia să vrea să modifice buffer-ul încălcând astfel
semantica TCP. Al doilea motiv e că aplicaţia şi kernel-ul utilizează spaţii
diferite de adrese virtuale.
Unele sisteme de operare (Mach) oferă o facilitate numită copiere-la-
scriere, COW(copy-on-write) care permite unui proces să duplice o pagină
virtuală în memorie la un cost scăzut. Copia trebuie să indice pagina fizică
P originală din care a fost copiată. Aceasta implică doar actualizarea unori
descriptori (cuvinte de memorie) şi nu copierea întregului pachet (1500
bytes de date). Remarcabil la COW este că, dacă proprietarul datelor
originale modifică datele, sistemul de operare detectează această condiţie
automat şi generează două copii fizice separate P şi P’. Proprietarul
original pointează acum către P şi poate face modificări asupra lui P;
98
proprietarul paginii copiate pointează către vechea copie P’. Treaba merge
bine dacă majoritatea paginilor rămân nemodificate (sau se modifică doar
câteva pagini) de către proprietarul original.
Astfel, în sistemul cu copiere-la-scriere, aplicaţia ar putea face o copie
COW pentru kernel. În cazurile rare în care aplicaţia îşi modifică buffer-ul,
kernel-ul face o copie fizică (scumpă), dar asta nu se prea întâmplă.
Evident, se utilizează o evaluare înceată (P2b) pentru minimizarea
încărcării pentru cazurile cele mai probabile (P11). În figura 5.4. suma de
control poate fi returnată fie într-o copie spre/dinspre memoria adaptorului,
fie folosind circuite specializate pentru calculul CRC, aflate în adaptor.
Din păcate, multe sisteme de operare, precum UNIX(*) şi Windows nu
oferă copiere-la-scriere. Totuşi, rezultate asemănătoare se pot obtine
sesizand ideea care sta la baza COW si anume utilizarea memoriei
virtuale.
(*) Ssistemul V-UNIX foloseşte COW când apare un fork; paginile partajate de
procesele părinte şi fiu sunt partajate folosind poziţionarea unui bit.

Implementarea serviciului copiere-la-scriere


Majoritatea calculatoarelor moderne utilizează memoria virtuală.
Programatorul lucrează cu abstractizarea unei memorii infinite, adică un
şir liniar în care el (de fapt compilatorul) asignează locaţii variabile; de
exemplu, locaţia X va fi locaţia 1010 în acest şir imaginar sau virtual.
Aceste adrese virtuale sunt apoi asociate cu memoria fizică (rezidentă pe
disc sau în memoria principală) utilizând un tabel de pagini. Pentru orice
adresă virtuală biţii de ordin superior (20 în exemplu) dau numărul paginii
şi biţii de ordin inferior (12 în exemplu) dau poziţia în pagină. Memoria
principală este divizată şi ea în pagini fizice astfel încât fiecare grup de 212
cuvinte de memorie formează o pagină fizică. Asocierea adresă virtuală-
adresă fizică se face asociind pagina virtuală-pagina fizică, printr-o căutare
în tabelul de pagini indexat cu numărul paginii virtuale corespunzătoare.
Dacă pagina dorită nu este rezidentă în memorie, hardware-ul generează o
excepţie, care face ca sistemul de operare să citească pagina de pe disc în
în memoria principală.
Supraîncărcarea apărută prin citirea tabelelor de pagini din memorie
poate fi deobicei evitată folosind TLB(translation look-aside buffer) (*),
care este un cache rezident în procesor.
(*)TLB este o tabelă folosită în sistemele cu o memorie virtuală, care conţine lista cu
numerele paginilor fizice asociate numărului fiecărei pagini virtuale. TLB este folosit
împreună cu o memorie cache având indicii bazaţi pe adresele virtuale. Adresa virtuală
este simultan prezentă la TLB şi la cache, astfel că accessul la cache şi translatarea adresă
virtuale-adresă fizică se face în paralel. Dacă adresa cerută nu se află în cache atunci data
se încarcă într-o adresă fizică din memoria principală. Tabela cu translaţii ar putea fi
plasată şi între cache şi memoria principală, ca să poată fi activate simultan când adresa nu
se află în cache.
Schema de copiere-la-scriere se bazează pe memoria virtuală.
Presupunem că pagina virtuală X pointează la o pagină P rezidentă în
memoria fizică, şi că sistemul de operare doreşte să duplice conţinutul lui X
într-o nouă pagină virtuală, Y. Calea mai grea pentru a face aceasta ar fi
alocarea unei noi pagini fizice P’, copierea conţinutului lui P în P’ şi apoi
asociind pe Y cu P’ în tabelul de pagini. Calea mai simplă concretizată în
copiere-la-scriere este asocierea noii pagini virtuale Y tot cu vechea pagină
fizică P, modificând intrarea din tabelul de pagini. Deoarece majoritatea
99
sistemelor de operare moderne utilizează pagini lungi, modificarea unei
intrări în tabelul de pagini este mai eficientă decât copierea dintr-o pagină
fizică în alta.
În plus, kernel-ul setează şi un bit de protecţie COW ca parte a intrării, în
tabelul de pagini, pentru pagina virtuală originală X. Dacă aplicaţia
încearcă să scrie în pagina X hardware-ul va accesa tabelul de pagini pentru
X, va observa bitul de protecţie setat şi va genera o excepţie, care apelează
sistemul de operare. În acest moment sistemul de operare va copia pagina
fizică P într-o altă locaţie P’, şi va face ca X să pointeze spre P’, după ce a
modificat bitul COW. Y continuă să indice către vechea pagină fizică. Desi
fiecare bit e la fel de costisitor ca şi copierea unei pagini fizice, ideea e ca
acest cost apare doar în cazurile când aplicaţia scrie într-o pagină COW.

Memorie
CPU
Server +buffer socket
Aplicatie Copiere la o
write() pagina libera
Kernel TCP/IP doar daca
aplicatia scrie

Magistrala de memorie

O singura copie (cu suma


de control calculata prin
hard/soft)

Magistrala I/O

Buffer socket
Adaptor
de retea

Retea
Figura 5.4. Utilizarea copierii-la-scriere

Modul de funcţionare al COW oferă o şansă şi acelor sisteme de operare,


ca UNIX sau Windows, care deşi nu au COW au memorie virtuală.
Memoria virtuală prezintă o indirectivitate care, pentru a evita copierea
fizică, poate fi exploatată prin modificarea intrărilor tabelului de pagini.
Trebuie găsită o altă soluţie, decât la COW, de protejare la tentativele de
scriere ale aplicaţiei.

100
Pachet
date

Procesul 1 Scrie Procesul 2


Tabel pagini Tabel pagini
VP 10 VP 8

Figura 5.5. Operaţiile de bază implicate în copierea unei pagini utilizând


memoria virtuală

5.2.3. Fbufs: optimizarea reasocierii paginilor


Chiar dacă ignorăm aspectul protecţiei împotriva scrierilor aplicaţiei,
figura 5.5. implică faptul că un buffer mare poate fi transferat din aplicaţie
în kernel (sau vice versa) cu o scriere în tabelul de pagini. Această viziune
simplistă a remapării paginilor este cam naivă şi greşită.
Figura 5.5. arată un exemplu concret al remapării paginilor. Presupunem
că sistemul de operare doreşte să facă o copie rapidă a datelor procesului 1
(aplicaţia) din pagina virtuală (VP10) într-o pagină virtuală (VP 8) din
tabelul de pagini a procesului 2 (kernel-ul).Aparent, ar fi necesară doar
modificarea intrării corespunzătoare lui VP 8 (din tabelul de pagini a
procesului 2), să indice spre pachetul de date la care deja indică pagina
virtuală 10 (din tabelul de pagini a procesului 1). Apar câteva încărcări
suplimentare, nespecificate încă:
• Tabele de pagini multinivel: majoritatea sistemelor moderne folosesc
asocieri pe mai multe niveluri ale tabelelor de pagini, deoarece este nevoie
de prea multă memorie pentru asocierea tabelelor de pagini cu 20 biţi de
pagină virtuală. Astfel, maparea reală poate cere modificări ale mapărilor
în cel puţin primul şi al doilea nivel al tabelului de pagini pentru
portabilitate. Există deasemenea atât tabele independente de calculator cât
si dependente de calculator. Astfel, sunt implicate câteva scrieri, nu doar
una.
• Obţinerea blocărilor şi modificarea intrărilor tabelului de pagini:
tabelele de pagini sunt resurse comune şi deci trebuie protejate folosind
blocări care trebuiesc obţinute şi eliberate.
• Folosirea TLB-urilor: pentru a economisi timpul de translaţie,
asocierile pentru tabelele de pagini utilizate mai des sunt memorate în
TLB. Când este scrisă o nouă locaţie de pagină virtuală pentru VP 8, orice
intrare TLB pentru VP 8 trebuie găsită şi eliminată sau corectată.
• Alocarea VM în domeniul destinaţie: deşi s-a presupus că paginii
destinaţie i s-a alocat locaţia 8 din memoria virtuală, trebuie făcute, înainte
să aibă loc copierea, calcule de găsire a unei intrări libere în tabelul de
pagini din procesul destinaţie.
• Blocarea paginilor corespondente: anumite pagini fizice de pe disc pot
fi înlocuite, ca sa faca loc altor pagini virtuale. Pentru a preveni înlocuirea,
paginile trebuie să fie blocate, ceea ce reprezintă un cost suplimentar.
Toate aceste suprasarcini cresc foarte mult în sistemele multiprocesor.
Rezultatul net este că, deşi asocierea tabelelor de pagini pare foarte bună
101
(asocierea pare că dureaza un timp constant, independent de dimensiunea
pachetelor de date), factorii constanţi (Q4) generează o suprasarcină mare.
Această supraîncarcare, sesizată deja la începutul anilor ’90 (Druschel şi
Peterson) a crescut permanent de atunci. Ei au propus o facilitate a
sistemului de operare numită fbufs (fast buffers), ce elimină majoritatea
celor 4 surse de supraîncărcare în cazul asocierii paginilor.
Fbufs
Ideea principală în fbufs este că: dacă o aplicaţie trimite prin kernel mai
multe pachete de date reţelei, atunci probabil ca un buffer va fi reutilizat
de mai multe ori, şi astfel sistemul de operare poate precalcula în avans
(P2a) toată informaţia de asociere a paginii pentru buffer, evitând astfel
mare parte din suprasarcina asocierii paginii din timpul transferului de
date. Sau, asocierile pot fi calculate mai încet (P2b) când este pornit
transferul de date pentru prima oară (generând o suprasarcină pentru
primele câteva pachete) dar care poate fi memorată (P11a) pentru
pachetele următoare, eliminând supraîncărcarea pentru cazurile obişnuite.
Cea mai simplă cale de implementare a acestui mod de lucru este prin
folosirea memoriei partajate. Se asociază simultan un număr de pagini
P1,...,Pn în tabelele de memorie virtuală ale kernel-ului şi simultan se
transmit aplicaţiile A1,...,Ak. Deşi ideea e tentanta, s-ar putea ca aplicaţia
A1 să trebuiască să citească pachetele trimise de aplicaţia A2. S-ar încălca
astfel securitatea şi separarea greşelilor.
Mai sigură ar fi rezervarea (sau stabilirea lentă) paginilor partajate
mapate pentru fiecare transfer aplicaţie-kernel, şi vice versa. De exemplu,
ar putea fi un set de buffer-e (pagini) pentru FTP, unul pentru HTTP, şi
aşa mai departe. Mai general, unele sisteme de operare definesc multiple
subsisteme de securitate, pe lângă kernel şi aplicaţie. Proiectanţii fbufs-
urilor numesc cale o secvenţă de domenii de securitate. In exemplul
anterior, e suficient să gândim o cale kernel-aplicaţie si alta aplicaţie-
kernel (FTP-kernel, kernel-HTTP). Căile sunt unidirecţionale, deci
aplicaţia are nevoie simultan de două căi, câte una în fiecare direcţie.
Figura 5.6. arată un exemplu mai complex de căi, unde software-ul
Ethernet este implementat ca un driver la nivelul kernelului, stiva TCP/IP
este implementată ca un domeniu de securitate la nivelul utilizatorului, iar
aplicaţia Web este implementată la nivelul aplicaţie. Fiecare domeniu de
securitate are propriul set de tabele de pagini. Căile receptoare sunt
Ethernet, TCP/IP, Web şi Ethernet, OSI, FTP.
Pentru a implementa ideea fbuf, sistemul de operare ar putea lua câteva
numere din paginile fizice P1,...Pk şi să le premapeze în tabelul de pagini
al driver-ului Ethernet, codului TCP/IP şi aplicaţiei Web. Aceeaşi operaţie
ar putea fi realizată cu un set de pagini fizice diferite pentru Ethernet, OSI
şi FTP. Astfel, folosim P2a pentru a precalcula mapările. Rezervarea
paginilor fizice pentru fiecare cale poate fi o risipă serioasă, deoarece
traficul este în rafale; o idee mai bună este P2b, maparea lentă doar când o
cale devine ocupată.
P2b evită supraîncărcarea cauzată de: actualizarea multinivel a tabelelor
de pagini, obţinerea blocărilor, eliminarea TLBurilor şi alocarea memoriei
virtuale de destinaţie după sosirea si expedierea primelor câteva pachete.
Această treabă se face odată, la începutul transferului. Pentru a face fbuf
funcţional, este crucial ca la sosirea unui pachet, driver-ul de nivel minim
102
(sau chiar adaptorul însuşi) să fie capabil să realizeze rapid calea completă
pe care pachetul va fi mapat (demultiplexare anticipată). Intuitiv, (fig. 5.6)
aceasta este realizată examinând toate antetele pachetelor pentru a
determina (pe moment) dacă un pachet, cu un antet Ethernet, IP şi HTTP
aparţine căii 1.

Web FTP

(Domeniu 2)
Calea 1 Calea 2
IP OSI ?

(Domeniu 1)
(Domeniu 0) Calea 1 Buffer cache
Ethernet
Calea 2 Buffer cache

Figura 5.6. Preasocierea paginilor buffer in tabele de pagini ale fiecarui domeniu
intr-o cale evita costul remaparii paginii in calea de timp real dupa setarea initiala

Driver-ul/adaptorul va avea atunci o listă cu buffer-ele libere pentru acea


cale, care vor fi utilizate de adaptor pentru a scrie pachetele în ele; când
adaptorul termină, va transmite descriptorul buffer-ului următoarei
aplicaţii de pe cale. Un descriptor de buffer este doar un pointer spre o
pagină partajată, şi nu pagina în sine. Când ultima aplicaţie din cale
termină cu pagina, o retrimite primei aplicaţie din cale, unde devine din
nou un buffer liber, ş.a.m. d.
În acest punct, se pune problema de ce căile sunt unidirecţionale. Căile
sunt făcute unidirecţionale deoarece primul proces de pe fiecare cale este
presupus a fi scriitor şi procesele rămase sunt presupuse a fi cititoare.
Acest lucru poate fi impus în timpul premapării prin setarea unui bit de
permisiune-scriere pentru prima aplicaţie în intrarea tabelului său de
pagini, şi un bit doar-citire în intrările tabelelor de pagini ale celorlalte
aplicaţii. Apare asimetrie între sensuri şi necesitatea căilor unidirecţionale.
Dar se asigură astfel un anumit grad de protecţie. In figura 5.7. sunt doar
două domenii pe o cale. De observat că scriitorul scrie pachetele în nişte
buffere (descrise de o coadă de fbufs libere) şi apoi pune descriptorul scris
într-o coadă de fbufs scrise, care vor fi citite de următoarea aplicaţie (în
figura 5.7. este arătat doar unul).
Mai departe, este posibil ca pagina 8, premapată unei căi în prima
aplicaţie, să fie asociată paginii 10 în a doua aplicaţie. Lucrul e neplăcut,
deoarece când a doua aplicaţie citeşte un descriptor pentru pagina 8
trebuie să ştie cumva că el corespunde cu propria sa pagină virtuală 10.
Dar proiectanţii au evitat a generalizările inutile (P7) şi au insistat ca
fbuf-ul să fie mapat în aceeaşi pagină virtuală în toate aplicaţiile de pe
cale. Aceasta se poate face prin rezervarea unui număr iniţial de pagini în
memoria virtuală a tuturor proceselor, pentru a fi pagini fbuf.
103
Mai există câteva probleme dificile. Pentru a obţine protecţie s-a
permis un singur scriitor şi mai mulţi cititori. Dar asta înseamnă că
paginile sunt fixe; doar scriitorul le poate atinge. Dar, cum rămâne cu
adăugarea antetelor în stivă ? Soluţia problemei este arătată în figura 5.8.
în care un pachet, este de fapt o structură de date agregată, cu pointeri spre
fbuf-uri individuale astfel ca să poată fi adăugate antetele, prin adăugarea
unui buffer obişnuit sau a unui fbuf la structura agregată.

Pachet
date
Scriitor Cititor
Proces 1 Proces 2
Tabel de pagini Tabel de pagini
Scris
VP 10 W odata R VP 10
(prealocat)
initial

Fbufs scriitoare

Fbufs libere

Figura 5.7. Optimizarea cu un singur scriitor

Structura agregata

……………… …………….

Figura 5.8.Folosirea obiectelor agregate pentru a permite suplimentarea


nivelurilor pentru adăugarea de antete, când e permis doar un scriitor

Nu e aşa de complicat cum pare, deoarece mbufs-urile utilizate de obicei


în UNIX sunt deasemenea compuse din buffer-e legate împreună. Mai
precis acolo legarea se face într-o topologie liniară, în timp ce bufferele
agregate au o topologie arbore mai generală, dar câştigul de performanţă,
datorat înlănţuirii şi indexării, este asemănător.
Până acum schema cu fbuf-uri a utilizat ideile de mapare VM de bază
din figura 5.4, dar le-a făcut mai eficiente ( prin amortizarea costurilor
mapării pe un număr mare de transferuri de pachete). Deobicei sunt
104
eliminate actualizările tabelelor de pagini, lucru făcut în sistemele de
operare obişnuite. De fapt, Thadani şi Khalidi au extins ideea fbufs şi au
implementat-o în sistemul de operare al lui Sun Solaris. Dar cum se
păstrează semanticile copiilor standard? Ce se întâmplă dacă aplicaţia face
o scriere? Un sistem de operare standard ca UNIX nu poate depinde de
copierea-la-scriere (figura 5.4).
Răspunsul este că nu se păstrează semanticile copiilor standard. API este
schimbat. Aplicaţiile care scriu trebuie să fie atente să nu scrie într-un fbuf
când acesta a fost alocat kernel-ului până când fbuf-ul este returnat de
kernel listei de buffere goale. Pentru a proteja împotriva unui cod ce
contine erori sau este rău intenţionat, kernel-ul poate activa bitul de scriere
când un fbuf este transferat de la aplicaţie la kernel; bitul este setat din nou
când fbuf-ul este returnat. Dacă aplicaţia face o scriere fara să aibă
permisiunea de scriere, este generată o excepţie şi aplicaţia eşuează, lăsând
celelalte procese neafectate.
Deoarece activarea biţilor de scriere necesită o activitate suplimentară pe
care fbufs încearcă să o evite, facilitatea fbuf permite şi o altă formă de
fbufs numită volatilă. De observat că, dacă procesul care scrie este o
entitate de încredere (cum ar fi kernel-ul), nu are rost să fie impusă
protecţia la scriere. Dacă kernel-ul are o eroare (bug), care îl face să
realizeze scrieri neaşteptate, sistemulse va defecta oricum.
Schimbarea API-ului în acest fel sună dramatic. Ar trebuie rescrise toate
aplicaţiile software ale UNIX–ului? Pentru a evita aceasta, există soluţii.
API-ul existent poate fi completat cu noi apeluri de sistem. Extensiile
Solaris adaugă un apel uf_write() în plus faţă de apelul standard write().
Aplicaţiile performante pot fi rescrise utilizând aceste noi apeluri.
Apoi, pot fi folosite extensiile în implementarea substraturilor I/O
comune (ca biblioteca UNIX stdio) care sunt părţi ale mai multor aplicaţii.
Aplicaţiile legate de această bibliotecă nu trebuie să fie schimbate, dar le
creste totuşi performanţa.
Problema nu este modificarea API-ului, ci cât de greu este să modificăm
aplicaţiile pentru a beneficia de schimbările din API. Thadani şi Khalidi,
respectiv Pai au prezentat câteva aplicaţii la care modificările, necesare ca
respectivele aplicaţii să folosească un API cu fbuf, au fost mici si locale.

5.2.4. Emularea transparentă a semanticilor copiilor


Pentru noul API cu fbuf aplicaţia poate fi modificată pentru a-şi creşte
performanţa. Deşi modificările pot fi simple şi locale, se modifică drastic
conceptia programatorului despre buffer. În API-UNIX standard, aplicaţia
asignează adresele de buffer-e; în fbufs buffer-ele sunt asignate de kernel,
din spaţiul de adrese fbuf. În API-UNIX standard, programatorul poate
proiecta buffer-ul cum doreste, inclusiv folosirea buffer-elor succesive. În
fbufs datele recepţionate de la reţea pot fi dispersate în părţi, legate între
ele prin buffer-ul agregat, şi programatorul aplicaţiei trebuie să lucreze cu
noul model de buffer ales de kernel. Beneficiile fbufs pot fi obţinute fără
modificarea API-UNIX ? Teoretic software-ul aplicaţiei va rula
neschimbat, şi s-ar putea obţine performanţe fără rescrierea aplicaţiilor.
Un mecanism inteligent de copiere-la-scriere rapidă este TCOW
(transient copy-on-write). Teoretic se păstreaza API-ul neschimbat si
aplicaţiile vor avea performanţe mai bune, dar propunerea nu e verificată
105
experimental. In practică, probabil că aplicaţiile vor trebui modificate, mai
mult intuitiv, pentru a profita de schimbările fundamentale ale
implementării kernel-ului.
API standard trebuie să permită oricând unei aplicaţii să scrie sau să
dezaloce un buffer pasat kernel-ului. Proiectarea fbuf modifică API
interzicând unei aplicaţii să facă asta. Dar, pentru a păstra API cand se fac
doar mapări de memorie virtuală, sistemul de operare trebuie să lucreze cu
cele două ameninţări posibile (scrierea/dezalocarea aplicaţiei), timp în care
buffer-ul este utilizat de kernel pentru a trimite/retransmite un pachet. În
sistemul Genie maparea VM e utilizată la fel ca în fbufs, iar cele două
ameninţări sunt tratate după cum urmează.
Contorizarea ameninţărilor de scriere prin modificarea managerului de
erori VM: când o aplicaţie face mai întâi o scriere, buffer-ul este marcat în
mod special, ca Read-Only. Astfel, dacă aplicaţia face o scriere, este
invocat managerul de erori VM. În mod normal, acest lucru ar trebui să
genereze o excepţie dar, dacă sistemul de operare păstrează semanticile
copiilor, nu ar trebui să fie o eroare. Genie modifică handlerul de excepţii
astfel. În primul rând pentru fiecare pagină/buffer Genie urmăreşte dacă
există transmisii de rezolvat (transmisii spre reţea) utilizând un numarător,
incrementat când începe transmisia şi decrementat când transmisia este
completă. În al doilea rând, handlerul de erori e modificat ca sa facă o
copie separată a paginii pentru aplicaţie (care conţine noul Write) dacă
există un Send nerezolvat. Desigur, performanţa este afectată, dar se
păstrează semanticile copiilor standard ale API, ca în UNIX. Această
tehnică numită protecţie tranzitorie la COW e folosită doar la nevoie –
când buffer-ul este citit si de subsistemul de reţea.
Contorizarea ameninţărilor de de-alocare prin modificarea Pageout
Daemon: într-un sistem standard de memorie virtuală, există un proces
responsabil pentru punerea paginilor dezalocate într-o listă liberă, din care
paginile pot fi scrise pe disc. Acest Pageout Daemon poate fi modificat sa
nu dezaloce o pagină, când e folosită pentru a trimite/recepţiona pachete.
Aceste două idei sunt exemple ale principiului P3c, deplasarea
calculelor în spaţiu. Acţiunea de verificare a scrierilor neaşteptate este
pasată handlerului de erori VM, şi acţiunea de dezalocare este pasată
rutinei de dezalocare a paginii. Cele două idei sunt suficiente pentru a
trimite un pachet dar nu şi pentru recepţie. La recepţie, Genie trebuie să se
bazeze, la fel ca fbufs, pe suportul hardware din adaptor, ca să despartă
antetele pachetelor într-un buffer, şi datele rămase într-alt buffer de
dimensiunea unei pagini, care poate fi schimbat cu buffer-ul aplicaţiei.
Pentru ca această acţiune să nu implice şi o copiere fizică, buffer-ul de
date al kernel-ului trebuie să înceapă la acelaşi offset din pagină ca şi
buffer-ul de recepţie al aplicaţiei. Pentru un buffer mare, probabil că ar fi
mai eficient ca pentru prima şi ultima pagină (care pot fi parţial
completate) să fie făcută o copie fizică; totuşi, paginile intermediare care
sunt pline pot fi simplu schimbate de la kernel la aplicaţie printr-o mapare
corectă a tabelelor de pagini. Există şi optimizarea numită copiere reversă.
Datorită complexităţii remapării tabelelor de pagini, nu e clar cum se
face eficient remaparea paginilor în Genie. O posibilitate este că Genie
foloseste aceeaşi idee fbuf a memorării mapărilor VM bazată pe căi,
pentru a evita supraîncărcarea prin eliminarea TLB, când avem mai multe
106
tabele de pagini, etc. Experimentele Genie au fost făcute pe reţele ATM,
unde cu identificatorul căii virtuale remaparea căii se face rapid.
Cum poate TCOW să beneficieze de implementarile anterioare? Nu
există confirmări experimentale ale acestei idei, deoarece experimentele
au folosit un simplu banc de test de copiere şi nu o aplicaţie cum ar fi un
server Web. Pare dificil pentru o aplicaţie existentă să beneficieze de noua
implementare a kernelului cu API-urile existente.
Considerăm o aplicaţie ce rulează TCP, care-i dă un buffer TCP-ului.
Deoarece nu există o reacţie spre aplicaţie (ca la fbufs) aplicaţia nu ştie
când poate reutiliza buffer-ul în siguranţă. Dacă aplicaţia rescrie prea
devreme buffer-ul pe care TCP îl ţinea pentru retransmisie, atunci nu este
compromisă siguranţa, ci doar performanţa, datorită copiei fizice implicată
în copierea-la-scriere. E puţin probabil ca o aplicaţie nemodificată să-şi
poată sincroniza momentele modificării buffer-elor cu momentele
transmisiilor TCP, şi ar trebui să-şi poată alinia buffer-ele sale destul de
bine pentru a permite un schimb de pagini corect.
Astfel, aplicaţiile trebuie modificate pentru a beneficia de sistemul
Genie. Chiar şi atunci, ar trebui să ştie când să reutilizeze un buffer,
deoarece lipseşte reacţia. Aplicaţia ar putea monitoriza erorile TCOW şi să
îşi modifice, funcţie de acestea, modelul de reutilizare. Dar dacă aplicaţiile
trebuie modificate subtil, pentru a beneficia de noul kernel, nu e clar ce
beneficiu au obţinut din păstrarea API-ului.

5.3. Evitarea copierii utilizând DMA la distantă (RDMA)


Fbufs oferă o soluţie rezonabilă pentru evitarea copiilor redundante
aplicaţie-kernel, dar există o soluţie mai directă de eliminare a
suprasarcinii cauzata de control. Dacă un fişier de 1MB este transferat
între două staţii de lucru pe Ethernet, e divizat în părţi de 1500-byte. CPU
tratează fiecare parte de 1500-byte pentru a face procesarea TCP şi
copierea fiecărui pachet (posibil printr-o interfaţă zero-copii cum ar fi
fbufs) în memoria aplicaţiei.
Dar CPU poate face un DMA între disc şi memorie pentru a transfera să
zicem 1MB. CPU setează DMA, spune discului zona de adrese în care
datele trebuiesc scrise şi îşi vede de treburile sale. După transfer, discul
întrerupe CPU pentru a-i spune că a terminat. Procesorul nu controlează
în detaliu transferul, ca in cazul anterior al transferului în reţea.
Această analogie sugerează execuţia unui DMA prin reţea, RDMA
(Remote-DMA). Această soluţie de reţea a fost propusă în VAX Clusters,
de proiectantii de calculatoare. Intenţia este ca datele să fie transferate prin
reţea între memoriile celor două calculatoare, fără intervenţia celor două
procesoare la fiecare pachet. Cele două adaptoare cooperează la citirea
dintr-o memorie şi scrierea în alta. La DMA prin reţea trebuie rezolvate
două probleme: (1) cum ştie adaptorul receptor unde să plaseze datele – el
nu poate cere ajutor calculatorului fără a compromite ideea; (2) cum este
menţinută securitatea. Nu e discutata posibilitatea pachetelor solitare care
sosesc de pe reţea şi rescriu părţi cheie de memorie.

5.3.1.Evitarea copierii în cluster


În ultimii ani, clusterele de staţii de lucru au fost acceptate ca
înlocuitoare mai ieftine şi mult mai utile decat calculatoarele mari. Multe
107
servere Web sunt compuse dintr-o reuniune de servere. Deşi tehnologia
pare recentă, DEC (Digital Equipment Corporation) a introdus acum 20 de
ani un produs comercial de succes numit VAX Clusters, pentru a asigura
baza unor operaţii scalabile (aplicaţii cu baze de date). Inima sistemului a
fost o reţea de 140-Mbit numită CI (Computer Interconnect) având un
protocol asemănător cu Ethernet. Clienţii puteau conecta la CI, un număr
de calculatoare VAX şi discuri ataşate reţelei. Copierea eficientă a fost
cauzată de necesitatea transferului unor mari cantităţi de date între discul
îndepărtat şi memoria VAX-lui. Asa a aparut RDMA.
RDMA impune ca pachetele de date, conţinând părţi dintr-un fişier
mare, să ajungă la destinaţia finală odata sosite în adaptorul destinaţiei.
Lucrul este mai greu de facut decât pare. În reţelele tradiţionale, pachetul
sosit obligă CPU să-l examineze si sa-i decida destinatia. Chiar dacă CPU
se uită la antete, el poate specifica doar coada bufferelor de recepţie (bazat
pe aplicaţia destinatie).
Presupunem că aplicaţia de recepţie memoreaza in coada aplicaţiei 1 de
la adaptorul de recepţie, paginile 1, 2 şi 3. Primul pachet soseşte şi este
trimis la pagina 1, al treilea pachet soseşte în afara secvenţei şi este pus în
pagina 2, în loc de pagina 3. CPU poate întotdeauna remapa paginile la
sfârşit, dar remaparea tuturor paginilor la sfârşitul transferului, pentru un
fişier mare, poate fi dificilă. Datorită pierderii de pachete, sosirile în afara
secvenţei pot apărea întotdeauna, chiar şi într-o legătură FIFO.
La VAX-Clusters ideea este însă este ca mai întâi aplicaţia destinaţie să
blocheze un număr de pagini fizice (paginile 11 şi 16 din figura 5.9.)
pentru memoria destinaţie de transfer a fişierului. Logic ar fi ca bufferul, B
să zicem, să aibă pagini consecutive (paginile 1 şi 2 din figura 5.9.).
Numele acestui buffer B este pasat la aplicaţia de transmisie. Sursa
pasează acum (P10 pasarea informaţiei în antetele de protocol) numele
buffer-ului şi decalajul cu fiecare pachet trimis. Astfel, când trimite
pachetul 3 în afara secvenţei din ultimul exemplu, acesta va conţine B şi
pagina 3 şi deci, nu poate fi memorat în pagina 2 a buffer-ului chiar dacă
soseşte înaintea pachetului 2. Astfel, după ce toate pachetele sosesc, nu
mai este nevoie de o remapare a paginilor.
Pentru ca procesorul să nu mai fie ocupat la fiecare sosire de pachet
luate nişte măsuri. În primul rând adaptorul trebuie să implementeze
protocolul de transport (şi să verifice toate duplicatele,etc.) ca în
procesarea TCP. În plus, adaptorul trebuie să fie capabil să determine unde
se termină antetul şi unde încep datele, astfel ca în buffer-ul destinaţie să
fie copiate doar datele.
În sfârşit, să permitem fiecărui pachet, care transportă ID–ul buffer-ului
de la reţea, să fie scris direct în memorie, ar putea fi o lacună în securitate.
Pentru a evita acest lucru, ID-ul buffer-ului conţine un şir aleator, greu de
ghicit.Astfel, clusterele VAX sunt utilizate doar între gazdele de încredere
din cluster, ceea ce evident că ar fi mai difícil pentru transferul de date la
scala Internet-ului.

108
Numele buffer-ului B

Pagina 11

B, 1

B1
B2 Pagina 16
B, 2
ADAPTORUL
DESTINATIE

Figura 5.9 Realizarea DMA prin reţea

5.3.2. RDMA actual.


Clusterele VAX au introdus foarte devreme ceea ce se numeşte reţea cu
zonă de stocare SAN (storage area network). SAN sunt reţele “în culise”
(back-end) care conectează mai multe calculatoare care partajează
memoria, adică discurile ataşate reţelei. Câţiva succesori recenţi ai
clusterelor VAX care folosesc tehnologia SAN sunt: canalul pe fibra
optică (Fiber Channel) mai vechi, sau mai modernele InfiniBand şi
iSCSI(Small Computer System Interface)

Fiber Channel
În 1988 ANSI Task Group X3T11 a început să lucreze la un standard
numit FiberChannel. Unul din scopurile FiberChannel era să ia standardul
SCSI dintre o staţie de lucru şi un disc local şi să îl extindă pe distanţe
mari. Astfel, în multe instalaţii FiberChannel, SCSI este încă utilizat ca
protocolul care rulează pe FiberChannel.
FiberChannel merge la fundamentele reţelei mai departe decât clusterele
VAX, utilizând tehnologii noi de reţele, cum ar fi legăturile punct-la-punct
pe fibră conectate cu switch-uri. Aceasta permite viteze de până la
1Gb/sec şi distanţe mai mari decât în reţeaua clusterelor VAX. Switch-
urile pot fi chiar conectate la distanţă, permiţând unei firme de comerţ să
aibă pe un site îndepărtat o memorie de rezervă a tuturor tranzacţiilor.
Utilizarea switch-urilor trebuie să aibă în vedere controlul fluxului, care
trebuie făcut cu grijă pentru a evita pierderea de pachete pe cât posibil.
FiberChannel face mai multe concesii asupra securitaţii decât
clustereleVAX, unde orice dispozitiv cu un nume potrivit poate scrie în
memoria oricărui alt dispozitiv. FiberChannel permite reţelei să fie
virtualizată în zone, si nodurile dintr-o zonă nu pot accesa memoria
nodurilor din alte zone. Produse mai recente merg mai departe si propun
chiar tehnici de autentificare ; dar ideile de bază sunt aceleaşi.

Infiniband
Infiniband porneşte de la faptul că PCI, magistrala internă I/O utilizată
în multe staţii de lucru şi PC-uri, e învechită si trebuie înlocuită. Cu o
lăţime de bandă maximă de 533 Mb/sec, magistrala PCI este inundată de
perifericele moderne de mare viteză, ca plăcile de interfaţă Gigabit
Ethernet. Deşi există câteva alternative temporare cum ar fi magistrala
109
PCI-X, magistralele interne din calculatoare trebuie redimensionate ca să
ţina pasul cu modificări de genul de la 10-Mbit Ethernet la Gbit Ethernet.
De asemenea, observăm că există trei tehnologii de reţea diferite într-un
calculator: interfaţa de reţea (ex.Ethernet), interfaţa de disc (ex.SCSI prin
FiberChannel) şi magistrala PCI. Principiul lui Occam,cea mai bună cale
este cea mai simplă, sugerează substituirea acestora trei cu o tehnologie de
reţea unică. Astfel, Compaq, Dell, HP, IBM şi SUN au format Infiniband
Trade Association. Specificaţiile Infiniband utilizează multe din ideile
tehnologiei FiberChannel. Interconectarea se face cu comutatoare şi
legături punct-la-punct. Infiniband are câteva schimbări: adresare IP pe
128 biti pentru Internet-ul de generaţie următoare, virtualizarea legăturilor
fizice individuale sub formă de căi, facilităţi pentru calitatea serviciului,
multicast si RDMA pentru evitarea copiilor.

iSCSI
FiberChannel pare să aibă un cost mai ridicat la viteză echivalentă,
pentru scrierea între părţi decât scrierea între părţile Gigabit Ethernet. Dat
fiind faptul că IP a pătruns în numeroase spaţii de reţea cum ar fi voce, TV
şi radio, natural ar fi să pătrundă şi în spaţiul de memorare. Aceasta ar
trebui să scadă preţurile (deschizând noi pieţe pentru furnizorii de
reţele).Pe mai departe, se extind FiberChannel şi Infiniband să conecteze
prin Internet centre de date distante. Aceasta implică folosirea de
protocoale de transport care nu sunt neapărat compatibile cu TCP, în ceea
ce priveşte reacţia la congestie. De ce să nu adaptăm TCP în acest scop, în
loc să modificăm celelalte protocoale ca să fie compatibile TCP?
Cel mai interesant lucru la iSCSI este modul de emulare a RDMA prin
protocoale IP standard. La RDMA, adaptorul gazdă implementează în
hardware protocoalele de transport ale Internet-ului. Protocolul de
transport din Internet este TCP. Deci adaptoarele trebuie să implementeze
TCP în hardware. Chip-uri care realizează TCP, fără a creşte efortul de
calcul, sunt pe cale să devină accesibile pe scară largă.
Mai dificile sunt următoarele părţi. În primul rând, TCP este un protocol
orientat pe flux. Aplicaţia scrie octeţii într-o coadă, şi aceşti octeţi sunt
segmentaţi arbitrar în pachete. RDMA, pe de altă parte, este bazată pe
mesaje, fiecare din acestea are un câmp “nume buffer”. În al doilea rând,
RDMA prin TCP necesită un antet pentru numele buffer-elor.
Propunerile RDMA rezolvă ambele probleme stratificând trei
protocoale peste TCP. Primul protocol, MPA, adaugă un antet care
defineşte limitele mesajelor în fluxul de biţi. Al doilea şi al treilea protocol
implementează câmpurile antetelor RDMA dar sunt separate dupa cum
urmeaza. Când un pachet transportă date are nevoie doar de numele
bufferului şi deplasarea. Acest antet este abstractizat prin antetul DDA
(Direct Data Access) împreună cu o comandă (read sau write).
Protocolul RDMA, stratificat peste DDA adaugă un antet cu câteva
câmpuri în plus. De exemplu, pentru o citire RDMA la distanţă, cererea
iniţială trebuie să specifice numele buffer-ului de la distanţă (de citit) şi
numele bufferului local (de scris). Unul dintre numele acestor două buffer-
e poate fi plasat în antetul DDA, dar celălalt trebuie plasat în antetul
RDMA. Astfel, cu excepţia mesajelor de control, cum ar fi iniţierea unei
citiri, toate datele transportă doar un antet DDA şi nu un antet RDMA.
110
În evoluţia de la clusterele VAX la RDMA, o generalizare interesantă a
fost înlocuirea buffer-ului cu nume cu un buffer anonim. În acest caz
antetul DDA conţine numele unei cozi şi pachetul este plasat în primul
buffer liber, din capul cozii de la recepţie.

5.4. Extinderea la sistemul de fişiere


Până acum s-a tratat doar evitarea copiilor redundante ce apar cand se
transmit date între o aplicaţie (ca serverul Web) şi reţea. Dar (fig.5.2) şi
după îndepartarea tuturor antetelor redundante datorate copierii prin reţea,
încă mai sunt copii redundante care implică sistemul de fişiere. Tehnicile
de evitare a copierii, discutate până acum, sunt extinse si la sistemul de
fişiere. Pentru a procesa o cerere pentru fişierul X (fig.5.2), serverul
trebuie să citească X de pe disc (copia 1) într-un buffer kernel
(reprezentând fişierul cache) şi apoi să facă o copie din cache în buffer-ul
aplicaţiei (copia 2). Copia 1 este eliminată dacă fişierul este deja în cache,
o presupunere corectă pentru fişierele cu utilizare intensivă dintr-un server
cu memorie suficientă. Principalul scop este eliminarea copiei 2. Intr-un
server Web dublarea inutilă a numărului de copii înjumătăţeşte lărgimea
de bandă efectivă şi înjumătăţeşte cache-ul serverului. Aceasta reduce
performanţa serverului, crescând rata de pierderi, adică mai multe
documente sunt servite la viteza discului şi nu la viteza magistralei.
Sunt prezentate aici trei tehnici pentru îndepărtarea copiei redundante a
sistemului de fişiere (copia 2): maparea memoriei partajate care poate
reduce copia 2, dar nu este bine integrată în subsistemul de reţea; IO-Lite,
o generalizare a fbufs extinsă la sistemul de fişiere; I/O splicing, utilizată
de multe servere Web comerciale.

5.4.1. Memoria partajată


Variantele moderne de UNIX oferă un apel de sistem convenabil
mmap() pentru a permite unei aplicaţii (un server) să mapeze un fişier în
spaţiul său de adresare a memoriei virtuale. Şi alte sisteme de operare
oferă funcţii echivalente. Când un fişier este mapat în spaţiul de adrese al
unei aplicaţii, este ca şi cum aplicaţia ar fi făcut o copie cache a fişierului
în memoria sa. Operaţia pare redundantă, deoarece sistemul de fişiere
reţine fişierele şi în cache. Dar folosind memoria virtuala (P4, egalizarea
componentelor sistemului), fişierul cache este doar un set de mapări,
pentru ca alte aplicaţii şi cache-ul serverului de fişiere să acceseze în
comun setul de pagini fizice pentru acel fişier.
Serverul Web Flash evită copia 1 şi 2 (fig.5.2) impunând ca aplicaţia
server să mapeze în memorie fişierele des utilizate. Dar, fiind limitate atât
numărul de pagini fizice ce pot fi alocate paginilor fişierului, cât şi
mapările tabelului de pagini, serverul Web Flash trebuie să trateze aceste
fişiere mapate, ca un cache. În loc să memoreze în cache fişierele întregi,
el memorează doar segmente din aceste fişiere şi foloseşte o politică LRU
(Least Recently Used) pentru a demapa fişierele nefolosite de ceva vreme.
Funcţiile de mentenanţă cache sunt dublate prin cache-ul sistemului de
fişiere (care are o evidenţă mai precisă asupra resurselor, cum ar fi
paginile libere, fiind rezident în kernel). Totuşi, aşa se evită copiile 1 şi 2
(figura 5.2). În timp ce Flush utilizează mmap() pentru a evita copierea
sistemului de fişiere, el rulează în UNIX API. Astfel că Flush e obligat să
111
facă o copie suplimentară pentru subsistemul de reţea (copia 3, figura
5.2.). Chiar când s-a reuşit eliminarea copiei 2, reapare copia 3 !
Copia 3 poate fi evitată combinând emularea copierii, făcută de TCOW,
cu mmap() (desi TCOW are unele dezavantaje, menţionate anterior).
Deasemenea, nu este o soluţie general valabila pentru evitarea copierii la
interacţiunea cu un proces CGI prin intermediul unui canal/pipe UNIX .

5.4.2. IO-Lite: o vedere de ansamblu asupra buffer-elor


Deşi combinarea copiere emulată-mmap() elimină copierea redundantă,
lipsesc totuşi câteva optimizări. În primul rând, nu se face nimic pentru a
evita copierea între orice aplicaţie CGI (care generează conţinut dinamic)
şi serverul Web. Acest tip de aplicaţie se implementează deobicei ca un
proces separat, care-şi trimite conţinutul dinamic procesului server printr-
un canal/pipe Unix. Dar pipe-urile şi alte comunicaţii interprocese similare
presupun copierea conţinutului între două spaţii de adrese.
În al doilea rând, nici una din schemele de până acum nu a rezolvat
problema sumei ciclice de control TCP, care este o operaţie costisitoare.
Dacă acelaşi fişier indică mereu memoria cache, în afară de primul
răspuns care contine antetul HTTP, toate pachetele succesive, care
returnează conţinutul fişierului, rămân aceleaşi pentru oricare cerere. De
ce nu s-ar putea memora în cache suma de control TCP? Asta ar
presupune o memorie cache care să poată cumva asocia conţinutul
pachetului cu suma de control, treabă ineficientă într-o schemă de
memorare convenţională.
Schema de memorare IO-Lite generalizează ideile fbuf şi la sistemul de
fişiere. IO-Lite nu numai că elimină toate copiile redundante din figura
5.2, ci şi copierea redundantă între procesele CGI şi server. Ea are o
schema specializată de numerotare a buffer-elor care permite unui
subsistem (ca TCP) să realizeze eficient că retransmite un pachet anterior.
IO-Lite derivă din fbufs, dar extinderea la sistemul de fişiere adaugă mai
multă complexitate. Spre deosebire de TCOW, fbufs nu pot fi combinate
cu mmap(), deoarece în mmap() aplicatia alege adresa şi formatul buffer-
ului aplicaţie, în timp ce în fbufs kernel-ul selectează adresa şi buffer-ul
rapid (fbuf). Astfel, dacă aplicaţia a mapat un fişier utilizând un buffer în
spaţiul adreselor virtuale al aplicaţiei, buffer-ul nu poate fi trimis utilizând
un fbuf (spaţiul adreselor kernel) fără o copie fizică.
Deoarece fbufs nu pot fi combinate cu mmap, IO-Lite generalizează
fbufs pentru a include sistemul de fişiere, făcând mmap inutil. IO-Lite este
implementat şi într-un sistem de operare de interes general (UNIX), spre
deosebire de fbufs. Dar IO-Lite împrumută toate ideile principale de la
fbufs: noţiunea partajării read-only prin buffer-e invariabile (numite
felii/slices in IO-Lite), utilizarea buffer-elor compuse (numite buffer
agregates), şi noţiunea unui cache creat relaxat pentru o cale (numit I/O
stream in IO-Lite).

112
CPU Memorie
Aplicatia server-ului Web Buffer server Cached response
header
write() read() Buffer socket
Cached checksum
Kernel TCP/IP Sistem fisiere Buffer fisier
cache

Buffer IO-Lite

Bus memorie
Copia 1
Copia 2 Disc

Bus I/O
Adaptor de retea

Retea
Figura 5.10. IO-Lite elimină toate copiile redundante din figura 5.2. pasând efectiv pointeri
(prin mapări VM) la un singur buffer IO-Lite. Presupunând că fişierul, suma de control TCP şi
răspunsul HTTP sunt toate memorate în cache, server-ul Web nu trebuie decât să transmită
aceste valori din cache într-o singură copie la interfaţa de reţea.

IO-Lite trebuie să rezolve problema dificilă de integrare în sistemul de


fişiere. În primul rând, IO-Lite trebuie să se ocupe de modelele complexe
de partajare, unde mai multe aplicaţii pot avea buffer-e care pointează la
un buffer IO-Lite, împreună cu codul TCP şi cu serverul de fişiere. În al
doilea rând, o pagină IO-Lite poate fi în acelaşi timp o pagină de memorie
virtuală (returnată de fişierul de pagini de rezerva de pe disc) sau o pagină
de fişier (returnată de copia actuală de pe disc a fişierului). Astfel, IO-Lite
trebuie să implementeze o politică complexă de înlocuire, care integrează
împreună regulile standard de înlocuire a paginilor şi cele de înlocuire a
fişierului cache. În al treilea rând, ca să rulăm pe UNIX trebuie găsită o
cale de a integra IO-Lite fără operaţii majore asupra UNIX.
Figura 5.10. arată paşii pentru a răspunde la cererea GET din figura 5.2.
Când fişierul este citit prima dată de pe disc în memoria cache a sistemului
de fişiere, paginile fişierului sunt memorate ca buffer-e IO-Lite. Când
aplicaţia face un apel de citire a fişierului, nu se realizează nici o copie
fizică, dar este creat un buffer agregat cu un pointer spre buffer-ul IO-Lite.
Când aplicaţia trimite fişierul TCP-ului pentru a fi transmis, sistemul
reţelei primeşte un pointer la aceleaşi pagini IO-Lite. Pentru a preveni
apariţia erorilor sistemul IO-Lite păstrează un contor de referinta, pentru
fiecare buffer, şi realocă bufferul doar când toţi utilizatorii au terminat.
Figura 5.10. arată încă două optimizări. Aplicaţia ţine o memorie cache
de răspunsuri HTTP pentru fişierele comune, şi deseori poate adăuga pur
şi simplu răspunsul standard cu modificări minime. În al doilea rând,

113
fiecărui buffer îi este dat un număr unic (P12, adaugă stări redundante) de
către IO-Lite, şi modulul TCP ţine un cache al sumelor de control indexate
cu numărul buffer-ului. Astfel, când un fişier este transmis de mai multe
ori, modulul TCP poate evita calcularea sumei de control, după prima
transmisie. Aceste modificări elimină toată redundanţa din figura 5.2 care
măreşte viteza de procesare a răspunsului.
IO-Lite poate fi utilizată şi pentru implementarea unui program pipe
modificat, care elimină copierea. Când acest mecanism IPC este utilizat
între procesul CGI şi procesul server, toate copiile sunt eliminate, fără a
compromite siguranţa şi izolarea defectelor, oferită de implementarea
celor două programe ca procese separate. IO-Lite permite deasemenea
aplicaţiilor să-şi particularizeze strategia de memorare-cache a buffer-elor,
permiţând strategii de memorare-cache mai deosebite, pentru serverele
web bazate pe dimensiunea şi frecvenţa accesului.
Este important de remarcat că IO-Lite realizează aceste performanţe fără
a elimina complet kernel-ul Unix şi fără a lega strâns aplicaţia de kernel.
Serverul Web Cheetah, cu sistemul de operare Exokernel, e şi mai radical,
permite fiecărei aplicaţii (inclusiv serverului Web) să îşi individualizeze
complet reţeaua şi sistemul de fişiere. Mecanismele Exokernel permit
astfel de individualizari, pentru fiecare aplicaţie, fără a compromite
siguranţa. În virtutea acestor modificări, serverul Web Cheetah poate
elimina toate copiile din figura 5.2. şi deasemenea poate elimina
calcularea sumei de control TCP utilizând un cache.
În timp ce Cheetah permite câteva artificii suplimentare, enorma
provocare în ingineria software de proiectare şi menţinere de kernel-uri
individualizate pentru fiecare aplicaţie, face mai atractive abordările ca
IO-Lite. IO-Lite se apropie de performanţa kernel-urilor individualizate ca
Cheetah avand de rezolvat mult mai putine probleme de inginerie
software.

5.4.3. Evitarea copiilor sistemului de fişiere prin I/O Splicing


Server-ele Web sunt apreciate prin teste comerciale: SPECweb pentru
serverele Web şi poligraf-Web pentru proxy-ul Web. În spaţiul proxy,
există un raport de recurenţă anual în care toate dispozitivele sunt
măsurate împreună pentru a calcula cea mai mare rată de găsire a fişierului
dorit în cache, normalizată la preţul dispozitivului. Testerele SPECweb
utilizează un sistem diferit în care producătorii îşi înregistrează propriile
rezultate experimentale la sistemul de teste, chiar dacă aceste rezultate
sunt verificate. În testele poligraf Web, la vremea scrierii, tehnologia unui
server Web bazată pe ideile IO-Lite era printre cele mai avansate.
De altfel, în testele SPECweb mai multe server-e Web prezintă,
deasemenea, performanţe impresionante. Aceasta se datorează şi unui
hardware mai rapid (şi mai scump). Totuşi, există două idei simple care
pot evita nevoia de schimbări complete ale modelului ca în cazul IO-Lite.
Prima idee este de a plasa complet în kernel aplicaţia serverului Web. În
acest fel în figura 5.2., toate copiile pot fi eliminate deoarece aplicaţia şi
kernelul sunt părţi ale aceleaşi entităţi. Problema majoră cu această
abordare este că astfel de servere, Web aflate în kernel, trebuie să se ocupe
de rezistenţa la schimbări de implementare a sistemelor de operare. De
exemplu, pentru un server popular de mare performanţă care ruleaza pe
114
Linux, fiecare modificare internă a Linux-ului poate invalida ipotezele
software-ului serverului, şi cauza o blocare a sistemului. De menţionat că
un server convenţional din spaţiul utilizatorilor nu are această problemă
deoarece toate modificările implementării UNIX păstrează API-ul.
A doua idee menţine aplicaţia server în spaţiul utilizator dar se bazează
pe o idee simplă numită I/O splicing pentru a elimina toate copiile din
figura 5.2. I/O splicing arătat în figura 5.11. a fost introdus pentru prima
data în Fall şi Pasquale. Ideea este de a introduce un nou apel de sistem
care combină vechiul apel de citire a unui fişier cu vechiul apel (P6, rutine
specializate eficiente) de trimitere a unui mesaj în reţea. Permiţând kernel-
ului să îmbine aceste două sisteme de apel, separate până acum, putem
evita toate copiile redundante. Multe sisteme au apeluri de sistem cum ar
fi sendfile () care acum sunt utilizate de câţiva producători comerciali. În
ciuda succesului acestui mecanism, mecanismele bazate pe sendfile nu pot
fi bine generalizate pentru comunicaţia cu procesele CGI.

CPU Memorie
Aplicatia sesver-ului Web
sendfile()

Kernel Buffer fisier cache

Magistrala de memorie
Copia 1

Copia 2 Disc

Magistrala I/O
Adaptor de retea

Retea
Figura 5.11. În I/O splicing, toate indirectările cauzate de copierea în şi din buffer-e din
spaţiul utilizator sunt îndepartate printr-un singur apel de sistem care “îmbină” fluxul I/O
de la disc cu fluxul I/O de la reţea. Ca de obicei, copia 1 poate fi eliminată pentru fişierele
din cache.

5.5. Extinderea dincolo de copii


Clark şi Tennehouse, într-o lucrare de referinţă, generalizând ideea lui
Van Jacobson (descrisă mai înainte), au sugerat integrarea sumei de
control şi a copierii. Mai detaliat, ideea Jacobson a pornit de la următoarea
observaţie. Când se copiază un cuvînt de pachet dintr-o locaţie (de
exemplu W10 în memoria adaptorului din figura 5.12.) într-o locaţie de
memorie (de exemplu M9 din figura 5.12.), procesorul trebuie să încarce
W10 într-un registru şi apoi să memoreze acel registru în M9. Tipic, cele
mai multe procesoare RISC cer ca, între o încărcare şi o memorare
115
compilatorul să insereze un aşa numit slot de întârziere, sau ciclu gol,
pentru a asigura ca pipeline-ul să functioneze corect (aici nu motivăm de
ce). Acel ciclu gol poate fi utilizat pentru alte operaţii. De exemplu, el
poate fi folosit pentru a adăuga cuvântul tocmai citit într-un registru care
conţine suma de control curentă. Astfel, fără cost în plus, bucla de copiere
poate fi deseori complementata să fie şi buclă de sumă de control.
Dar există alte manevrări de date intensive cum ar fi criptarea de date şi
conversiile de format. De ce să nu integrăm toate aceste manevre în bucla
de copiere ? au susţinut Clark şi Tennehouse. De exemplu, în figura 5.2.
CPU ar fi putut citi W10 şi după aceea să decripteze W10 şi să scrie
cuvântul decriptat M9 decât să facă asta într-o altă buclă. Ei au numit
această idee integrated layer processing, sau ILP. Ideea esenţială este de a
evita pierderile evidente, (P1), la citirea (şi posibil la scrierea) biţilor
unui pachet, de mai multe ori pentru operaţii de manevrare multiplă a
datelor aceluiaşi pachet.

CPU
Stocare M9, R0 M9
(adaugare R0 la suma de
control)
Incarcare W10, R0

W10
Memoria adaptorului

Figura 5.12. Integrarea sumei de control şi a copierii

Astfel, ILP este o generalizare a integrării copiei sumei de control şi


pentru alte manevrări(de exemplu, criptare). Totuşi, ea are câteva cerinţe.
• Cerinţa 1: Informaţia necesară manevrărilor e specifică fiecărui nivel
(criptarea la nivelul aplicaţie, verificarea sumei de control la nivelul TCP).
Integrarea codului în nivele e dificilă fără sacrificarea modularitaţii.
• Cerinţa 2: Fiecare manevrare poate opera pe felii de diferite mărimi şi
pe zone diferite ale pachetului. TCP lucrează pe 16 biţi pentru o suma de
control de 16 biţi iar criptarea DES lucrează pe 64 biţi. Astfel, în timp ce
lucrează cu un cuvânt de 32 biţi, bucla ILP trebuie să se ocupe de două
cuvinte de sumă de control TCP şi o jumătate de cuvânt DES.
• Cerinţa 3: Unele manevrări pot depinde una de cealaltă: nu ar trebui ca
un pachet să fie decriptat, dacă suma de control nu este verificată.
• Cerinţa 4: ILP poate mări rata de pierderi a cache, deoarece poate
reduce localizarea printr-o singură manevrare. Dacă TCP şi DES au fost
făcute separat, şi nu într-o singură buclă, codul care ar fi fost folosit la
fiecare etapă este mai redus pentru cele două bucle luate separat decât cel
pentru o singură buclă. Aceasta face mai probabil ca, într-o implementare
mai naivă, codul să fie găsit în cache-ul instrucţiunii. Creşterea integrării,
116
dincolo de un anumit punct poate distruge localizarea codului într-o
asemenea măsură încât, poate avea efectul opus.
Primele trei cerinţe arată că ILP este greu de realizat. A patra sugerează
că integrarea a mai mult de câteva operaţii odată, poate chiar reduce
performanţa. În sfârşit, dacă pachetul de date este utilizat de mai multe ori,
ar putea fi rezident în cache-ul de date (chiar şi într-o implementare
naivă), făcând inutil tot deranjul pentru integrarea buclelor. Posibil din
cauza acestor motive, ILP a rămas doar o idee tentantă. Înafară de
combinarea copierii cu verificarea sumei de control, au fost puţine
manevrări care au mai fost integrate în sistemele academice sau
comerciale.

5.6. Extinderea dincolo de manevrările datelor


Până acum scopul a fost reducerea lărgimii de bandă a memoriei (şi a
magistralei) cauzată de operaţiile de manipulare a datelor. În primul rând,
a fost problema eliminării copierii de date redundante între reţea şi
aplicaţie. Apoi a fost tratată copierea redundantă dintre sistemul de fişiere,
aplicaţie şi reţea. În final, s-a pus problema eliminării citirilor şi scrierilor
redundante în/din memorie, utilizând procesarea integrată pe niveluri când
mai multe operaţii de manevrări a datelor operează asupra aceluiaşi
pachet. Comun acestor tehnici este încercarea de a reduce presiunea
asupra memoriei şi a magistralei I/O, prin evitarea scrierilor şi citirilor
redundante. Dar odată realizată, mai sunt alte surse de presiune care apar
într-o arhitectură endnode (fig.5.2). Aceasta se referă la următorul extras
dintr-un e-mail trimis după lansarea etapei alpha a serverului rapid Web
Linux de nivel utilizator.
Cu un transfer de fişier cu zero copii, deplasarea datelor nu mai este o
problemă, reteaua I/O asincronă permite o planificare cu adevărat
necostisitoare, şi invocarea apelului de sistem adaugă un antet neglijabil
în Linux. Ce ne rămâne acum sunt doar ciclurile de aşteptare, CPU-urile
şi NIC-urile, care rivalizează pentru lăţime de bană de memorie şi de
magistrală.
În esenţă, odată ce se are grijă de efectele de ordinul întâi (cum ar fi
eliminarea copiilor), performanţa poate fi îmbunătăţită doar având grijă la
ce ar putea fi considerat ca efecte de ordin doi. Două astfel de efecte
arhitecturale cu impact mare asupra utilizării lărgimii de bandă a
magistralei şi a memoriei sunt utilizarea eficientă a cache-urilor şi alegerea
DMA versus PIO.

5.6.1. Utilizarea eficientă a cache-urilor


Modelul arhitectural din figura 5.2, nu prezintă două detalii
importante. Reamintim că procesorul păstrează una sau mai multe cache-
uri de date (d-caches) şi una sau mai multe cache-uri de instrucţiuni (I-
caches). Cache-ul de date este un tabel care mapează de la adrese de
memorie la conţinutul de date; dacă există citiri şi scrieri repetate la
aceeaşi locaţie L din memorie şi L este în cache, atunci aceste citiri şi
scrieri pot fi servite direct din cache-ul de date, fără a implica lăţime de
bandă de memorie şi de magistrală. Similar, reamintim că programele sunt
stocate în memorie; fiecare linie de cod executată de CPU trebuie adusă
din memoria principală dacă nu se găseşte în cache-ul de instrucţiuni.
117
Acum, pachetul de date beneficiază puţin de un cache de date, deoarece
datele sunt puţin reutilizate şi copierea implică scrierea într-o nouă adresă
de memorie, spre deosebire de citiri şi scrieri repetate din aceeaşi adresă
de memorie. Astfel, tehnicile deja discutate de reducere a copiilor sunt
inutile, în ciuda prezenţei în procesor a unei memorii cache de date mari.
Totuşi, există două alte elemente stocate în memorie care pot beneficia de
pe urma memoriei cache. În primul rând, programul, care execută codul de
protocol pentru procesarea unui pachet, trebuie extras din memorie, dacă
nu este stocat în I-cache. În al doilea rând, starea necesară pentru a procesa
un pachet (tabelele de stări ale conexiunilor TCP) trebuie extrasă din
memorie, dacă nu este stocată în d-cache. Din aceste două posibile
concurente pentru lăţimea de bandă de memorie, codul de executat este o
ameninţare mai serioasă. Aceasta deoarece starea (în bytes), necesară
pentru procesarea unui pachet (de exemplu intrarea într-un tabel de
conexiuni, sau într- un tabel de rutare), este în general mică. Totuşi, pentru
un pachet mic de 40 bytes până şi aceasta poate fi semnificativă. Astfel,
evitarea de câte ori este posibil, a utilizării unor stări redundante (care
tinde să polueze d-cache),poate îmbunătăţi performanţa.
Codul necesar pentru executarea întregii stive de reţea (legăturile de
date, TCP, IP, nivelul socket şi intrările/ieşirile kernel) poate fi mult mai
mare. Masurările la Blackwell arată un cod de 34 KB utilizând
implementarea TCP NetBSD din 1995. Ţinând cont că, chiar şi pachetele
Ethernet mari au maxim 1,5 KB, efortul de a încărca codul din memorie
face să pară nesemnificativ efortul de a copia pachetul de mai multe ori.
Dacă, de exemplu, I-cache are 8KB (tipic pentru sisteme mai vechi, cum
ar fi sistemele alpha utilizate în Blackwell) înseamnă că cel mult un sfert
din stiva de reţea poate intra în cache. Rezultatul e că toate codurile, sau
cea mai mare parte a lor, trebuie aduse din memorie, de fiecare dată când
un pachet trebuie procesat. Sistemele moderne nu şi-au îmbunătăţit
semnificativ dimensiunea I-cache. Pentium III utilizează 16 KB. Astfel,
utilizarea eficientă a I-cache poate fi o soluţie pentru îmbunătăţirea
performanţei, în special pentru pachetele mici.
Sunt descrise două tehnici care pot îmbunătăţi eficienţa I-cache:
aranjarea codului şi procesarea pe nivele
Aranjarea codului
Este greu sa-ti dai seama când scrii un cod de reţea, că plasarea codului în
memorie (deci în I-cache) are un grad de libertate (P13) care, cu puţin
efort, poate fi exploatat. Ideea este de a plasa codul în memorie pentru a
optimiza situaţiile comune (P11): codul utilizat frecvent să fie în I-cache
pentru ca efortul de încărcare a I-cache să nu fie risipit.
Aparent, nu e necesar un efort suplimentar. Cache-ul ar trebui să
favorizeze codul frecvent utilizat fată de cel utilizat mai rar, deci operatia
ar trebui sa fie automată. Acest lucru nu se întîmplă însă din cauza
modului de implementare a I-cache-ului.
• Maparea directă: Un I-cache este o mapare a adreselor de memorie la
conţinutul lor; maparea este de obicei implementată de funcţie hash simplă
optimizată pentru accesul secvenţial. Astfel, majoritatea procesoarelor au
I-cache-uri mapate direct, unde biţii de ordin inferior ai unei adrese de
memorie sunt utilizaţi pentru a indexa zona I-cache. Dacă biţii de ordin
superior se potrivesc, conţinutul este returnat direct din cache; altfel, se
118
face o citire de memorie prin magistrală, şi se stochează în aceeasi locaţie
noile date şi biţii de ordin superior.
Figura 5.13. arată efectul acestei implementări. În stânga se vede
dispunerea codului în memorie pentru două funcţii de reţea, codul
neutilizat frecvent fiind cel cu negru. Deoarece dimensiunea I-cache este
doar jumatate din mărimea totală a codului, este posibil ca două linii de
cod accesate frecvent (cum ar fi X şi Y, cu adrese care au acelaşi rest
modulo în dimensiunea I-cache) să mapeze către acceaşi locaţie din I-
cache.Dacă şi X şi Y sunt utilizate pentru a procesa fiecare pachet, ele se
vor evacueze una pe alta din cache, chiar dacă amandouă sunt utilizate
frecvent.
• Mai multe instrucţiuni pe bloc: Multe I-cache-uri pot fi văzute ca o zonă
de blocuri, în fiecare bloc fiind memorate instrucţiuni multiple (8 de
exemplu). Când o instrucţiune este adusă, toate cele 8 instrucţiuni din
acelaşi bloc sunt aduse, în ipoteza localizării spaţiale: cu acces secvenţial,
probabil că celelalte şi celelate 7 instrucţiuni vor fi aduse, şi este mai ieftin
de citit mai multe instrucţiuni din memorie în acelaşi timp.
Din păcate, o mare parte din codul de reţea conţine controlul la erori,
cum ar fi “dacă E eronat execută X, altfel execută Z”. Z este rar executat,
dar deseori compilatorul plasează codul pentru Z imediat după X. De
exemplu, (figura 5.13) ne imaginăm că codul pentru Z urmează imediat
după X. Dacă X şi Z cad în acelaşi bloc de 8 instrucţiuni, extragerea lui X,
frecvent utilizat, implică extragerea lui Y, rar utilizatul. Astfel încărcarea
cache e mai puţin eficientă(multa muncă inutilă), şi după încărcare cache-
ul e mai puţin util (mai puţin cod util în cache).

Dimensiunea
I-cache Utilizarea frecventa a
Codul codurilor F1 si F2
F1
X

Utilizarea nefrecventa a
Codul codurilor F1 si F2
F2
Y
Re-localizare

Figura 5.13. În stânga este codul de reţea dispus în memorie, cu intercalare arbitrară a codului
frecvent utilizat (alb) şi rar utilizat (negru). Utilizarea unui cache direct mapat cu jumătate din
dimensiunea totală a codului poate conduce la coliziunea a două instrucţiuni frecvent utilizate,
cum ar fi X şi Y. Această problemă poate fi evitată reamplasând tot codul utilizat frecvent,
astfel încât să fie continuu, (dreapta).
Ambele efecte sunt cauzate de reflectarea imperfectă de către cache-uri a
localitaţii temporale. Prima cauză este o funcţie hash imperfectă, ce poate
cauza coliziuni între două adrese utilizate frecvent, iar a doua este
optimizarea localitaţii spaţiale. Ambele efecte pot fi diminuate prin
reorganizarea codului de reţea astfel încât tot codul utilizat frecvent sa fie
continuu (dreapta figurii 5.13). De exemplu, în cazul “dacă E eronat
119
execută X, altfel execută Z”, codul pentru Z poate fi mutat departe de X.
Aceasta necesită adăugarea unei instrucţiune de salt la cod lui Z pentru ca
acesta să poată reveni la codul ce urma după Z în versiunea neoptimizată.
Cum saltul se face doar în caz de eroare nu reprezintă un cost mare.
Deci localizarea codului în memorie (P13) permite prima optimizare, a
doua optimizare fiind a cazului cel mai probabil(P11), prin lungirea
codului rar utilizat.

Procesarea pe nivele
Reorganizarea codului poate ajuta până la un anumit punct, dar dă greş
dacă zona de lucru (setul de instrucţiuni accesat pentru aproape fiecare
pachet) depăşeşte dimensiunea I-cache. De exemplu, în figura 5.13. dacă
dimensiunea albului, adică a instrucţiunilor frecvent utilizate este mai
mare decât I-cache, reorganizarea codului va fi încă de folos (sunt
necesare mai puţine încărcări din memorie, deoarece fiecare încărcare
aduce instrucţiuni utile). Totuşi, fiecare instrucţiune va mai trebui adusă
din memorie.

Timp
P1
sosire P2 Procesare
convenţională
P1 P1 P1 P2 P2 P2
Legătura Reţea Transport Legătura Reţea Transport
de date de date
Procesare
P1 P2 P1 P2 P1 P2 localizata
Legătura Legătura Reţea Reţea Transport Transport pe
de date de date niveluri.

Figura 5.14 Reprezentând în timp procesareaconvenţională, toate nivelurile de reţea


ale pachetului P1 sunt procesate înaintea celor pentru pachetul P2. Într-o procesare
localizată pe niveluri a receptorului, codul fiecarui nivel este executat de mai multe ori,
pentru pachetele multiple recepţionate, înainte de a trece la nivelul următor.

Dacă zona de lucru al stivei reţelei poate încăpea într-un I-cache modern
(care este din ce în ce mai mare) e posibil ca protocoalele mai complicate
(ce rulează TCP/IP ) să nu încapă. Ideea procesării pe nivele este utilizarea
efectivă a I-cache cât timp codul fiecărui nivel al stivei reţelei încape în
I-cache. Procesând repetat codul aceluiaşi nivel pntru mai multe pachete,
costul încărcării I-cache-ului este împărţit pe mai multe pachete(P2c). În
figura 5.14, la reprezentarea în timp a procesării convenţionale, toate
nivelurile reţea ale pachetului P1 sunt procesate înainte celor ale
pachetului P2. Dacă două pachete P1 şi P2 sosesc la un server, într-o
implementare convenţională, se termină toată procesarea lui P1 începând
cu nivelul legătură de date şi terminând cu nivelul transport, şi doar apoi
se începe procesarea pachetului P2.
Ideea principală în procesarea localizată pe niveluri este de a exploata un
alt grad de libertate (P13) şi de a procesa toate codurile unui nivel, pentru
cât mai multe pachete posibil, înainte de a trece la nivelul următor. Astfel,
120
după ce codul de nivel legătură de date pentru P1 este terminat, CPU trece
la executarea codului de nivel legatură de date pentru P2 şi nu la codul de
nivel reţea pentru P1. Corectitudinea ar trebui să nu fie afectată, deoarece
codul unui nivel n-ar trebui să depindă de starea nivelurilor
inferioare.Totuşi, procesarea integrată pe niveluri are câteva dependenţe
subtile şi deci cazuri de eşec.
Astfel, dacă codul pentru fiecare nivel încape în I-cache (de exemplu
codul nivelului legatura de date) dar nu încape codul pentru toate
nivelurile, atunci această optimizare amortizează costul încărcării I-cache
pentru mai multe pachete. Se utilizează procesarea pe loturi (P2c): cu cât
lotul este mai mare, cu atât I-cache este utilizată mai eficient.
Implementarea poate adapta dinamic dimensiunea lotului. Codul poate
procesa o serie de până la k pachete, din coada de pachete sosite( k este un
parametru care limitează latenţa). Dacă sistemul este slab încărcat, atunci
un singur mesaj va fi procesat la un moment dat. Dacă sistemul este
puternic încărcat, se creşte dimensiunea lotului, crescând astfel când e
nevoie, eficienţa utilizării lărgimii de bandă a memoriei.

Consideraţii de inginerie software


Optimizările de gen restructurarea codului (figura 5.13.) şi procesarea
localizată pe niveluri(figura 5.14.) trebuie evaluate şi ţinând cont de
efectele lor asupra modularităţii codului şi mentenanţei. S-ar putea rescrie
kernel-ul şi toate aplicaţiile, utilizând un limbaj de asamblare pentru o
optimizare perfectă a utilizării lărgimii benzii memoriei. Dar ar fi greu de
a face codul să ruleze corect sau să fie uşor de întreţinut.
Restructurarea de cod este cel mai bine realizată de un compilator. De
exemplu, codul ce manevrează erorile, poate fi adnotat cu indicii care să
sugereze ramificaţiile cele mai frecvente (evidente, în general, pentru un
programator) şi cu un compilator completat special, care poate restructura
codul pentru localizarea I-cache.
Pe de altă parte, procesarea localizată pe nivele păstrează modularitatea
în interiorul nivelurilor. Comunicarea între niveluri trebuie modificată
după cum urmează. Dacă fiecare cod de nivel pasează un pachet către
codul unui nivel superior cu o procedură de apel, acest cod trebuie
modificat să adauge pachetele pentru nivelul superior într-o coadă.
Similar, când un nivel este apelat, el scoate pachete din coada sa de citire
până când aceasta este terminată; după procesarea fiecărui pachet, îl
plasează în coada pentru următorul nivel superior. Această strategie
funcţionează corect când fiecare nivel poate reutiliza buffer-ele de la alte
niveluri, aşa cum este cazul mbuf-urilor UNIX. În ansamblu, modificările
codului ar putea să nu fie majore.

5.6.2. Accesul direct la memorie DMA versus I/O programate


Secţiunile anterioare au afirmat că schema Witless utilizează I/O
programate, sau PIO (procesorul este implicat în transferul fiecărui cuvânt
între memorie şi adaptor), în timp ce alte scheme, cum ar fi VAX Clusters
utilizează DMA (unde adaptorul copiază datele direct în memorie). Poate
părea că DMA este întotdeauna mai bună decât PIO. Totuşi, comparaţiile

121
între DMA şi PIO sunt complicate deoarece fiecare metodă are implicaţii
subtile pentru utilizarea per total a lărgimii de bandă de memorie.
De exemplu, PIO are avantajul că datele circulă prin procesor şi astfel
ajung în cache-ul procesorului, prevenind pierderile de lăţime de bandă a
memoriei la accesul secvenţial. Deasemenea, cu PIO e mai uşoară
integrarea altor funcţii, cum ar fi verificarea sumei de control, fără să fie
necesar un adaptor hardware care să facă aceeaşi funcţie.
Totuşi, unele studii au arătat că dacă datele sosite sunt utilizate mult mai
târziu de către aplicaţie, atunci plasarea datelor în d-cache prea devreme
este o risipă a d-cache şi mai degrabă scade decât creşte rata de succes a
cache. Pe de altă parte DMA poate fura cicluri de la CPU şi necesită
deasemenea o atentă invalidare a cache-ului când datele sunt scrise într-o
locaţie de memorie(care poate fi tot în cache). Alegerea se face de la caz la
caz, din considerente arhitecturale şi de aplicaţie
.
5.7. CONCLUZII
Pe măsură ce reţelele devin mai rapide legăturile, cum ar fi Gigabit
Ethernet, sunt deseori mai rapide decât magistralele interne şi memoriile
din calculatoare sau servere. Astfel, lărgimile de bandă de
memorie/magistrale sunt resurse cruciale. Au fost descrise tehnici de
optimizare a utilizării lărgimii de bandă de memorie/magistrală pentru
procesare de pachete IP şi Web, care reprezintă traficul Internet dominant.
S-a arătat cum să înlăturăm copiile redundante implicate în
procesarea unui pachet IP utilizând memoria adaptorului sau remaparea
memorie virtuale. Apoi s-a arătat cum să îndepărtăm copiile redundante
implicate în procesarea cererilor Web la un server, prin generalizarea
remapării memoriei virtuale, pentru a include sistemul de fişiere sau prin
combinarea sistemului de fişiere şi reţelei I/O într-un singur apel de
sistem. Apoi s-a arătat cum să combinăm diferite manipulări de date
simultan. Toate aceste tehnici necesită schimbări în aplicaţie şi kernel dar
schimbările sunt destul de localizate şi majoritatea păstrează
modularitatea.
În final s-a arătat că, dacă nu se are grijă, procesarea protocolului poate
face nesemnificativă suprasarcina datorată copierii şi au fost descrise
tehnici de optimizare a cache-ului de instrucţiuni. Serverele Web moderne
pot fi deja optimizate pentru implementări zero-copii utilizând apeluri de
sistem de tipul sendfile (). Totuşi, astfel de servere încă consumă cicluri de
processor aşteptând memorie. Astfel, tehnici de îmbunătăţire a eficienţei I-
cache pot oferi următoarea etapă de optimizări pentru serverele Web.
Figura 5.1. prezintă un sumar al tehnicilor utilizate în acest capitol,
împreună cu principiile majore implicate.
Dar nu au fost eliminate toate problemele de performanţă implicate în
construirea unui server Web modern. Site-uri Web complexe, cum ar fi
amazon.com utilizează adesea mai multe niveluri de procesare pentru a
răspunde cerinţelor Web (un server aplicaţie, un server Web şi un server
bază de date). Serverele Web bază de date introduc noi constrângeri care
pot necesita noi tehnici în afară de cele descrise aici. Totuşi, principiile de
bază ar trebui să rămână aceleaşi.
Referitor la principii, capitolul se ocupă cu utilizarea repetată a P1, de
evitare a pierderilor evidente, ca citiri şi scrieri inutile consumatoare de
122
lăţime preţioasă de bandă de memorie/magistrală. La prima privire,
principiul P1 pare stupid . Ceea ce face acest principiu mai profund este că
pierderea devine vizibilă doar dacă analiza este extinsă la întregul sistem,
sau la cea mai mare parte din el.În interiorul fiecărui subsistem local (de
exemplu aplicaţie-kernel, kernel-retea, disc-sistem de fişiere) nu există
risipă de lăţime de bandă de memorie. Doar atunci când se urmăreşte
traseul unui pachet recepţionat se descoperă redundanţa dintre copiile
aplicaţie-kernel şi kernel-reţea. Doar atunci când se extinde vederea şi mai
departe pentru a observa distorsiunile implicate în răspunsul la o cerere
Web se observă redundanţele care implică sistemul de fişiere. Doar atunci
când se extinde şi mai mult vederea se văd toate manipulările implicate în
procesarea unui pachet şi citirile inutile din memorie. În sfârşit, doar
atunci când se examinează încărcarea instrucţiunilor se vede posibilitatea
alarmantă ca codul protocolului să fie de câteva ori mai mare decât
dimensiunea pachetului.
Astfel, utilizarea primului principiu al algoritmilor de reţea necesită o
vedre sinoptică a întregului sistem, de la http şi anteteele sale la sistemul
de fişiere şi la cache-urile de instrucţiuni. Deşi complexitatea pare
descurajantă, s-a argumentat deja că modelele simple de hardware,
arhitectură, sisteme de operare şi protocoale pot face posibilă o astfel de
vedere globală. De exemplu, I-cache-urile au câteva variante complexe,
dar un model simplu, de mapare directă I-cache cu multiple instrucţiuni
per bloc, e uşor de ţinut minte de proiectantul unui sistem de operare.
Comparate cu eleganţa şi complexitatea tehnicilor teoretice, cum ar fi
algoritmul elipsoid pentru programarea lineară şi teoria lanţurilor Markov
cu mixaj rapid, tehnicile de sistem, cum ar fi evitarea copierii, par lipsite
de strălucire şi superficiale. Totuşi, se poate argumenta că complexitatea
sistemului nu se referă la profunzime(complexitatea fiecărei componente
în parte) ci la ansamblu(relaţiile complexe dintre componente). Poate că
gradul de înţelegere (HTTP, sistemul de fişiere, codul de reţea,
implementarea cache-ului de instrucţiuni) necesar pentru a optimiza
lăţimea de bandă de memorie într-un server Web pledează pentru această
lucrare.

5.8. EXERCIŢII
1. Memorii cache de date şi copii: o memorie cache de date normală
mapează de la o adresă de locaţie de memorie la o piesă de conţinut. Cand
conţinutul e accesat frecvent, atunci poate fi accesat direct din cache, în
loc sa se faca un acces la memorie. Presupunând că memoria cache este de
tip write-back, chiar şi scrierile pot fi facute în cache în loc de memorie şi
sunt scrise în memorie doar când cache-ul e supra-încărcat. Un bloc cache
modern e destul de mare (128 biţi), cu mapari de la adrese de 32 de biţi la
128 de biţi, de date începând cu acea adresă.
Abordăm problema copierii, unde diferite module(inclusiv reţeaua şi
sistemul de fişiere) copiază date prin intermediul buffer-elor care devin
curând supra-încărcate (de exemplu, buffer-ul socket, buffer-ul aplicaţie).
lucru făcut pana acum cu modificări software. Analizam acum dacă
schimbarea arhitecturii hardware poate ajuta, fără modificări software ca
IO-Lite, fbufs şi mmap.

123
• Chiar şi un cache de date obişnuit poate ajuta la îndepărtarea unor
supraincarcari când copiem date de la locaţia L la locaţia M. Explicaţi de
ce. (Presupunem că locaţia M este un buffer temporar care este în curând
supraînscris, ca si un buffer socket. Presupunem că dacă un singur cuvânt
este scris într-un bloc cache mare, cuvintele rămase pot fi marcate ca
invalide.) Intuitiv, problema e dacă există un echivalent al copierii-la-
scriere (utilizată pentru a reduce copierea între spaţiile adreselor virtuale)
folosind cache-uri de date.
• Acum presupunem o proiectare diferita a cache-ului de date, unde un
cache este o mapare de la una sau mai multe adrese la acelaşi conţinut.
Astfel, un cache s-a modificat de la o mapare unu-la-unu la o mapare mai
multe-la-unu. Presupunem un cache unde două locaţii pot pointa către
acelaşi conţinut. O intrare cache poate fi (L,M,C), unde L şi M sunt adrese
şi C este conţinutul comun al lui L şi M. Un acces la memorie la oricare L
sau M va returna C. Care este avantajul faţă de schema precedentă ?
• Totul e foarte ipotetic si ciudat. Comentaţi dezavantajele ideii din
cazul precedent. Multe cache-uri utilizează tehnica numită asociativitatea
multimilor/set associativity, unde o simplă funcţie hash (de exemplu biţii
de ordin inferior) este utilizată pentru a selecta un set mic de intrări cache,
pe care hardware-ul le parcurge în paralel. De ce ar putea adresarea
multiplă per intrare cache să interacţioneze slab cu căutarea prin
asociativitatea multimilor ?
2. Optimizări de nivel aplicaţie pentru servere Web: sisteme de
operare ca Exokernel permit aplicaţiei să particularizeze kernelul in
beneficiul său, fără a compromite securitatea altor aplicaţii. O optimizare
interesantă este combinarea TCP final FIN cu citirea ultimului segment de
date (optimizare permisă de TCP).
• De ce această optimizare ajută transferurile Web mici, obişnuite?
• De ce este această optimizare greu de realizat într-un server Web
obişnuit şi mai uşor de realizat dacă aplicaţia este integrată în kernel, ca în
Exokernel?
• Cum poate fi deplasată această optimizare într-un server Web obişnuit
pasând informaţie prin interfaţă (P9) fără a compromite siguranţa?
3. Copiere inversă: emularea copierii-la-scriere are un grad de libertate
interesant (P13) pentru copiere paginilor-aliniate de date între două
module (să zicem, sistem şi aplicaţie). Imaginaţi-vă că doriţi să copiaţi o
pagină parţială dintr-o pagină de aplicaţie, X, într-o pagină de sistem,Y.
Dacă pagina este plină presupuneţi că puteţi schimba cele două pagini
eficient. Presupuneţi că pagina parţială are date utile D şi o rămăşiţă R.
• Dacă volumul de date D este mic comparativ cu R e mai simplu să
copiem D în pagina destinaţie Y. Dar dacă D e mare (aproape toată
pagina) comparativ cu R, imaginaţi o strategie simplă pentru a minimiza
copierea. De reţinut că dacă pagina destinaţie Y are alte date în restul de
pagină aceste date trebuie să rămână şi după copiere.
• Care e criteriul de alegere între aceste două strategii?

124
CAPITOLUL 6

CONTROLUL TRANSFERULUI

Într-o companie plata unei facturi durează 6 luni. Factura ajunge prima
dată în poştă, petrece ceva timp pe biroul secretarei, ajunge pe biroul celui
care ia principalele decizii şi ajunge în sfârşit la contabil. La procesarea
facturii, fluxul de control îşi urmează calea prin diferite straturi de
comandă, fiecare adăugând un antet semnificativ. Un consultant de
management sugereaza scurtarea procesarii facturii elimininand straturile
intermediare când e posibil, şi facand fiecare nivel cât se poate de selectiv.
Totuşi, fiecare nivel are câteva motive de a exista. Camera de poştă îmbină
serviciul de livrare a poştei pentru toate departamentele companiei.
Secretara protejează şeful de întreruperi şi elimină cererile nepotrivite.
Şeful trebuie eventual să decidă dacă factura merită plătită. Detaliile de
alocare a banilor sunt decise de contabil.
Un CPU modern, care procesează un mesaj de reţea, trece prin nivele
similare de mediere. Dispozitivul (un adaptor Ethernet) întrerupe CPU,
solicitand atenţie. Controlul este pasat la kernel, care comasează
întreruperile cand e posibil, face procesarea pachetului la nivelul reţea şi
planifică să ruleze procesul aplicaţie (un server Web). Recepţia unui
singur pachet oferă o vedere limitată a contextului global de procesare. De
exemplu, un server Web va analiza cererea din pachetul de reţea, va căuta
fişierul şi va iniţializa extragerea fişierului de pe disc. Când fişierul este
citit în memorie, este trimis înapoi un răspuns conţinând fişierul cerut,
adăugând un antet HTTP.
A fost analizata reducerea supraincarcarii creata de operaţiile cu date
(copiere, verificarea sumei de control). Va fi analizata reducerea
supraincarcarii creata de controlul implicat în procesarea unui pachet
(trimiterea/recepţia unui pachet) si apoi la aplicatia de reţea, serverul Web.

Număr Principiu Utilizare


P8 Trecerea dincolo de apelurile de sosire Apel de plecare
utilizate în specificaţii
P8 Procesare pe mesaj, nu pe nivel x-Kernel
P13 Protocol de legătură implementat cu cod Potrivirea variantelor
utilizator
P13 Proces per acces la disc Flash
P13 Modularizare pe sarcini, nu pe clienţi Server Web Haboob
P4 Mapări VM pentru evitarea copiilor Flash
în cache şi aplicaţie
P15 Arbore bitmap ufalloc() rapidă
P12a Calculul incremental al vectorului de interes select() rapidă
P9 Pasarea indiciilor de la protocol la select()
P12 Reţinerea intereselor de-a lungul apelurilor
P3c Mutarea protecţiei din kernel în adaptor ADC-uri
P2 Autorizarea adaptorului de către Kernel la
iniţializare
P13 Intreruperi comasate de procese Majoritatea OS
P2b Executarea protocolului în contextul LRP
procesului de recepţie
Figura 6.1. Tehnici de reducere a antetelor de control si principiile corespondente

125
6.1 Suprasarcina generata de control
Capitolul precedent a analizat suprasarcina cauzata de copiere într-un
server web (arătând potenţialele copii implicate în răspunsul unui server la
o cerere get, fig.5.2). Figura 6.2 arată potenţialele suprasarcini generate de
controlul dintr-un server Web mare care deserveşte mai mulţi clienţi (sunt
ignorate aspectele transferului de date). E prezentata o vedere arhitecturală
simplificată, axată pe interacţiunea controlulului dintre adaptorul de reţea
şi CPU (prin intreruperi), între aplicaţie şi kernel (prin apelul de sistem) şi
între diferite procese de nivel aplicaţie sau thread-uri (prin apeluri la
planificator). Pentru simplitate e dat doar un CPU în server (multe servere
sunt multiprocesor) şi un singur disc (unele servere utilizează discuri
multiple şi discuri cu capete multiple). Serverul poate servi un număr mare
/mii de clienţi concurenţi.
Pentru a înţelege posibilele suprasarcini generate de control, implicate in
deservirea unei cereri GET, presupunem că clientul a trimis o cerere TCP
SYN, care soseşte la adaptor, de la care este plasată în memorie. Kernelul
este apoi informat de această sosire prin intermediul unei întreruperi.
Kernelul anunţă serverul Web prin deblocarea unui apel de sistem
anterior; aplicaţia serverului Web va accepta conexiunea dacă are resurse
sufiente.

Proces per grup; grupuri definite de structura aplicatiei

Client 1...Client 50 Client 51...Client 74 Client 75


Aplicatia
serverului
Web
write() read() FindActive()

Kernel
TCP/IP Sistem fisiere

Pachet CPU
receptionat Memorie Disc
Bus

Adaptor de retea

Retea
Figura 6.2. Antetul de control implicat in manipularea unei cereri GET la un server

În pasul 2 al procesării, unele procese ale serverului parcurg cererile


Web. Presupunem că cererea este GET fisier1. În pasul 3, serverul trebuie
să localizeze fişierul pe disc, navigand prin structurile de directoare, care
pot fi deasemenea stocate pe disc. Odată fişierul localizat, în al 4-lea pas,
procesele server iniţiază un Read spre sistemul de fişiere (alt apel de
sistem). Dacă fişierul este în cacahe-ul de fişiere, cererea Read poate fi
satisfăcută repede; altfel, subsistemul de fişiere iniţiază o căutare pe disc
pentru a citi datele de pe disc. După ce fişierul este în buffer-ul aplicaţiei,

126
serverul trimite răspunsul HTTP, prin scrierea conexiunii corespunzătoare
(alt apel de sistem).
Pînă acum, singurele suprasarcini generate de control par să fie apelurile
de sistem şi întreruperile, dar asta deoarece nu au fost examinate
îndeaproape structura reţelei şi codul aplicaţiei.
În primul rand, pentru un cod structurat naiv, cu un singur process per
nivel în stivă, atunci suprasarcina generata de planificarea proceselor (de
ordinul sutelor de microsecunde) pentru procesarea unui pachet, poate fi
cu uşurinta mult mai mare decât timpul de sosire al unui singur pachet.
Această potenţială suprasarcina generata de planificare este arătată în
figura 6.2 cu linie punctată spre codul TCP/IP din kernel. Din fericire, cele
mai multe coduri de reţea sunt structurate mai mult monolitic, cu un antet
de control minim, deşi unele tehnici mai inteligente pot face si mai bine.
În al doilea rând, descrierea procesării web s-a axat pe un singur client.
Deoarece presupunem un server Web mare ce lucrează concurent pentru
mii de clienţi, nu e clar cum ar trebui structurat acesta. La o extrema,
fiecare client este un process separat (sau thread) rulând codul serverului
Web si concurenţa este maximizată (pentru că atunci când clientul 1
aşteaptă pentru o citire de pe disc, clientul 2 ar putea trimite pachete de
reţea) cu costul mare al suprasarcinii generata de planificarea procesului.
Pe de altă parte, dacă toţi clienţii sunt serviţi de un singur proces lansat
de evenimente, pierderea datorată schimbării contextului este minimizată,
dar procesul unic trebuie să-şi planifice intern clienţii, pentru a maximiza
concurenţa si trebuie să stie când sunt gata citirile fişierului şi când au
sosit datele de reţea.
Multe sisteme de operare oferă un apel de sistem in acest scop, numit
generic FindActive () în figura 6.2. In Unix, numele sau este select (). Dar
daca un apel de sistem gol e costisitor din cauza depăşirii limitei kernel-
aplicaţie, o implementare select() ineficientă poate fi si mai costisitoare.
Dar raman intrebări provocatoare ca structurarea reţelei şi a codului
serverului în ideea minimizarii suprasarcinii generata de planificare şi
maximizarii concurenţei. Din acest considerent, figura 6.2 arată clienţii
partiţionaţi în grupuri, fiecare din acestea fiind implementat într-un singur
proces sau thread. De remarcat ca suprasarcina generata de planificare
tuturor clienţilor într-un singur grup determina abordarea lansata-de-
evenimente, iar plasarea fiecarui client într-un grup separat determina
abordarea proces/thread -per-client.
Astfel, o implementare neoptimizată poate implica suprasarcini
considerabile datorate comutării proceselor (sute de μ s ) dacă aplicaţia şi
codul de reţea sunt prost structurate. Chiar dacă este eliminata
suprasarcina structurării-proceselor, apelurile-sistem pot costa zeci de μ s ,
iar întreruperile μ s (la o legătură Ethernet de 100 Gbps un pachet de 40
bytes poate sosi la PC la fiecare 3,2 μ s ).
Dat fiind faptul că legăturile de 10 Gbps se raspandesc, trebuie acordată
mai multă atenţie suprasarcinii generata de control. Dar, desi procesoarele
au devenit mai rapide, suprasarcina generata de control asociata cu
comutarea contextului, apelurile-sistem şi întreruperile nu s-au imbunatatit
in acelasi ritm. Unele progrese au fost făcute cu sisteme de operare mai
eficiente, ca Linux, dar progresul nu este suficient pentru a ţine pasul cu
vitezele pe legătură crescânde.
127
6.2. Evitarea suprasarcinii generata de planificare în codul de reţea.
Una din marile dificultăţi în implementarea unui protocol este de a
echilibra modularitatea (să implementezi un sistem mare pe părţi şi să faci
fiecare parte bine, independent de celelalte) şi performanţa (să faci să
funcţioneze bine intreg sistemul). Să considerăm exemplul simplu de
implementare a unei stive de reţea. “Modularitatea evidentă” ar fi
implementarea ca procese separate a protocolului de transport (TCP), a
protocolului de rutare (IP), şi a aplicaţiei. In acest caz, fiecare pachet
recepţionat ar trece prin cel puţin două comutari de context a procesului,
care sunt costisitoare.
Există totuşi alternative care permit si modularitate şi eficienţă. Fie o
aplicaţie simplă (fig.6.3) care citeşte date de la tastatură şi le trimite în
reţea, utilizând un protocol de transport. Când acestea sunt recepţionate de
un receptor din reţea sunt afişate pe ecran. Pe verticala sunt arătate diferite
nivele de protocol, sus fiind protocolul aplicaţie, apoi protocolul transport
şi jos protocolul de reţea. O implementare naiva a acestui protocol ar fi să
avem un protocol per secţiune, adica trei procese şi două schimbări
complete de context per pachet primit/trimis.
Pot fi utilizate doar două procese la transmiţător şi două la receptor (pe
verticala) pentru a implementa stiva protocoalelor de reţea. Procesele
receptorului sunt in stânga, iar procesele transmiţătorului in dreapta.
Astfel, transmiţătorul are un proces de manevrare a tastaturii/Keyboard
Handler care capteaza datele in/din tastatura, si cand are ceva date,
apeleaza transport-arm-to-send, care este o functie de nivel transport,
exportata spre procesul de manevrare a tastaturii si executata de acesta. In
acest moment, procesul de manevrare a tastaturii se poate autosuspenda
(comutarea contextului). Transport-arm-to-send spune protocolului de
transport ca acea conexiune doreste sa transmita date, dar el nu transfera
date.
Procesul transmisie-transport poate să nu transmită datele imediat, din
cauza limitărilor impuse de controlul fluxului. Dacă dispar condiţiile
impuse de controlul fluxului (datorită sosirii unor confirmări/ack),
procesul de transmisie, SendProcess va executa rutina transport-send
pentru această conexiune. Apelul send va chema mai intâi protocolul
aplicaţie care va exporta rutina display-get-data care furnizează de fapt,
aplicaţiei datele. Acest lucru este avantajos, deoarece e posibil ca aplicaţia
să fi recepţionat mai multe date de la tastatură în timp ce protocolul de
transport este pregătit pentu a trimite, şi se pot trimite deodată mai multe
date, câte incap într-un pachet. In final, în contextul aceluiaşi proces,
protocolul transport adaugă un antet de nivel de transport şi apelează
protocolul de nivel reţea pentru a transmite efectiv pachetul.
La partea de receptor, pachetul este primit de către handlerul
întreruperilor de recepţie care, folosind o rutină de reţea numită net-
dispatch, trebuie să găsească cărui proces să-i expedieze pachetul. Pentru a
afla procesul, net-dispatch apelează portul-primire-transport, care de fapt
este o rutină exportată de către nivelul transport, care citeşte numerele
portului din antet pentru a-şi da seama care aplicaţie (de ex. FTP) trebuie
să manevreze pachetul. Apoi se comută contextul şi handlerul de recepţie
128
recâştigă controlul şi atenţionează procesul de recepţie, care execută
funcţiile de reţea-transport, şi în final codul aplicaţiei pentru afişarea
datelor. De menţionat că un singur proces execută toate nivele
protocolului.

Proces de Manipulator
receptie de receptie Proces Manipulator
intreruperi transmitator tastatura

afisare-date afisare-date manipulator


primite tastatura

transport-
alocare port transport-
transport-date transmisie transport-
primite pregatit de
transmisie

retea-
expediere
retea- retea-
receptie transmisie

Intrerupere
Figura 6.3. Implementarea unui protocol utilizand apeluri de transmisie

Ideea a fost neobişnuită la acel timp din cauza prejudecăţii că nivelurile


ar trebui să folosească doar serviciile oferite de nivelurile inferioare;
apelurile între niveluri se numeau downcalls (cerute de specificaţiile
protocolului). Dar nu sunt singurele alternative pentru implementările
protocolului. In exemplul prezentat, pentru obţinerea datelor sunt folosite
apelurile de tip upcalls (apeluri pentru afişarea datelor, display-get-data),
respectiv upcalls-uri pentru avizarea nivelurilor superioare (upcalls-uri
spre transport-get-port).
Deşi upcalls-urile sunt de obicei folosite în implementări reale, probabil
nu este nici o diferenţă între upcalls-uri şi procedura standard de apel, cu
excepţia noutăţii în contextul implementării reţelei pe niveluri. Ideea mai
importantă, este a folosirii doar a unul/două procese pentru procesarea
unui mesaj, fiecare proces constând din rutine a două sau mai multe
niveluri de protocol. Ideea a fost folosită în sisteme ca x-Kernel şi în
folosirea reţelor la nivelul utilizatorului, descrisă în secţiunea următoare.
Ideea folosirii structurilor alternative de implementare, care conservă
modularitatea fără să sacrifice performanţa, este un exemplu al
principiului P8. Fiecare nivel din protocol poate fi implementat modular,
dar rutinele de tip upcall pot fi înregistrate de nivelele superioare când
sistemul porneşte.

6.2.1. Aplicarea implementării protocolului la nivel de utilizator.


Majoritatea maşinilor moderne nu implementează fiecare nivel de
protocol în procese separate. In UNIX, toate codurile de protocol
(transport, reţea, legătură de date) sunt manevrate ca parte a unui singur

129
“proces” nucleu/kernel-ul. Când un pachet soseşte prin intermediul unei
întreruperi, handlerul de întreruperi notează sosirea pachetului, posibil îl
memorează într-o coadă şi apoi planifică un proces nucleu (printr-o
întrerupere software) pentru procesarea propriu-zisă a pachetului.
Procesul nucleu execută codurile de nivel legatură de date-reţea-
transport (folosind upcalls-uri); citind numerele porturilor de transport,
procesul nucleu află aplicaţia şi o lansează. Astfel, fiecare pachet este
procesat, prin cel puţin două comutări; una de la contextul întreruperii la
procesul nucleu manevrând protocolul, şi una de la procesul nucleu la
procesul care rulează codul aplicaţiei (ex. Web, FTP).

Figura 6.4 Demultiplexarea unui pachet spre procesul destinaţie final,


folosind un proces intermediar de demultiplexare, este costisitoare

Implementarea protocoalelor la nivel-utilizatorul e arătată în procesul de


primire (fig.6.3) unde handlerul de protocol rulează în acelaşi process ca
aplicaţia şi poate comunica folosind upcalls-uri. Implementările la nivel-
utilizator pot avea două avantaje. Putem să nu folosim nucleul şi să trecem
de la handlerul de întreruperi direct către aplicaţie, economisind o
comutare de context. Codul protocolului poate fi scris şi depanat
(debugged) în spaţiul-utilizatorul (programele de depanare functionează
bine în spaţiul utilizatorului şi nu prea bine în nucleu).
Solutia extremă aparţine lui Mach, care a implementat toate protocoalele
în spaţiul utilizatorului, iar protocoalelor au fost create mai generale decât
în fig 6.3. Astfel, când un handler de întreruperi de recepţie primeşte un
pachet, nu poate spune cu uşurinţă care este procesul spre care să fie trimis
pachetul (deoarece implementarile la nivel-reţea din procesul final conţin
codul de demultiplexare). In particular, nu se poate face doar un apel-
transport pentru a examina numerele portului (ca în exemplul din fig 6.3)
deoarece putem avea o mulţime de protocoale de transport/reţea posibile.
Initial, metoda naivă (fig 6.4) implica un proces de demultiplexare
separat, care să primeasca toate pachetele şi să le examinaze determinând
procesul destinaţie final, spre care să expedieze apoi pachetul. Dar noul
proces de demultiplexare adaugă de fapt comutatorul de context lipsă.
Ideea simplă de remediere a acestei situaţii, este de a transmite o
informaţie suplimentară (P9) prin interfaţa aplicaţie-nucleu, ca fiecare
aplicaţie să poată informa ce fel de pachete doreşte să proceseze (fig. 6.5).
De exemplu, o aplicaţie de e-mail doreşte toate pachetele cu câmpul
Ethernet-IP, al căror număr de protocol IP specifică TCP, şi având
numărul portului TCP destinaţie 25 (aplicaţie de mail implementează IP,
TCP şi mail). Pentru a face aceasta, nucleul defineşte o interfaţă, care de
obicei este o formă de limbaj de programare. De exemplu, primul a fost
CSPF (CMU Stanford Packet Filter), care specifică câmpurile pachetelor
130
folosind un limbaj de programare bazat pe stive. Mai raspandit este BPF
(Berkeley Packet Filter) care foloseşte un limbaj de programare bazat pe
stive; un limbaj mai eficient este Path Finder.

Trebuie să fim atenţi la transmiterea informaţiei de la o aplicaţie la


nucleu, ca aplicaţiile greşite sau maliţioase să nu poată distruge nucleul.
Trebuie evitate acele aplicaţii care generează coduri arbitrare nucleului, ce
duc la dezastre. Din fericire, există tehnologii software care pot încapsula
codurile străine, în “sandbox”, astfel încât să poată face rău doar în spaţiul
de memorie alocat lor (sandbox-ul propriu). De exemplu, un limbaj bazat
pe stive poate fi pus să lucreze pe o stivă cu mărime specificată cu graniţe
controlate în fiecare punct. Această formă de tehnologie a culminat recent
în executarea applet-urilor Java arbitrare primite din reţea (applet-ul este
un program scris în Java, ce poate fi integrat în alt program, fiind
independent de limbajul de programare în care se integrează appletul)
Dacă pachetele sunt expediate de la handlerul de întreruperi al nucleului
(folosind o serie de filtre de pachete) către procesul de recepţie, acesta ar
trebui să implementeze stiva protocolului. Repetarea codului TCP/IP în
fiecare aplicaţie ar genera foarte mult cod redundant. Astfel, în asemenea
sisteme, TCP este implementat ca o bibliotecă partajată cu legături (este
folosită o singură copie, spre care aplicaţia are pointer, dar codul este
scris în modul reentrant, pentru a permite reutilizarea).
Aceasta nu e atât de uşor cum pare deoarece există unele stări TCP
comune tuturor conexiunilor, chiar dacă majoritatea sunt stări TCP
specifice unor conexiuni. Există şi alte probleme, deoarece ultima scriere
făcută de o aplicaţie ar trebui să fie retransmisă aplicaţiei de către TCP,
dar aplicaţia ar putea sa-şi termine procesul după ultima scriere. Oricum,
aceste probleme pot fi rezolvate. Au fost scrise implementări la nivel
utilizator cu performanţe excelente, care exploatează gradul de libertate
(P13) adică protocoalele nu trebuie implementate în nucleu.

6.3. Evitarea în aplicaţii, a suprasarcinii comutarii contextului


A fost tratata evitarea suprasarcinii dată de planificarea proceselor, la
procesarea unui singur pachet primit din reţea, prin limitarea procesării la
domeniul întreruperilor (care poate de asemenea fi eliminată/diminuată
când se prelucrează mai multe pachete) şi expedierea pachetului către
procesul final aplicaţie (care procesează pachetul). Daca procesul
destinaţie rulează în acel moment, nu va exista o supraîncărcare cauzată de
131
planificarea proceselor. Astfel, după toate optimizările, nu va apărea o
suprasarcină dată de controlul procesării pachetului, asemanator cu
procesarea unui pachet primit, cu zero copii. Dupa analiza procesării
complete a aplicaţiei, vom vedea că apar alte copii redundante, cauzate de
interacţiuni cu sistemul de fişiere.
Acum extindem analiza dincolo de procesarea unui singur pachet, pentru
a vedea cum procesează pachetele o aplicaţie. Considerăm din nou un
server Web tipic, important în practică (fig 6.2), care trebuie făcut mai
eficient. El manipuleaza un număr mare de conexiuni: un server proxy
TCP → UDP de telefonie poate manevra 100 000 de conexiuni concurente.
Cum ar trebui structurat un server Web? Inainte, trebuie să înţelegem
concurenţa posibilă dintr-un singur server Web. Chiar cu un singur CPU şi
un singur disc e posibilă concurenţa. Presupunem că într-o procesare apare
o citire a fişierului 1, care nu se află în memoria cache. Astfel CPU
initiază o citire a discului, care necesită câteva milisecunde. Dar pentru
CPU, care poate rezolva o instrucţiune aproape la fiecare nanosecundă,
timpul ar fi irosit dacă ar aştepta terminarea citirii. O posibilitate este
comutarea procesării spre un alt client, în timp ce are loc citirea discului
pentru clientul 1. Aceasta permite ca procesarea discului pentru clientul 1,
să fie suprapusă cu procesarea CPU pentru clientul 2.
Un al doilea exemplu de concurenţă CPU/dispozitiv (tipic pentru un
server Web) este suprapunerea dintre operaţiile I/O ale reţelei (realizată de
adaptor) şi CPU. De exemplu, după ce serverul acceptă o conexiune, poate
face pentru clientul 1 o citire a conexiunii acceptate. Citirea conexiunii
poate dura câteva milisecunde, deoarece clientul distant trebuie să trimita
pachetul, care trece prin reţea, şi în final e scris de adaptor pe socket-ul din
server corespunzător clientului 1.Comutarea la un alt client, face ca
procesarea reţelei pentru clientul 1, să se suprapună cu procesarea CPU
pentru alt client. La fel, când se face o scriere către reţea, scrierea poate fi
blocată din cauza lipsei de buffer-e la socket. Buffer-ul poate fi eliberat
mult mai târziu, cand sosesc confirmari ACK de la destinaţie.
Pentru creşterea eficienţei unui server Web, trebuie exploatată orice
posibilitate de concurenţă. CPU–ul serverului Web trebuie să comute între
clienţi, când un client este blocat în aşteptarea unei I/O.

6.3.1. Procese per client


Pentru un programator, cea mai simplă cale de implementare a unui
server Web este să structureze procesarea fiecărui client ca un proces
separat, adică fiecare client se află singur într-un grup separat (fig 6.2).
Planificatorul sistemului de operare comută între procese, repartizând un
proces nou CPU-ului, când procesul curent este blocat. Majoritatea
sistemelor de operare moderne pot lucra cu mai multe CPU-uri, şi să le
planifice astfel încât toate CPU-urile să aibă o activitate utilă de câte ori
este posibil. Astfel, sistemul de operare jongleaza între clienti, în contul
aplicaţiei, şi nu aplicaţia serverului Web. Dacă de exemplu, clientul 1 este
blocat în aşteptarea controlerului de disc, OS poate salva în memorie tot
contextul procesului-client 1, şi să permită procesului-client 2 să ruleze
prin restaurarea contextului său din memorie.
Această simplitate costă. Comutarea între contextul şi restaurarea
procesului este dificilă, deoarece necesită citiri/scrieri din memorie în
132
registre pentru salvarea/restaurarea contextului. Contextul include
schimbarea tabelelor de pagini care sunt folosite (deoarece tabelele de
pagini sunt per proces); astfel, fiecare translatare a memoriei virtuale,
(memorată în memoria cache TLB), trebuie incarcată în cache. Si
conţinutul memoriei cache de-date/de-instrucţiuni era cel util procesului
rezident anterior, astfel că aproape tot conţinutul acestora e inutil noului
proces. Când toate apelurile la cache eşuează, performanţa iniţială a
comutării între procese poate fi slabă.
Dar este dificilă şi naşterea unui nou proces la sosirea unui nou client,
aşa cum s-a făcut iniţial în serverele Web. Din fericire, supraîncărcarea
dată de distrugerea/crearea unui proces când un client vine/pleacă poate fi
evitată prin precalcul şi/sau ştergerea relaxată a proceselor (P2
deplasarea în timp a calcului). Când un client termină sarcina de procesare
şi conexiunea este terminată, în loc să distrugem procesul, el e returnat
stocului de procese în aşteptare. Apoi procesul poate fi alocat următorului
client nou, ca să supravegheze cererea acestuia către server.
A doua problemă este asocierea clienţilor nou sosiţi cu procesele în
aşteptare din stoc. Calea naivă trimite fiecare client nou la un proces de
asociere cunoscut, care-l asociaza cu un proces liber oarecare din stoc.
Proiectanţii sistemelor de operare au realizat importanţa asocierii si au
inventat apelurile de sistem (apelul Accept din UNIX) pentru a face
asocierea cu alt cost decat cel al comutării contextului proceselor.
Când se desfasoara un proces din stoc, el face un apel Accept şi aşteaptă
la rând într-o structură de date a kernel-ului. Când apare un nou client,
socket-ul său este trimis primului proces inactiv din rand. Astfel, kernel-ul
furnizează direct serviciile de asociere.

6.3.2. Fire de proces per client


Procesele sunt o solutie scumpă, chiar şi după eliminarea supraîncărcării
produsă de crearea la cerere a unui proces şi de asociere a proceselor.
Conexiunile lente pe arii extinse către servere sunt obişnuite şi sosirile la
un server Web cunoscut depaşeste uşor 2.000/sec, e obişnuit ca un server
Web să aibă 6000 de clienţi concurenţi serviţi simultan.
Chiar dacă procesele sunt deja create, comutarea între procese include
TLB-uri şi pierderi în cache şi necesită efort pentru salvarea şi restaurarea
contextului. In plus, fiecare proces necesită memorie pentru stocarea
contextului, care poate lua din cea necesară cache-ului de fişiere.
O solutie intermediară este folosirea firelor de proces/thread sau procese
uşoare. Ele au încredere unele în altele, cum şi trebuie în cazul în care ele
procesează clienţi diferiţi în serverele Web. Putem înlocui procesarea
fiecărui client cu un fir de proces separat per client, toate protejate de un
singur proces (fig 6.6). Firele de proces folosesc aceeaşi memorie virtuală,
deci intrările TLB nu trebuie şterse între firele de proces.
In plus, partajarea memoriei intre firele de proces implică faptul că toate
pot folosi un cache comun pentru a partaja şi translatarile numelor
fişierelor şi chiar fişierele. Pe de altă parte, cu un proces per client, adesea
cache-urile de fişiere nu pot fi partajate eficient de procese, deoarece
fiecare proces foloseşte un spaţiu separat al memoriei virtuale. Astfel,
cache-urile de aplicaţii pentru serverele Web vor suferi la capitolul

133
performanţă, deoarece fişierele comune multor clienţi sunt replicate.
Serverul Web Apache e implementat în Windows cu cate un fir per client.
Suprasarcină generata de comutarea între firele de proces e mai mică
decât comutarea intre procese, dar este totuşi considerabilă. In esenţă,
sistemul de operare trebuie încă să salveze/repună contextul per fir de
proces, ca stivele/registrele. De asemenea, memoria necesară pentru
stocarea stării per fir de proces sau per proces ia din cache-ul pentru
fişiere, care duce probabil la o rată de erori mai mare.

Fig 6.6 Două alternative simple pentru structurarea unui server Web:(1) folosirea unui
singur proces (sau fir) per client; (2) implementarea unui proces unic care foloseşte un
manager de evenimente pentru a raporta procesului starea I/O a fiecărui client.

6.3.3. Planificatorul lansat de evenimente


Daca facilităţile unui sistem de operare cu scop general sunt prea
scumpe, trebuie evitate. Daca planificarea firelor de procese, care permite
jonglarea între clienţi fără o programare ulterioară, este prea scumpă,
aplicaţia poate jongla ea însăşi.
Aplicaţia trebuie să implementeze propriul său planificator intern care să
jongleze cu stările fiecărui client. De exemplu, s-ar putea ca aplicaţia să
trebuiască să implementeze o maşină care să ţină minte că, clientul 1 este
în stadiul 2 (procesare HTTP) în timp ce clientul 2 este în stadiul 3
(aşteptând o I/O la disc), şi clientul 3 este în stadiul 4 (aşteptând un buffer
de socket liber pentru a trimite următoarea parte a răspunsului).
Kernel-ul are un avantaj faţă de un program aplicaţie, deoarece vede
toate evenimentele I/O care se încheie. Dacă într-o implementare per fire
de proces, clientul 1 este blocat aşteptând o I/O, când controlerul de disc
întrerupe CPU ca să-i spună că datele sunt acum în memorie, kernel-ul
poate să planifice firul de proces al clientului 1.
Astfel, dacă aplicaţia serverului Web îşi planifica singură clienţii,
kernel-ul trebuie să paseze informaţie (P9) către API, pentru a permite
aplicaţiei per un singur fir de proces să vadă încheierea tuturor operaţiilor
de I/O pe care le-a iniţiat. Multe sisteme de operare oferă această facilitate,
134
FindActive() (fig. 6.2). De exemplu, Windows NT 3.5 are un mecanism
IOCP (I/O completion port), de verificare a terminării I/O şi UNIX
furnizează funcţia select() de apel la sistem. Aplicaţia stă într-o buclă care
apelează funcţia FindActive(). Presupunând că tot timpul este ceva de
făcut la partea clientului, apelul va returna o listă de descriptori I/O a
activităţii respective (în exemplu datele fişierului 1 sunt acum în memorie,
conexiunea 5 a primit date). Când serverul Web procesează aceşti
descriptori activi, revine cu un alt apel FindActive().
Dacă există mereu cate un client care necesită atenţie (tipic pentru un
server aglomerat), nu trebuie să abandonam invocand costurile comutării
contextului când se jonglează între clienţi. Asemenea jonglări necesită ca
aplicaţia să aibă o maşină de stări care să-i permită să comute singură
contextul între numeroasele cereri concurente. O astfel de planificare
internă specifică aplicaţiei e mai eficientă decât apelarea planificatorului
extern de uz general, deoarece aplicaţia ştie setul minim de contexte care
trebuie salvat cand se mută de la client la client.
Serverul Zeus şi serverul original Harvest/Squid proxy cache folosesc
modelul lansat de evenimentele unui singur proces. Fig. 6.6 arată
arhitectura serverelor multiproces (şi fire de proces multiple) comparativ
cu arhitectura lansată de evenimente.

6.3.4. Serverele lansate de evenimente, cu procese ajutătoare


In principiu, un server lansat de evenimente poate să extragă tot atâta
concurenţă din fluxul de operaţii a clientilor ca şi un server multiproces
sau multifir. Dar multe sisteme de operare, ca UNIX, nu au suportul
adecvat pentru operaţii de disc care nu se blochează.
De exemplu, dacă un server lansat de evenimente vrea să fie eficient,
când dă o comandă read() pentru un fişier care nu este în cache, ar vrea ca
funcţia read() să răspunda imediat că nu este disponibil, astfel ca funcţia
read() să fie fără blocare, ceea ce ar permite serverului să se mute către
alţi clienţi. Mai târziu, când se termină operaţia de I/O a discului, aplicaţia
poate fi regăsită folosind următorul apel FindActive(). Dar dacă apelul
read() se blochează, bucla principală a serverului va aştepta milisecundele
necesare incheierii operaţiei de I/O a discului.
Dificultatea este că multe sisteme de operare ca Solaris şi UNIX permit
operaţii de read(), write() nonblocante pentru conexiunile reţelei, dar care
se pot bloca când sunt folosite pentru fişierele de pe disc. Aceste sisteme
de operare permit alte apeluri sistem asincrone pentru I/O cu discul, dar
care nu sunt integrate în apelul select() (echivalentul în UNIX a
FindActive() ). In asemenea sisteme de operare trebuie să se aleagă între
pierderea concurenţei, prin blocarea operatiei de I/O a discului, şi trecerea
dincolo de modelul cu un singur proces.
Serverul de Web Flash merge dincolo de modelul cu un singur proces
pentru maximizarea concurenţei. Când trebuie citit un fişier, procesul
principal de pe server întâi testează dacă fişierul este în memorie, folosind
fie un apel standard de sistem, fie blocand paginile fişierelor cache, pentru
ca procesele serverului să ştie întotdeauna care fişiere sunt în cache. Daca
fişierul nu este în memorie procesul principal din server instruieşte un
proces ajutător/helper pentru a face citirea de disc, posibil blocantă. Când

135
procesul ajutător a terminat, comunică cu procesul principal de pe server,
printr-o formă de comunicare interprocese, precum pipe.
Spre deosebire de modelul mutiproces, procesele ajutătoare sunt
necesare doar pentru fiecare operaţie concurentă pe disc, şi nu pentru
fiecare cerere client concurentă. Modelul exploatează un grad de libertate
(P13), observând că mai sunt şi alte alternative interesante între un singur
proces şi un proces per client.
In afara de citirea fişierelor, procesele helper mai pot fi folosite la căutări
în directoare pentru localizarea fişierelor pe disc. In timp ce Flash menţine
un cache, care face legătura între numele căilor directoarelor şi fişierele de
pe disc, dacă există cumva o greşeală a cache-ului, atunci trebuie căutat
prin structurile directoarelor de pe disc. Deoarece asemenea căutari de
directoare se pot bloca, sunt de asemenea necesare proceselor helper.
Crescând cache-ul pentru numele de căi, creşte de asemenea consumul de
memorie, dar reducerea ratei de erori a cache-ului poate reduce numărul
de procese ajutătoare necesare şi să scadă necesarul global de memorie.
Procesele helper ar trebui să evite întârzierea creării unui proces, de
fiecare dată când un process helper este invocat. Câte procese helper
trebuie să fie? Prea puţine cauzează pierderi de concurenţă şi prea multe
cauzează pierderi de memorie. Solutia în Flash este crearea dinamică şi
distrugerea proceselor helper în funcţie de încarcare.

6.3.5 Structurarea bazată pe sarcini/task-uri


In figura 6.7 sus, e descrisa abordarea lansata-de-evenimente îmbogăţită
cu procese helper. E similara cu manevrarea simplă lansata de evenimente,
figura 6.6 jos, cu exceptia proceselor helper. Există câteva probleme la
arhitectura lansată de evenimente cu procese helper.
• Complexitatea: proiectantul aplicaţiei trebuie să ştie să folosească
maşina de stări pentru manevrarea cererilor clientului fără ajutor.
•Modularitate: codul pentru server e scris ca un singur modul.
Serverele Web sunt foarte răspîndite, dar sunt şi multe servicii Web care
pot utiliza părţile de cod similare (ca de exemplu cele pentru acceptarea
conexiunilor). Abordarea şi mai modularizată permite reutilizarea codului.
•Supraîncărcarea cauzata de control: producătorii serverelor Web
trebuie sa facă faţă variaţiilor considerabile a sarcinilor provenite de la
numărul imens de clienţi, fiind esential ca serverul să-şi continue
activitatea în suprasarcina (fără să piarda date), tratând corect toţi clienţii.
Ideea principală în arhitectura multinivel a proceselor acţionate de
evenimente este exploatarea gradului de libertate (P13) în descompunerea
codului. În locul descompunerii orizontale de către client în fire de
execuţie, ca la arhitectura cu fire multiple, sistemul serverului este
descompus vertical de către sarcini, la fiecare ciclu de cerere a clientului
(fig. 6.6, jos). Fiecare nivel poate fi controlat de unul sau mai multe fire.
Astfel, modelul pe niveluri poate fi considerat o rafinare a modelului
simplu a proceselor lansate de evenimente, deoarece el atribuie un fir
principal şi un fir posibil fiecărui nivel de procesare a serverului. Apoi,
nivelurile comunică prin cozi de aşteptare şi, la fiecare etaj, poate fi făcut
un control mai rafinat al supraîncărcării.

136
Fig. 6.7 Două propuneri de arhitecturi Web ( diferite de cele din fig. 6.6) : (3) procese
lansate de evenimene (event-driven) si procese ajutatoare; (4) arhitectura pe niveluri a
proceselor lansate de evenimente

6.4 Selecţia rapidă


Pentru a justifica problema selecţiei rapide, se prezintă o problemă de
performanţă din literatură, apoi e descrisa utilizarea şi implementarea in
UNIX a apelului select(), se face o analiză a supraîncărcării, şi se aplică
principiile de implementare sugerând diverse imbunătăţiri. E descrisă o
îmbunătăţire, în ideea că API nu poate fi schimbat si se propune o soluţie
mai bună care implică însă multe schimbări ale API.

6.4.1 Probleme ale serverului


Evitarea supraîncărcării generată de planificarea proceselor este o
problemă importantă într-un server Web. De exemplu, un server „event-
driven” reduce complet supraîncarcarea dată de planificarea proceselor prin
utilizarea unui singur fir pentru toţi clienţii, după care se utilizează un apel
FindActive() la fel cu select(). Acum, proxy-ul Web CERN utilizează un
proces per client, iar serverul Web Squid utilizează o implementare „event-
driven”. Măsurătorile făcute într-un mediu LAN au arătat că proxy-ul Web
Squid e mai performant decât serverul CERN. Testele într-un mediu WAN
au arătat că aici nu există nici o diferenţă între performanţa serverelor
CERN şi Squid. De ce ?
Problema a fost elucidată dupa ce s-a remarcat că la acelaşi trafic(ca
număr de conexiuni pe secundă) cu cât întârzierea tur-retur dintr-un mediu
WAN e mai mare, cu atât e mai mare numărul de conexiuni concurente din
WAN. Fie un mediu WAN cu un timp mediu de 2 secunde per conexiune şi
un server Web cu traficul de 3000 conexiuni pe secundă. Legea lui Little
(teoria cozilor de aşteptare) spune că numărul mediu de conexiuni simultane
este dat de produsul acestora, adică 6000.

137
Dar într-un mediu LAN, cu ciclul de întârziere de 2 msec, numărul mediu
de conexiuni simultane scade la 6. Dacă numărul de conexiuni pe secundă
rămâne la fel, în WAN o mare parte din conexiuni trebuie să fie în starea
„idle”(aşteptând răspunsuri) în orice moment.
Două cauze principale de supraîncărcare au fost apelurile de sistem
folosite de către serverul event-driven:
• select(): serverele event-driven rulând pe UNIX utilizează apelul de
select() pentru apelul FindActive(). Experimentele făcute arată că mai mult
de jumătate din CPU este utilizat pentru kernel şi funcţii nivel-utilizator
select() cu 500 conexiuni;
• ufalloc(): de asemenea, serverul are nevoie să repartizeze cel mai mic
descriptor nealocat pentru socket-uri sau fişiere noi. Acest apel simplu
consumă a treia parte din timpul CPU.
Performanţele ufalloc() pot fi uşor explicate. In mod normal, găsind un
descriptor liber, acesta poate fi uşor implementat utilizând o lista liberă de
descriptori. Din păcate, UNIX cere alegerea celui mai mic descriptor
neutilizat. De exemplu, daca lista curentă de descriptori nealocaţi are
elementele (nesortate) 9, 1, 5, 4, 2, atunci nu poate fi găsit 3, ca fiind cel mai
mic număr nealocat, fără parcurgerea întregii liste nesortate. Din fericire, o
simplă schimbare în implementarea kernel (P15 utilizarea unor structuri de
date eficiente) poate reduce aproape la zero suprasarcina.

6.4.2 Utilizarea select() şi implementarea existentă


Presupunând ca supraîncarcarea generată de ufalloc() poate fi
minimizată uşor prin schimbarea implementării kernel-ului, este important
să ameliorăm strangulările rămase, cauzate de implementarea select() într-
un server event-driven. Deoarece cauzele problemei sunt mai complexe,
sunt revăzute utilizarea si implementarea select() pentru a înţelege
diferitele surse de supraîncarcare.
Parametri
Select() este numită:
-Input (de intrare): o aplicaţie cheamă select() cu trei bitmap-uri de
descriptori (unul din care se citeste, unul în care se scrie şi unul din care se
extrag excepţiile) şi o valoare de timeout.
-Interim (provizorie): aplicaţia e blocată dacă nu e nici un descriptor
pregătit.
-Output (de iesire): când apare ceva de interes, apelul returnează
numărul descriptorilor pregătiţi (transmis ca valoare de întreg) şi lista
specifică a descriptorilor fiecărei categorii (transmisă ca referinţă, prin
supraînscrierea bitmap-urilor de intrare).
Utilizarea într-un Server Web
După parametrii apelului select(), este important să înţelegem cum poate
fi acesta utilizat de către un server Web lansat de evenimente. Un exemplu
de utilizare este prezentat în cele ce urmează. Firele aplicaţiei serverului
stau într-o buclă cu trei componente majore:
•Iniţializare: primele zero-uri ale aplicaţiei sunt scoase în afara
bitmap-urilor şi se setează bitii pentru descriptorii de interes în vederea
scrierii/citirii. Aplicaţia serverului ar putea dori să citească din descriptorii
fişierului şi să scrie/citească socket-urile reţelei deschise clienţilor.

138
•Apel: apoi aplicaţia apelează select() cu bitmap-urile construite la
pasul anterior, şi se blochează dacă nici un descriptor nu e pregătit în
momentul apelului; dacă expiră timpul (timeout), aplicaţia face o
procesare de excepţie.
•Răspuns: după ce apelul revine, aplicaţia trece liniar prin bitmap-
urile returnate şi invocă antetele de citire şi scriere adecvate, pentru
descriptorii corespunzători poziţiilor cu biţi setaţi.
Costurile construirii bitmap-urilor, din pasul 1 şi scanării lor din pasul 3,
sunt suportate de către utilizator, deşi ele sunt atribuibile direct costurilor
de pregătire a răspunsului la apelul select().
Implementare
Este important să înţelegem implementarea select() în kernel-ul unei
variante tipice de UNIX. Kernel-ul face următoarele (adnotat cu surse de
supraîncărcare):
•Curăţare: kernel-ul începe cu folosirea bitmap-urilor, transmise ca
parametri, pentru a construi un sumar cu descriptorii marcaţi în cel puţin
unul din bitmap-uri (numit setul selectat).
Acest lucru necesită o căutare liniară printre bitmap-urile de dimensiune N, indiferent
de numărul de descriptori de care aplicaţia este interesată.
•Verificare: apoi, pentru fiecare descriptor din setul selectat, kernel-ul
verifică dacă descriptorul este pregătit; dacă nu, încarcă ID-ul firului
aplicaţiei în coada de aşteptare selectată a descriptorului. Kernel-ul pune
în adormire firul aplicaţiei apelante, dacă nu există descriptori pregătiţi.
Acest lucru necesită investigarea tuturor descriptorilor selectaţi, indiferent de câţi sunt
pregătiţi de fapt. Acest pas este mai costisitor decât simpla scanare a bitmap-ului.
•Reluare: când descriptorul devine pregătit din cauza unei operaţii de
I/O (de exemplu, un pachet soseşte la un socket, de la care serverul
aşteaptă date), modulul de I/O al kernel-ului îşi verifică coada de aşteptare
selectată şi „trezeşte” toate firele care aşteaptă un descriptor.
Acest lucru implică o supraîncărcare cauzată de planificare, care pare inevitabilă fără
un apel selectiv sau aşteptare de ocupat.
•Redescoperire: in final, select() redescoperă lista descriptorilor
pregătiţi făcând o scanare a tuturor descriptorilor selectaţi pentru a vedea
care a devenit pregătit în timpul în care select() fusese pus în adormire şi
„trezit” apoi. Aceasta necesită repetarea aceloraşi verificări costisitoare de
la pasul 2.
Verificările se repetă deşi modulul de I/O ştie care descriptori devenisera pregătiţi, dar
nu informează implementarea select().

6.4.3 Analiza operaţiei select()


Se descriu posibilităţile de optimizare a implementării operaţiei select()
existente, şi apoi se sugereaza strategii de îmbunătăţire a performanţei
folosind principiile.
Risipa evidenta în implementarea select()
Principiul P1 urmăreşte înlăturarea risipei evidente, dar pentru aplicarea
lui trebuie găsite sursele risipei evidente în implementarea select(). Pentru
fiecare sursă de risipă, căutăm o cauza.
1.Recreerea interesului pentru fiecare apel: acelaşi bitmap este folosit
pentru intrare şi ieşire. Aceasta supraîncărcare determină aplicaţia să
reconstruiască bitmap-urile din zona de lucru (scratch), deşi poate o
interesează în majoritate aceiaşi descriptori la apelurile consecutive spre

139
select().De exemplu, dacă doar 10 biţi se schimbă la fiecare apel într-un
bitmap de dimensiune 6000, aplicaţia tot trebuie să treacă prin cei 6000
biţi pentru a seta fiecare bit dacă e nevoie.
Trebuie învinuită fie interfaţa API, fie lipsa calculului incremental in aplicaţie.
2.Reverificarea stării după fiecare reluare: nici o informaţie nu trece, de
la modulul protocolului (care trezeşte un fir adormit de pe socket) spre
apelul select() invocat când firul îşi reia activitatea. De exemplu, dacă
modulul TCP primeşte date pe socket-ul 9, pe care doarme firul 1,
modulul TCP se va asigura mai întâi ca firul 1 s-a trezit; dar nici o
informaţie nu-i e dată firului 1 la trezire. Astfel firul 1 trebuie să reverifice
toate socket-urile selectate pentru a stabili dacă socket-ul 9 are într-adevar
date, deşi modulul TCP ştia sigur acest lucru când l-a trezit.
Trebuie învinuită implementarea kernel-ului.
3.Kernel-ul reverifică disponibilitatea descriptorilor anterior
nepregătiţi: aplicaţia server-ului Web e interesată de un socket până la
eşecul/sfârşitul conexiunii. Atunci de ce se repetă testele pentru
disponibilitate dacă nu s-a observat nici o schimbare a stării? Presupunem
de exemplu, că socket-ul 9 este o conexiune spre un client distant, cu o
întârziere de 1 sec pentru trimiterea/ recepţia pachetelor. Presupunem că la
timpul t este trimisă o cerere la un client prin socket-ul 9 şi serverul
aşteaptă răspuns, care soseşte la t+1 secunde. Presupunem că în intervalul
dintre t şi t+1, firul serverului apelează select() de 15000 ori. De fiecare
dată când e apelată select(), kernel-ul face o verificare laborioasă a socket-
ului 9 pentru a determina dacă au sosit date sau nu. In schimb, kernel-ul
poate deduce aceasta, din simplul fapt ca socket-ul era verificat la timpul t
şi nici un pachet din reţea nu a fost recepţionat de la acest socket de la
momentul t. Astfel 15000 de verificări nefolositoare şi costisitoare pot fi
evitate; când pachetul soseşte în sfarsit, la timpul t+1, modulul TCP poate
transmite informaţia pentru a reporni verificarea acestui socket.
Trebuie învinuită implementarea kernel-ului
4.Bitmap-uri liniari în raport cu dimensiunea descriptorului: Atât
kernel-ul cât şi utilizatorul trebuie să scaneze bitmap-urile, proporţional cu
dimensiunea descriptorilor posibili, şi nu cu volumul de muncă utilă. De
exemplu, dacă sunt 6000 de descriptori posibili, un server Web ar trebui să
trateze un vârf de sarcină, bitmap-uri cu lungimea 6000. Presupunem că în
timpul unei perioade oarecare sunt 100 de clienţi concurenţi, din care doar
10 sunt pregătiţi în timpul fiecărui apel select(). Atât kernel-ul cât şi
aplicaţia scanează şi copiază bitmap-uri de dimensiune 6000, deşi
aplicaţia este interesată doar de 200 de biţi si doar 10 biţi sunt setaţi la
revenirea fiecărui select().
Trebuie învinuită API.
Strategii şi principii pentru a repara select()
Odată stabilite cauzele risipei, pot fi aplicate strategii simple, utilizând
principiile algoritmice:
•Recrearea interesului pentru fiecare apel: Consideră schimbarea API-
ului (P9) pentru a utiliza bitmap-uri separate pentru intrare şi ieşire. Sau
menţine API-ul şi foloseşte calculul incremental (P12a).
•Reverificarea stării după fiecare reluare: Pasează informaţia între
modulele protocolului care ştiu când un descriptor este pregătit şi modulul
selectat (P9).

140
•Să ai reverificări kernel a disponibilităţii descriptorilor ştiuţi a fi
nepregătiţi. Kernel îşi menţine starea în timpul apelurilor, asfel încât nu
trebuie să reverifice descriptorii ştiuţi a fi nepregătiţi (P12a, utilizează
calculul incremental)
•Să foloseşti bitmap-uri liniari în raport cu mărimea posibilă şi nu cu
mărimea descriptorului: Schimbă fundamental API-ul pentru a evita
necesitatea interogării asupra stării tuturor descriptorilor reprezentaţi de
bitmap-uri (P9).

6.4.4 Accelerarea select() fără schimbarea API


Vom vedea cum să eliminam primele trei, din patru, cauze de risipă,
prezentate anterior.
1.Evitarea reconstruirii bitmap-urilor din scratch-uri: Codul aplicaţiei
este schimbat pentru a folosi doua bitmap-uri de descriptori, de care este el
interesat. Bitmap-ul A este folosit pentru memorarea pe termen lung, şi
bitmap-ul B este folosit ca parametru curent transmis ca referinţă la
select(). Astfel în bitmap-ul A, dintre apelurile spre select(), doar câţiva
descriptori care s-au schimbat trebuie să fie actualizaţi. Inainte de a apela
select(), bitmap-ul A este copiat în bitmap-ul B. Deoarece copierea se face
per cuvânt este mai eficientă decât inspecţia laborioasă bit cu bit a bitmap-
ului. In esenţă, noul bitmap este calculat incremental (P12a).
2. Evitarea reverificărilor tuturor descriptorilor când select() se
activeaza: Pentru a evita aceasta supraîncărcare, implementarea kernel-
ului este modificată astfel încât fiecare fir păstrează un set de indicaţii H,
care înregistrează socket-urile care au devenit pregătite de la ultima dată
când firul a apelat select(). Protocolul sau modulele de I/O sunt
modificate astfel încât atunci când sosesc date noi (pachete din reţea,
terminarea unor operaţii de I/O cu discul), indexul descriptorului
corespunzător este scris în setul de indicaţii al tuturor firelor, aflate în
coada de aşteptare selectată pentru acel descriptor. In sfârşit, după ce un fir
se trezeşte în select(), doar descriptorii din H sunt verificaţi. Esenţa acestei
optimizări este pasarea de indicaţii interfeţelor dintre niveluri (P9).
3. Evitarea reverificării descriptorilor ştiuţi a fi nepregătiţi: Observaţia
fundamentală este aceea că un descriptor care aşteaptă date nu trebuie să
fie verificat până nu apare o notificare asincronă (de exemplu, descriptorul
este plasat în setul de indicaţii H descris anterior). Este clar că, orice
descriptori proaspăt sosiţi (de exemplu, socket-uri nou deschise) trebuie
verificaţi. A treia idee mai subtilă este ca aplicaţia poate citi doar 200
bytes, chiar şi după ce datele din reţea au sosit la un socket (de exemplu,
1500 bytes). Astfel, un descriptor trebuie verificat pentru disponibilitate,
chiar după sosirea primei date, până când nu mai rămân date (de exemplu,
aplicaţia citeşte toate datele) pentru a declara disponibilitatea.
Pentru a implementa aceste idei, pe lângă setul de indicaţii H,
implementarea kernel-ului mai păstrează încă două seturi. Primul este
setul de interes I, cu toţi descriptorii de care firul este interesat. Al doilea
este un set de descriptori R cunoscuţi a fi pregătiţi. Setul de interes I
reflectă interesul pe termen lung; de exemplu, un socket este plasat în I
când este menţionat prima dată într-un apel select() şi este înlăturat doar
când socket-ul este deconectat sau refolosit. Să notăm cu S setul transmis

141
la select(). Atunci I este actualizat la Inou=Ivechi ∪ S. Acesta incorporează
descriptorii proaspăt selectaţi fără a pierde descriptorii anterior selectaţi.
Apoi, kernel-ul verifică doar descriptorii care sunt în Inou , dar sunt fie:
(i) în setul de indicaţii H fie (ii) nu sunt în Ivechi sau (iii) în setul vechi de
descriptori pregătiţi Rvechi. Aceste trei afirmaţii reflectă cele trei categorii
discutate în urmă cu două paragrafe. Ele reprezintă fie activitatea recentă a
fiecăruia, interesul proaspăt declarat sau datele neconsumate rezultate
dintr-o activitate prioritară. Descriptorii găsiţi a fi pregătiţi în urma
verificării sunt înregistraţi în Rnou. La sfârşit, apelul select() returnează
utilizatorului elementele din Rnou ∩ S. Acest lucru se întâmplă deoarece pe
utilizator îl interesează doar disponibilitatea descriptorilor specificaţi în
setul selectat S.
Ca exemplu, socket-ul 15 poate fi verificat când e menţionat pentru
prima dată în apelul select() şi deci intră în I; socket-ul 15 poate fi verificat
apoi, când un pachet de 500 bytes soseşte din reţea, ceea ce face ca socket-
ul 15 să intre în H; în final, socket-ul 15 poate fi verificat repetat ca parte a
lui R până când aplicaţia consumă toţi cei 500 bytes, punct în care socket-
ul 15 părăseşte R (P12,suplimentarea stărilor pentru a creşte viteza) stă la
baza acestei optimizări. Optimizarea menţine starea peste apeluri (P12)
pentru a reduce verificările redundante.

6.4.5. Accelerarea procedurii select( ) schimbând API


Tehnica descrisă îmbunătăţeşte considerabil performanţa prin eliminarea
din select() a primelor trei surse (principale) de supraîncărcare. Oricum,
funcţionează asa, prin păstrarea unei stări suplimentare (P12) sub forma a
încă trei seturi de descriptori (H, I si R) care sunt menţinuţi, de asemenea
ca şi bitmap-uri. Aceasta, împreună cu setul selectat S transmis la fiecare
apel, necesită scanarea şi actualizarea celor patru bitmap-uri, separat.
Situaţia în care sunt prezente un număr mare de conexiuni, dar doar
câteva sunt active la un moment dat, implică o mică supraîncărcare,
proporţională cu numărul total de conexiuni şi nu cu numărul de
conexiuni active. Aceasta este a patra sursă de “risipă” enumerată anterior
şi pare inevitabilă cu API-ul existent.
Mai mult, precum am văzut anterior, chiar şi procedura select() rapidă
modificată verifică un descriptor de mai multe ori pentru fiecare
eveniment, ca sosirea unui pachet (dacă aplicaţia nu consumă toate datele
odată). Asemenea controale adiţionale sunt inevitabile deoarece select()
furnizează starea fiecărui descriptor.
Dacă ne uităm mai îndeaproape la interfaţă, ceea ce aplicaţia cere în
esenţă, este să fie informată de fluxul evenimentelor (terminarea unei I/O,
sosirea unui pachet de reţea) care-i schimbă starea. Notificările lansate de
evenimente apar pentru a putea înlătura neajunsuri evidente ce rezultă din
utilizarea anterioară.
• Notificare asincronă: dacă aplicaţia este anunţată imediat ce un
eveniment apare, ar apărea încărcări suplimentare, şi ar fi dificil de
programat. De exemplu, atunci când o aplicaţie serveşte socket-ul 5, un
pachet ar putea sosi la socket-ul 12. Intreruperea aplicaţiei, pentru a o
informa de noul pachet, poate fi o idee proastă.

142
• Rata excesivă a evenimentelor: aplicaţia e interesată de evenimentele
care cauzează schimbarea stării, nu de fluxul lor neprelucrat. Pentru un
transfer Web mare, mai multe pachete pot ajunge la un socket şi aplicaţia
ar dori să obţină o notificare pentru întregul lot, nu câte una pentru fiecare
pachet. Suprasarcina generată de fiecare notificare este echivalentă cu
memorarea fiecărei notificări, în termeni de costuri de comunicaţie(CPU).
Principiul P6 sugerează proiectarea rutinelor specializate eficiente
pentru a evita strangulările.
● Interogarea sincronă: ca şi în cazul apelului select() original,
aplicaţia poate interoga evenimentele în curs. Spre exemplu, în exemplul
anterior, aplicaţia continuă să servească socket-ul 5 şi toate celelalte
socket-uri active înainte să întrebe (şi să i se spună despre) evenimente ca
de pildă sosirea pachetului la socket-ul 5.
● Comasarea evenimentelor: dacă, în timp ce un prim eveniment a
fost introdus într-un şir pentru notificare, apare un al doilea eveniment
pentru descriptor, cea de-a doua notificare este omisă. Astfel per
descriptor, acolo poate fi cel mult o notificare de eveniment important.
Utilizarea noului API este directă dar nu prea conformă cu stilul
aplicaţiilor ce foloseau vechiul select() API. Aplicaţia stă într-o buclă, în
care are nevoie de sincronizare pentru setul următor de evenimente, şi
intră în repaus în caz contrar. Când apelul revine, aplicaţia trece prin
fiecare notificare de eveniment şi invocă antetele potrivite de scriere sau
citire. Implicit, setarea registrelor unei conexiuni interesează descriptorul
corespunzător, în timp ce deconectarea conexiunii înlătură descriptorul din
lista de interes.
Implementarea: fiecărui fir îi este asociat un set de descriptori de care
este interesat. Fiecare descriptor (socket de exemplu) păstrează o lista de
mapare inversa a tuturor firelor interesate de descriptor. In activitatea de
I/O (de exemplu, sosirea datelor la un socket), modulul I/O utilizează
propria sa lista de mapare inversă, pentru a identifica toate firele posibil
interesate. Daca descriptorul este în setul de fire interesat, un eveniment de
notificare este adăugat şirului evenimentelor în curs al acelui fir.
Un bitmap per fir simplu, un bit per descriptor, este utilizat sa
înregistreze faptul că un eveniment este în aşteptare în şir şi să evite
multiplele notificări de evenimente per descriptor. In final, când aplicaţia
cere următorul set de evenimente, acestea sunt returnate din şirul de
aşteptare.

6.5 Evitarea apelurilor de sistem


Omitem discuţia referitoare la select(), şi reluăm discuţia despre lucrul
cu reţeaua la nivelul utilizatorului. Se pare că am eliminat kernel-ul din
tablou prin recepţia şi trimiterea unui pachet, dar din păcate nu e aşa. Când
o aplicaţie vrea să trimită date, trebuie cumva sa anunţe adaptorul unde se
află acele date.
Când o aplicaţie vrea să primească date, ea trebuie sa specifice buffer-
ele unde se vor scrie pachetele de date primite. In Unix acest lucru se face
utilizând apelurile de sistem, în care aplicaţia comunica kernel-ului despre
datele pe care ar vrea să le trimită şi buffer-ele pe care ar vrea să le
primească. Chiar dacă implementăm protocolul în spaţiul utilizatorului,

143
kernel-ul trebuie să servească aceste apeluri de sistem (care pot fi
costisitoare) pentru fiecare pachet trimis sau primit.
Acest lucru pare a fi necesar deoarece pot fi câteva aplicaţii care primesc
şi trimit date de la un adaptor comun; din moment ce adaptorul este o
resursă partajată, este de neconceput pentru o aplicaţie să scrie direct în
registrele dispozitivului unui adaptor al reţelei fără ca kernel-ul să verifice
utilizarea eronată sau răuvoitoare.

Sunt posibile şi alte soluţii. In figura 6.8 vedem că dacă o aplicaţie


vrea să seteze valoarea unei variabile X la 10, nu face de fapt o chemare a
kernel-ului. Dacă ar trebui să facă asta, atunci fiecare citire si scriere s-ar
face foarte lent. In loc de asta, hardware-ul găseşte pagina virtuală a lui X,
o translatează într-o pagină fizică (sa zicem 10) prin intermediul TLB, şi
apoi permite accesul direct la ea atâta timp cât aplicaţia are pagina 10
mapată în memoria sa virtuală.
Dacă pagina 10 nu este mapată în memoria virtuală a aplicaţiei,
hardware-ul generează o excepţie, care determină kernel-ul să intervină,
stabilind de ce este un viol al accessului la pagina acolo. Kernel-ul a fost
implicat în setarea memoriei virtuale pentru aplicaţie (doar lui i se permite
sa facă asta, din motive de securitate) şi poate fi implicat dacă aplicaţia
violează accesul la paginile sale setate de kernel. De altfel, kernel-ul nu
este implicat în orice acces. Putem spera la o abordare similară în cazul
accesului aplicaţiei la memoria adaptorului, pentru a evita apeluri inutile la
sistem (P1) ?
Pentru a vedea dacă acest lucru este posibil trebuie să examinăm mai
atent ce informaţie trimite şi receptionează o aplicaţie, de la un adaptor.
Sigur că trebuie să prevenim ca aplicaţii incorecte sau rău intenţionate să
defecteze alte aplicaţii sau chiar kernel-ul însuşi. Figura 6.9 arată o
aplicaţie care doreşte să primească date direct de la adaptor. Tipic, o
aplicaţie care face asta trebuie să puna în coada de aşteptare un descriptor.
Un descriptor este o bucată mică de informaţie care descrie buffer-ul din
memoria principală, unde ar trebui înscrise datele din pachetul următor

144
(pentru această aplicaţie). In consecinţă ar trebui să considerăm cu atenţie
şi separat memoria descriptorului şi memoria actuală a buffer-ului.
Putem lucra uşor cu memoria descriptorului, ştiind că memoria
adaptorului este o memorie mapată. Presupunem că adaptorul are 10.000
bytes de memorie, considerată memorie pe magistrală, si că pagina fizică a
sistemului este de 1000 bytes. Rezultă faptul că adaptorul are 10 pagini
fizice. Presupunem că alocăm câte 2 pagini fizice fiecărei aplicaţii dintr-un
set de 5 aplicaţii de mare performanţă (ex.Web, FTP), aplicaţii care vor să
folosească adaptorul pentru a transfera date. Presupunem că aplicaţia Web
primeşte 2 pagini fizice, de exemplu nr.9 si 10. Kernel-ul mapează
paginile fizice 9 şi 10 la tabela de pagini a aplicaţiei Web, iar paginile
fizice 3 şi 4 le mapează la tabela de pagini a aplicaţiei FTP.
Acum, aplicaţia Web poate scrie direct în paginile fizice 9 şi 10, fără nici
un pericol; dacă va încerca să scrie în paginile 3 şi 4, hardware-ul
memoriei virtuale va genera o excepţie. Astfel (P4c) exploatăm hardware-
ul existent sub forma TLB, pentru a proteja accesul la pagini. Presupunem
acum că pagina 10 este o secvenţă de descriptori de buffer-e libere, scrise
de aplicaţia Web; fiecare descriptor de buffer descrie o pagină din
memoria principală (presupunem că avem la dispoziţie doar 32 biţi), care
va fi folosită pentru a recepţiona următorul pachet descris pentru aplicaţia
Web.Spre exemplu, pagina 10 poate conţine secvenţa 18, 12 (fig.6.9).
Aceasta înseamnă că aplicaţia Web are în coada curentă paginile fizice cu
numărul 18 şi 12 pentru recepţionarea următoarelor pachete. Presupunem
că paginile 18 şi 12 se află în memoria principală şi sunt pagini fizice
blocate, atribuite aplicaţiei Web de către kernel, la pornirea aplicaţiei
Web.
In momentul în care soseşte un nou pachet pentru aplicaţia Web,
adaptorul va demultiplexa pachetul către pagina 10, folosind un filtru de
pachete, iar apoi va scrie datele din pachet (folosind DMA) în pagina 18.
După ce a realizat aceasta, adaptorul va scrie descriptorul 18 într-o pagină
dedicată descriptorilor de pagini scrise (exact ca la fbufs), spre exemplu
pagina nr.9, pagina pe care aplicaţia Web este autorizată să o citească.
Este apoi sarcina aplicaţiei Web să termine procesarea paginilor scrise, şi
să pună periodic în coada adaptorului noi descriptori de buffer-e libere.
Totul pare frumos, dar există o problemă de securitate. Presupunem că
printr-o eroare/rea intenţie, aplicaţia Web scrie în pagina descriptorilor
secvenţa 155, 120 (lucru pe care-l poate face). Presupunem în continuare
ca pagina 155 este în memoria principală, şi că în această pagină kernel-ul
işi stochează structurile de date. Cand adaptorul primeşte un nou pachet
pentru aplicaţia Web, va scrie datele pachetului în pagina 155,
suprascriind structurile de date ale kernel-ului. Aceasta va cauza probleme
serioase, cel putin eşuarea sistemului.
Dar de ce nu poate detecta hardware-ul memoriei virtuale acest tip de
problemă? Motivul este ca hardware-ul memoriei virtuale (observaţi
pozitia TLB în fig.6.8) oferă protecţie doar împotriva accesului neautorizat
a proceselor care rulează pe CPU. TLB interceptează fiecare acces de tip
READ (sau WRITE) făcut de către procesor şi poate face verificari; însă
adaptoarele care fac acces de tip DMA, ocolesc sistemul memoriei virtuale
şi accesează memoria direct.

145
In practică, acest lucru nu reprezintă o problemă deoarece aplicaţiile nu
pot programa dispozitivele (precum discuri, adaptoare) să citească sau să
scrie la o anumită locaţie, la comanda aplicaţiei. Insă, accesul este mediat
mereu de către kernel. Dacă nu implicăm şi kernel-ul, trebuie să ne
asigurăm că instrucţiile pe care aplicaţia le-ar putea da adaptorului au fost
atent verificate.
Soluţia folosită la canalul dispozitivului aplicaţie ADC (application
device channel), este ca (P9, pasează indicaţii la interfaţă) kernel-ul să
trimită adaptorului o listă de pagini fizice valide, pe care fiecare aplicaţie
care foloseşte adaptorul, să le poată accesa direct. Acest lucru poate fi
făcut doar după ce aplicaţia a pornit şi înainte de a începe transferul de
date. Cu alte cuvinte, calculul implicat în autorizarea paginilor este decalat
în timp (P2), de la faza transferului de date la faza de iniţializare a
aplicaţiei. Spre exemplu, în momentul în care începe aplicaţia Web,
aplicaţia va cere kernel-ului 2 pagini, de exemplu 18 şi 12 iar apoi va cere
kernel-ului să autorizeze utilizarea acestor pagini de către adaptor.
Kernel-ul este apoi transpus în regim normal de operare de date. Daca în
acest moment aplicaţia Web pune în coadă descriptorul 155 şi apare un
nou pachet, adaptorul va verifica mai întâi dacă numarul 155 este în lista
autorizată a aplicaţiei (care conţine 18 şi 15). Deoarece 155 nu este în
listă, adaptorul nu va suprascrie structurile de date ale kernel-ului.
In concluzie, ADC-urile deplasează în spaţiu funcţiile de protecţie (P3c)
de la kernel la adaptor, folosind informaţii prelucrate anterior (lista
paginilor fizice autorizate, P2a) trimise de la kernel către adaptor (P9) şi
la care s-a adăugat hardware-ul memoriei virtuale (P4c).
In ultimii ani a fost promovată utilizarea mesajelor active pentru scopuri
similare. Mesajul activ conţine adresa procesului de nivel utilizator care va
manevra pachetul.Un mesaj activ evita intervenţia kernel-ului şi stocarea
temporară în tampoane, prin folosirea tampoanelor prealocate sau a
mesajelor de dimensiune mică, răspunsuri directe ale aplicaţiei,
ajungându-se astfel la o latenţă mică. Latenţa mică permite o suprapunere
a calculului şi comunicaţiei în maşinile paralele. Implementarea cu mesaje
active permite doar transferul mesajelor scurte sau a blocurilor mari.
Implemetarea mesajelor rapide merge mai departe şi combină interfeţele
de dispersie-regrupare la nivel-utilizator cu controlul fluxului, pentru a
permite o performanţă ridicată uniformă, atât pentru mesajele scurte cât şi
pentru cele lungi.

La ce folosesc kernelurile?
Problema este importantă deoarece ADC-urile şi mesajele active ocolesc
kernel-ul. Nucleele sunt bune pentru asigurarea protecţiei (protecţia
sistemului şi a utilizatorilor bine intenţionaţi, de programe rău intenţionate
şi erori) şi pentru planificarea resurselor între diferitele aplicaţii. Dacă
scoatem nucleul din funcţie, aceste servicii vor trebui îndeplinite de
altcineva. Spre exemplu, ADC-urile oferă protecţie folosind hardware-ul
memoriei virtuale (pentru a proteja descriptorii) şi constrângerea
adaptoarelor (pentru a proteja memoria tampon).
Trebuie de asemenea să multiplexeze legătura fizică de comunicare (în
special în partea de emisie) între diversele canale ale dispozitivelor
aplicaţiei şi să asigure un oarecare grad de corectitudine. Pentru aceasta
146
codul kernel-ului ar trebui replicat în fiecare dispozitiv; unele dispozitive
însă, precum discul sau adaptorul de reţea sunt mai speciale, în sensul că
necesită performanţă mai ridicată, şi trebuie tratate special. Există o
iniţiativă prin care se doreşte ca toate aceste idei să aibă şi o formă
comercială, bazat pe ideea ADC şi pe soluţia UUNet de la Cornell
(asemănătoare cu ADC şi propusă în paralel cu aceasta). Această
propunere, cunoscută sub numele de arhitectura interfeţei virtuale VIA
(Virtual Interface Architecture)este descrisă în cele ce urmează.

6.5.1. Propunerea de arhitectură a interfeţei virtuale (VIA)


Arhitectura interfeţei virtuale este un standard comercial, care
înglobează ideile legate de ADC. Termenul de interfaţă virtuală are sens,
dacă considerăm că un canal al unui dispozitiv al aplicaţiei poate oferi
fiecărei aplicaţii câte o interfaţă virtuală proprie, pe care o poate manevra
fără intervenţia nucleului. Interfeţele virtuale sunt desigur multiplexate
către o singură interfaţa fizică. VIA a fost propusă de un consorţiu
industrial care include Microsoft, Compaq şi Intel.
VIA foloseste următoarea terminologie:
• Memoria înregistrată: Acestea sunt regiuni de memorie folosite de
aplicaţie pentru a trimite/recepţiona date. Aplicaţia e autorizată să citească/
/scrie din/în aceste regiuni; regiunile sunt îmbinate pentru a evita
paginarea.
• Descriptorii: Pentru a trimite/recepţiona un pachet, aplicaţia foloseşte
o bibliotecă la nivel-utilizator (libvia), pentru a construi un descriptor care
este o structură de date ce conţine informaţii despre tampon (de exemplu
un pointer). VIA permite unui descriptor să se refere la mai multe
tampoane din memoria înregistrată (pentru dispersie/regrupare) şi permite
folosirea de diferite etichete de protecţie a memoriei. Descriptorii pot fi
adăugaţi unei cozi de descriptori.
• Soneriile (doorbells): Reprezintă o metodă nespecificată prin care
interfeţei de reţea i se comunică descriptorii. Se poate face fie prin partea
de scriere a interfeţei cardului de memorie, fie prin declanşarea unei
întreruperi pe card; variază de la implementare la implementare. Sunt de
fapt pointeri către descriptori, conducând la nivelul doi de indirectare.
Standardul VIA are câteva probleme :
• Performanţa mesajelor mici: trimiterea datelor necesită urmărirea unei
sonerii care este pusă în legătură cu un descriptor (destul de mare, în jur de
45 bytes), care indică spre date. In cazul unor mesaje mici, aceasta
reprezintă un cost suplimentar mare (un mod de a elimina această
problemă este de a combina descriptorul şi datele pentru mesajele mici).
• Memoria soneriei: La fel ca la memoriile înregistrate, şi soneriile
trebuiesc protejate. Specificaţia VIA cere ca fiecare sonerie să fie mapată
la o pagina de utilizator separată, ceea ce reprezintă o risipă a spaţiului
adreselor virtuale pentru mesaje de dimensiune mică (un mod de a elimina
această problemă este de a combina mai mulţi descriptori cu o singură
pagină, însa aceasta presupune mecanisme suplimentare)
Specificaţiile VIA sunt destul de vagi.

147
6.6. Reducerea intreruperilor
Deşi supraîncărcarea datorată întreruperilor este mai mică decât cea
apărută la planificarea proceselor sau apelurile sistem, ea poate fi totuşi
destul de substanţială. De fiecare dată când soseşte un pachet, apare o
întrerupere care are ca efect înteruperea procesorului şi o comutare de
context. Nu pot fi evitate de tot întreruperile, dar se poate reduce
supraîncărcarea datorată întreruperilor prin următoarele artificii.
• Intrerupere doar pentru evenimente importante: de exemplu, în
soluţia ADC, adaptorul nu trebuie să întrerupă procesorul la fiecare
recepţie a unui nou pachet, ci doar la recepţionarea primului pachet din
fluxul de pachete (presupunem că aplicaţia va verifica pachetele
recepţionate) şi când coada descriptorilor de tampoane libere este goală.
Aceasta poate reduce supraîncărcarea datorată întreruperilor, la un singur
pachet din N pachete recepţionate, presupunând că cele N pachete se
recepţionează într-o rafală.
• Polling/anchetarea: Procesorul (CPU) verifică dacă au apărut noi
pachete iar adaptorul nu întrerupe niciodată. Acest lucru poate duce la o
supraîncărcare mai mare decât în cazul procesării acţionate de întreruperi,
dacă numărul de pachete recepţionate este scăzut, dar metoda poate fi
eficientă pentru volume de trafic mari. O altă variantă a acestui concept ar
fi întreruperile de ceas: CPU verifică periodic, la expirarea unui timer.
• Controlul prin aplicaţie: Transmiţătorul poate determina momentul
când se întrerupe receptorul, setând un anumit bit din antetul pachetului.
De exemplu un transmiţător FTP poate seta bitul de întrerupere, doar în
ultimul pachet de date din cadrul unui transfer de fişiere (P10, pasează
indicaţii în antetele protocolului). Ideea este probabil prea radicală pentru
a putea fi folosită, însă a fost propusă o implementare care are la bază o
rafinare a acestei idei, într-un chip ATM, fabricat deja.
In general, folosirea simultana a mai multor metode funcţionează destul
de bine în practică. In unele implementări cum cum este “first bridge”,
utilizarea polling-ului este foarte eficace. De aceea, ideile mai radicale
precum întreruperile de ceas sau cele controlate de aplicaţie au devenit
mai puţin folositoare. Trebuie menţionat faptul că ideile RDMA au marele
avantaj de a înlătura, pentru transferurile de date de mari dimensiuni,
necesitatea apelurilor de sistem per pachet şi a întreruperilor per pachet.

6.6.1. Evitarea blocării Livelock la nivelul recepţiei


Pe lângă ineficienţele datorate costurilor de tratare a întreruperilor,
întreruperile pot interacţiona cu planificarea sistemului de operare,
reducând la zero debitul nodurilor terminale, fenomen cunoscut sub
numele de receiver livelock. In BSD UNIX, recepţionarea unui pachet
generează o întrerupere. Procesorul face un salt la rutina de tratare a
întreruperii, trecând peste planificator, pentru a câştiga viteză. Rutina de
tratare a întreruperii face o copie a pachetului, pe care o pune într-o coadă
a nucleului, care conţine toate pachetele IP ce aşteaptă să fie “consumate”,
face o cerere de atribuire a unui fir de execuţie către sistemul de operare
(întrerupere software) şi îşi termină activitatea.
Daca reţeaua este foarte încărcată, calculatorul poate intră în starea de
receiver livelock, stadiu în care calculatorul procesează toate pachetele
care ajung la el, doar pentru a le distruge ulterior, deoarece aplicaţiile nu
148
rulează niciodată. Dacă se receptionează un tren de pachete, doar rutina de
tratare a întreruperilor de prioritate maximă va rula, şi probabil nu va mai
fi timp pentru întreruperile software şi nici atât pentru procesul browser.
Astfel, se ajunge la momentul în care cozile IP sau cozile socket-urilor vor
fi pline, şi la pierderea pachetelor în a cărei procesare s-au investit resurse.
O tehnică, care pare necesară în astfel de situaţii este dezactivarea
întreruperilor, când procesarea la nivelul aplicaţiilor este foarte redusă.
Acest lucru se poate face urmărind timpul de rulare a rutinelor de
întrerupere aferente unui dispozitiv, iar dacă timpul respectiv depăşeşte un
anumit procentaj din timpul total, atunci dispozitivul respectiv va fi
mascat. Aceasta va duce însă la pierderea pachetelor care ajung în
momentele de supraîncărcare ale calculatorului, printre care vor fi şi
pachete importante sau care nu ar genera întreruperi.
Se apeleaza la două mecanisme. In primul rând ar trebui folosite cozi
separate pentru fiecare socket destinaţie şi nu o singură coadă utilizată în
comun. La recepţionarea pachetului, prin demultiplexare timpurie se va
plasa fiecare pachet în coada asociată socket-ului. In acest fel, dacă se
întamplă ca o coadă a unui socket să se umple, pentru că aplicaţia
respectivă nu citeşte pachetele, celelate socket-uri vor putea progresa,
neinfluenţate de această problemă.
Al doilea mecanism este implementarea procesarii protocolului la
nivelul de prioritate a procesului receptor şi ca parte a contextului
proceselor recepţionate (şi nu ca o întrerupere software separată). In
primul rând se înlătura practica neechitabila, în care se transferă
procesarea de la aplicaţia X la aplicaţia Y, ce rula în momentul
recepţionării pachetului pentru aplicaţia X. In al doilea rând, dacă o
aplicaţie rulează încet, coada soclului respectiv se va umple iar pachetele
recepţionate pe acest socket vor fi pierdute, permiţând însă procesarea
celorlalte pachete. In al treilea rand, şi cel mai important este faptul că
deoarece protocolul de procesare este făcut la un nivel mai scăzut
(procesarea aplicaţiei), se înlătură problema livelock cauzată de procesarea
parţială (de exemplu, procesarea de protocol) a multor pachete, fără ca
procesările aplicaţie aferente să fie obligate să scoată pachetele din coada
de socket.
Acest mecanism, denumit lazy receiver processing (LRP), foloseşte în
principiu procesarea înceată, nu atât de mult pentru eficienţă, cât pentru
echitate şi pentru a evita apariţia livelock.

6.7. Concluzii
Pe lângă restructurarea principală referitoare la evitarea copierii,
controlul supraîncărcării este o problemă foarte importantă, care trebuie
tratată când e vorba de aplicaţii de reţea. Serverele rapide trebuie să reducă
supraîncărcarea cauzată de planificarea proceselor, de apelurile la sistem,
şi de întreruperi. Sistemele de operare mai noi, precum Linux se străduiesc
enorm să reducă costul inerent pe care îl implică controlul supraîncărcării.
Arhitecturile moderne devin tot mai rapide în procesarea instrucţiunilor,
folosind date din memoria cache, fără o accelerare sensibilă a comutării
contextului sau a procesării întreruperilor.
Au fost trecute în revistă, pentru aplicaţiile de reţea, tehnicile de bază
pentru reducerea supraîncărcării cauzată de planificarea proceselor. Cu
149
greu se poate face ceva ieşit din comun, ca structurarea fiecărui nivel ca un
proces separat, fără apeluri la nivelurile superioare. Din fig.6.3 trebuie
desprinsă ideea profundă a procesarii la nivelul utilizatorului. Proiectarea
de reţele la nivel de utilizator, împreună cu canalele dispozitivelor de
aplicaţie fac posibilă existenţa unor tehnologii precum VIA, care vor face
parte în curând din sisteme reale, pentru a evita apeluri de sistem la
trimiterea/recepţionarea pachetelor.
Pe de altă parte, arta procesării structurate în contextul aplicaţiei, de
exemplu un server Web, s-a bucurat de tot mai multă atenţie în ultimul
timp. In timp ce serverele conduse de evenimente (împreună cu processele
ajutătoare) echilibrează maximizarea concurenţei şi minimizarea încărcării
datorată comutării de context, aspectele legate de ingineria software lasă
încă multe întrebări fără răspuns. Va fi suficientă abordarea lansată-de -
evenimente în mediul de producţie, cu modificări şi depanări frecvente?
Un pas inainte îl reprezintă abordarea condusă de evenimente, pe nivele,
însă ingineria serverelor Web mari va necesita cu siguranţă mai mult efort.
Abordarea condusă de evenimente se bazează pe implementarea rapidă a
apelului select() sau a funcţiilor echivalente cu acesta. Abordările Unix au
probleme fundamentale de scalabilitate ; sistemele de operare mai
populare, precum Windows, au API-uri mult mai eficiente
Ideea foarte importantă privind standardul VIA o reprezintă faptul că se
permite aplicaţiilor să comunice direct cu dispozitivele de reţea, folosind o
interfaţă virtuală protejată. Ideal, adaptorii sunt astfel proiectaţi încât să
permită funcţionarea mecanismelor de tip VIA sau similare. Intreruperile
sunt evident de neevitat, această problemă poate fi însă ameliorată
folosind amestecul şi polling-ul în medii potrivite.Figura 6.1 listează
tehnicile discutate în acest capitol şi principiile corespunzătoare.

6.8. Exercitii
1. Filtre de pachete şi apeluri : La descrierea apelurilor (fig.6.3)
sistemul şi-a dat seama pentru care aplicatie era destinat un anumit
pachet, prin faptul că apela o rutină de transport. De ce mai e necesară
filtrarea pachetelor? Ce presupuneri implicite au fost făcute aici?
2. Compararea modelelor de structurare a serverelor Web : au
fost comparate diferite mecanisme de structurare a serverelor prin
intermediul unor metrici simple ca eficienţa planificării şi concurenţa
CPU. Considerati şi celelalte două metrici în procesul de comparare.
• Concurenţa la disc : Unele sisteme au discuri multiple şi există o
planificare a discurilor. Intr-un asemenea mediu se pune întrebarea dacă
la o abordare condusă de evenimente ar putea apare probleme, în
comparaţie cu abordarea multiproces. Vor apare probleme şi la
abordarea condusă de evenimente care dispune şi de procese helper?
• Acumularea statisticilor: Serverele Web trebuie să deţină informaţii
statistice referitoare la structurile de utilizare pentru a ţine evidenţa. Vor
fi aceste statistici mai complexe la arhitecturile proces-per-client şi fir de
execuţie-per-client? De ce este mai simplu într-o arhitectură condusă de
evenimente?
3. Algoritmi versus algoritmică în reimplementarea ufalloc():
Se determina modul cel mai eficient de reimplementare a funcţiei
ufalloc(), pentru a găsi cel mai mic descriptor nealocat.
150
• Se folosesete o stivă/heap binară. Pentru N identificatori, câte accese
la memorie sunt necesare? Cât spaţiu este necesar, în biţi?
• Presupunem că maşina este pe W biţi (la Alpha W=64) şi că există o
instrucţiune (sau un set de instrucţiuni) pentru a găsi cel mai din dreapta
zero într-un cuvânt de W biţi. Presupunem că descriptorii alocaţi sunt
reprezentaţi ca un set de biţi de dimensiune N. Cum poate fi completat
setul de biţi cu o stare suplimentară (P12), pentru a calcula eficient cel mai
mic descriptor nealocat ?
• Care sunt costurile de spaţiu şi timp ale acestei scheme, în comparaţie
cu o stivă simplă? Se poate face stiva simplă mai rapidă folosind un
artificiu prin care se măreşte baza stivei, astfel încât să avem K>1
elemente în fiecare nod al stivei?
4. Implementarea modificată a unui fast select(): Textul explică
modul în care elementele sunt adăugate setului I, H şi R, dar nu specifică
în totalitate modul în care ele sunt scoase din set. Explicaţi modul în care
elemetele sunt scoase din set, în special la setul de indicaţii H.
5 Implementarea modificată a unui fast select(): Se consideră
următoarele modificări ale implementării fast select:
(a) Inew este egal cu S (şi nu cu Iold ∪ S).
(b) Rnew este calculat ca şi înainte.
(c) Utilizatorului i se returnează Rnew (şi nu Rnew ∪ S).

151
CAPITOLUL 11

CAUTAREA PENTRU ASOCIEREA PREFIXELOR

Considerăm o bază de date cu avioanele ce vin/pleacă dinspre/spre


Londra către sute de oraşe americane. O soluţie ar fi precizarea căilor către
fiecare destinaţie. Dacă majoritatea zborurilor către America trec prin
Boston, exceptând zborurile spre California, care trec prin Los Angeles,
putem reduce baza de date de la sute de locaţii, la două locaţii tip prefix:
(USA*Æ Boston; USA.CA.* Æ LA). Problema ce apare folosind această
reducere este: oraşul destinaţie USA.CA.Fresno se va potrivi cu ambele
prefixe: USA* şi USA.CA.*; baza de date trebuie să returneze cea mai lungă
potrivire (USA.CA.*)/the longest-prefix-match. Astfel, prefixele se folosesc
pentru comprimarea bazelor mari de date, cu costul unei căutări mai
complexe pentru cea mai lungă potrivire .
Internet-ul utilizează acelaşi principiu. În anul 2004 ruterele de tip core
memorau ≈ 150.000 prefixe, în loc de un miliard de intrări pentru fiecare
adresă de internet posibilă. Pentru un ruter de tip core, toate calculatoarele
din cadrul unei universităţi (ca UCSD) vor fi probabil găsite cu aceeaşi
adresă salt/hop (adresa ruterului de intrare/ieşire în/din subreteaua de
comunicaţii). Dacă tuturor calculatoarele din cadrul UCDS le vor fi date
aceleaşi seturi iniţiale de biţi (numărul reţelei sau prefixul), atunci ruterul va
putea memora o intrare în loc de sute de intrări pentru fiecare calculator din
UCDS. Tehnicile şi principiile corespunzătoare sunt prezentate în fig.11.1.
Număr Principiul Tehnica de c ăutare
P2a, P10 Precalculul indicilor Comutare de etichetă
P2a, P10, Indicii calculaţi se transmit la momentul Comutare IP.
P4a rulării.
Exploatarea hardware-ului comutatorului
ATM
P11 Memorarea întregii adrese IP în cache Căutare în cache-uri

P5 Căutare paralelă hardware CAM-uri


P4b Expandarea prefixelor pentru creşterea Expansiune controlată
vitezei
P13 Folosirea unor paşi variabili, Arbori ordonaţi, cu pas
ca grad de libertate variabil
P4b, P12, P2a Comprimare pentru creşterea vitezei Arbori ordonaţi de tip
Contorizare precalculată a setului de biţi Lulea
P15 Căutare eficientă Căutare binară a lungimii
P12 Adaugă stări marker prefixelor
P2a Precalcularea evoluţiei
marker-ilor
P2a Precalculează domeniul pentru potrivirea Căutare binară a
prefixelor prefixelor

Figura 11.1 Principiile utilizate în schemele de mascare a prefixului.

152
11.1 Cautarea prefixelor
Această secţiune introduce notaţia prefix, explică de ce e folosită căutarea
prefixelor şi descrie principalele metrici pentru evaluarea schemelor de
căutare a prefixelor.
11.1.1 Notarea prefixelor.
Prefixele utilizate pe internet sunt definite prin biţi şi nu prin caractere
alfanumerice, având o lungime de până la 32 de biţi. Confuzia e creată de
faptul ca că prefixele IP sunt scrise deseori cu notaţii cu punct zecimal.
Astfel, prefixul de 16 biţi pentru UCDS, se scrie 132.239. Fiecare din cifrele
zecimale dintre puncte reprezintă un octet. Deoarece în binar 132 este
10000100 şi 239 este 11101111, prefixul UCDS în binar poate fi scris şi ca
1000010011101111*, unde caracterul “*” arată că biţii rămaşi nu contează.
Toate adresele IP de 32 biţi ale staţiilor UCSD încep cu aceşti 16 biţi.
Deoarece prefixele pot avea lungime variabilă, alt mod de notare a
prefixelor este de forma A/L, unde A e o adresă IP de 32 biţi în virgulă
zecimală şi L lungimea prefixului. Astfel, prefixul UCDS poate fi scris şi ca
132.239.0.0/16, unde lungimea 16 indică faptul că doar primii 16 biţi
(132.239) sunt relevanţi. Al treilea mod de a scrie prefixele este utilizarea
unei măşti în loc de o lungime explicită de prefix. Astfel prefixele pot fi
scrise în următorul mod: 128.239.0.0 cu masca 255.255.0.0. Deoarece masca
255.255.0.0 are 1 pe primii 16 biţi, se indică o lungime implicită de 16 biţi. 1
Din aceste trei modalităţi prin care se poate nota un prefix (binar cu asterix
la sfârşit, cu notaţie ”/ ” slash, şi prin intermediul măştii), ultimele două sunt
mai compacte şi deci mai potrivite pentru scrierea prefixelor mari. Din
motive pedagogice este mai simplu să se utilizeze prefixe mici pentru
exemple şi scrierea în binar. În acest capitol se va utiliza 01110* pentru a
indica un prefix ce se potriveşte cu toate adresele IP de 32 de biţi care încep
cu 01110. Este uşor de convertit această notaţie spre notaţiile slash sau
mască. Majoritatea prefixelor au lungimea minimă de 8 biţi.

11.1.2 De ce prefixe cu lungime variabilă?


Înainte de a considera abordarea temei complexe de potrivire a prefixelor
cu lungime variabilă, să vedem de ce prefixele Internet-ului au lungime
variabilă. De exemplu pentru un număr de telefon 858-549-3816, e uşor de
extras codul zonei, adică primele trei cifre zecimale (858). Dar dacă prefixele
cu lungime fixă sunt uşor de implementat, care este avantajul celor cu
lungime variabilă?
Răspunsul general e că prefixele cu lungime variabilă sunt mai eficiente ca
spaţiu utilizat pentru adresare. Zonelor cu multe terminale li se asociază
prefixe scurte, iar celor cu puţine terminale prefixe lungi.
Răspunsul specific vine din istoria adresării pe Internet. Internetul a început
printr-o simplă ierarhie în care adresele de 32 de biţi erau divizate în adresă
de reţea şi număr de staţie gazdă; ruterele doar memorau intrările în reţele.
Pentru alocarea flexibilă de adrese, adresele de reţea vin cu dimensiuni
variabile: Clasa A (8 biţi), Clasa B (16 biţi), Clasa C (24 biţi). Pentru a face
faţă epuizării adreselor de clasă B, schema CIDR (Classless Internet Domain
1
Notaţia cu mască este de fapt mai generală din cauză că se pot folosi măşti neînvecinate,
la care 1-urile nu sunt neapărat consecutive pornind de la srtânga. Astfel de definiţii ale
reţelelor sunt din ce în ce mai puţin folosite şi nu există în tabelele de prefixe ale ruterelor
core.
153
Routing) asignează noilor organizaţii adrese multiple continue din Clasa C
ce pot fi agregate cu un prefix comun. Se reduce dimensiunea tabelului
ruterului core.
Lipsa spaţiului de adresare, a determinat ca înregistrările Intrenet să fie
foarte conservative în asignarea de adrese IP. Unei organizaţii mici i se poate
aloca doar o mică parte a adreselor Clasă C, poate a 30-a parte, care permite
folosirea a doar patru adrese IP în cadrul organizaţiei respective.Multe
organizaţii rezolvă problema prin împărţirea adreselor IP între mai multe
calculatoare, utilizând scheme ca translatarea adreselor reţea, NAT(Network
Address Translation).
Astfel, CIDR şi NAT au ajutat la manevrarea creşterii exponenţiale a
Internet-ului cu un spaţiu finit de adrese de 32 de biţi. Eficienţa NAT pe
termen scurt şi complexitatea lansării unui nou protocol au determinat o
dezvoltare lentă a IPv6 care are adrese de 128 biţi. Dar, într-o lume nouă cu
miliarde de senzori fără fir, interesul pentru IPv6 va reveni.
Decizia de dezvoltare a CIDR a salvat Internetul, dar a introdus
complexitatea metodelor de verificare a potrivirii celui mai lung prefix.

11.1.3 Modelul de căutare/lookup


Un pachet care soseşte pe o legătură de intrare poartă o adresă 2 de Internet
de 32 biţi. Procesorul consultă un tabel de expediere/forwarding care conţine
un set de prefixe cu legătura de ieşire corespunzătoare. Pachetul se asociază
cu prefixul cel mai lung, care se potriveşte cu adresa destinaţie a pachetului,
şi este retransmis către legătura corespunzătoare de ieşire. Sarcina găsirii
legăturii de ieşire numită şi căutarea adresei /address lookup, este subiectul
acestui capitol, care expune algoritmii de căutare şi arată că aceste căutări
pot fi implementate la viteze de gibabit şi terabit.
Înainte de a căuta soluţii pentru căutarea IP, trebuie să ne familiarizăm cu
câteva probleme legate de distribuţia traficului, tendinţele memoriilor, şi
dimensiunile bazelor de date (fig.11.2), care justifica schemele de căutare.
Studii asupra traficului în reţelele de tranzit/backbone (1997), arată
≈ 250.000 de fluxuri concurente de scurtă durată, cu o măsurare destul de
conservativă a fluxurilor. Numărul fluxurilor este în creştere, deci
memoriile cache nu funcţionează bine.
Aproximativ jumătate din pachetele recepţionate de către ruter sunt
pachete TCP de confirmare de lungime minimă. E posibil ca un ruter să
primească un şir de pachete de lungime minimă. Ca să putem căuta
prefixul în timpul retransmiterii unui pachet de lungime minimă, trebuie
evitată necesitatea unei cozi pe legătura de intrare, ceea ce ar simplifica
proiectarea sistemului.
Al doilea motiv este comercial: mulţi vânzători pretind transmisii la
viteza /firului: transmisia unui pachet de 40 byte la viteza liniei nu ar
trebui să dureze mai mult de 320ns la o viteză de 1Gbps, 32nsec la
30Gbps (vitezele OC – 192), şi 8nsec la 40Gbps (OC - 768).
Cea mai importantă metrică a schemelor de căutare/lookup este viteza
căutării. A treia observaţie arată că, deoarece costul computaţional e dominat
2
Deşi majoritatea utilizatorilor lucrează cu nume de domenii, trebuie menţionat că acestea
sunt translatate în adrese IP de către serviciul DNS, înainte de transmiterea pachetelor

154
de accesările memoriilor, cea mai simplă măsură a vitezei căutării este
numărul de accesări ale memoriei în cazul cel mai defavorabil. A patra
observaţie arată că deoarece bazele de date ale reţelelor backbone au prefixe
cu lungimea între 8 şi 32, schemele naive vor avea nevoie de 24 de accesări
ale memoriei în cel defavorabil caz, pentru a testa toate lungimile posibile
de prefix.
A cincea observaţie susţine că în timp ce bazele de date au în jur de
150.000 prefixe, posibila utilizare a rutelor pentru calculatoarele gazdă (cu
adrese de 32 biţi) şi a rutelor multicast, înseamnă că ruterele backbone
viitoare vor avea baze de date de prefixe cu 500.000 -1.000.000 de prefixe.
A şasea observaţie se referă la viteza de actualizare a structurilor de date
pentru căutări, pentru adăugarea/ştergerea unui prefix de exemplu.
Implementările instabile ale protocoalelor de rutare pot duce la actualizări de
ordinul milisecundelor. De observat că, indiferent dacă sunt de ordinul
secundelor sau a milisecundelor, sunt cu câteva ordine de mărime sub
necesarul căutării/lookup, permiţând astfel luxul precalculării informaţiilor
(P2a), sub formă de structuri de date, pentru accelerarea căutării, cu costul
creşterii timpilor de actualizare.
A şaptea observaţie se referă la memoriile standard ieftine, DRAM cu
timpi de acces de ≈ 60 nsec, şi memoriile de viteză mare (off/on-chip
SRAM, 1–10nsec) ce trebuie folosite la viteze mari. DRAM sunt teoretic
nelimitate, dar SRAM şi cele on-chip sunt limitate de costul mare şi
nondisponibilitate. Astfel a treia metrică este utilizarea memoriei; aceasta
poate fi scumpă şi rapidă (cache la software, SRAM la hardware), sau ieftină
şi lentă (DRAM, SDRAM).
O schemă de căutare care nu face o reactualizare incrementală, va avea
nevoie de două copii ale bazei de date de căutare/lookup, astfel încât în
prima să poată face căutarea, şi în cealaltă asocierea. Astfel actualizarea
incrementală reduce la jumătate necesarul de memorii de mare viteză..
A opta observaţie se referă la lungimea prefixelor. IPv6 foloseşte prefixe
de 128 biţi, iar lookup-urile multicast necesită asocieri pe 64 biţi (adresa de
grup şi adresa sursă pot fi concatenate în 64 de biţi). Este discutabila
generalizarea Ipv6 şi a multicast-ului, asa că lookup-urile IP pe 32 biţi
rămân actuale. Schemele descrise se scalează bine şi la prefixe lungi.
Pe scurt, metricile importante în ordinea importantei sunt: viteza lookup-
ului, memoriile şi timpul de actualizare. Ca exemplu, o proiectare buna on-
chip, folosind o memorie on-chip de 16 Mbiţi, poate suporta orice set din
500.000 prefixe, face un lookup în 8 nsec pentru a permite o expediere la
viteza firului la ratele OC-192, şi permite actualizarea prefixului în 1msec.
Datele sunt conform cu baza de date disponibilă pentru proiectul IPMA,
folosită pentru a compara experimental schemele de mascare. Cea mai mare
dintre acestea Mae East, e un model rezonabil pentru un suport larg de ruter.
Următoarea notaţie este utilizată în mod repetat pentru raportarea
performanţelor teoretice ale algoritmilor de mascare IP. N reprezintă numărul
de prefixe (150.000 pentru baze de date mari, în prezent ), iar W reprezintă
lungimea adresei (32 pentru IPv4) .

155
Observaţii Inferente
1)250000 de fluxuri concurente în backbone Memorarea cache lucrează slab în ruterele
backbone
2)50% sunt confirmări TCP Pentru pachetele de 40 biţi e necesară
căutarea cu viteza liniei
3)Căutare dominată de accesările memoriei Viteza căutării măsurată de numărul de
accesări ale memoriei
4)Lungimea prefixelor 8 - 32 Schemele naive cer 24 de accesări ale
memoriei
5)150.000 prefixe actuale şi rute multicast şi În urma creşterii
gazdă 500.000 -1.000.000 prefixe
6)BGP instabil, multicast Actualizare în timp de
milisecunde-secunde
7)Vitezele mari necesită SRAM Duce la minimizarea memoriei
8)Ipv6, întârzieri multicast Decisiv e lookup pe 32biţi
FIGURA11.2 Date curente despre problemele de lookup şi implicaţiile asupra soluţiilor

În final două observaţii pot fi exploatate pentru optimizarea cazului utilizat.


O1: Aproape toate prefixele au 24 biţi sau mai puţin, în timp ce majoritatea
au prefixe de 24 biţi şi următorul vârf e la 16biţi. Uneori vanzătorii utilizează
aceasta pentru a prezenta cel mai slab caz pentru metodele de căutare cu 24
biţi. Viitorul poate să aducă baze de date cu un număr mare de rutere gazdă
(32 adrese de bit) şi integrarea memoriilor ARP.
O2: Sunt rare prefixele ce sunt prefixele altor prefixe (ca 00* şi 0001*).
Numărul maxim de prefixe ale unui prefix, conţinute într-o bază de date, e 7.
Ideal ar fi să facem faţă şi celui mai defavorabil caz de căutare din punct de
vedere a timpului, dar este de dorit să găsim scheme ce utilizează
observaţiile precedente pentru creşterea performanţei medii de stocare.

11.2 Evitarea căutării


Pornirea instinctivă a proiectantului de sistem este nu să rezolve
problemele complexe (ca potrivirea cea mai lungă a prefixelor), ci să le
elimine. In reţelele cu circuit virtual, ca reţelele ATM, când o sursă doreşte
să transmită date către o destinaţie, stabileşte un apel, asemănător cu
stabilirea unui apel telefonic. Numărul de apel (VCI) este pentru fiecare ruter
un întreg cu lungime moderată, uşor de căutat. Stabilirea apelului duce la
creşterea întârzierii tur-retur, suplimentară faţă de timpul necesar transmisiei
datelor. In reţelele ATM comutatorul precedent trimite un index (P10,
trimite informaţii în antetele de protocol), către următorul comutator. Indexul
este calculat (P2a) chiar înainte ca datele să fie trimise, de către comutatorul
precedent (P3c, realizează deplasarea calculului în spaţiu). Aceeaşi idee
poate fi utilizată în reţelele de tip datagrame, ca Internetul, pentru a evita
căutarea prefixului. Vor fi descrise două metode ce au la bază această idee:
comutare cu etichetă şi comutarea circuit.

156
11.2.1 Şiruri de indici şi comutarea etichetelor
La folosirea şirurilor de indici, fiecare ruter pasează un indice tabelului de
retransmisie a următorul ruter; se evită astfel folosirea căutării prefixelor.
Indecşii sunt precalculaţi de către protocolul de rutare/dirijare de cate ori se
schimba topologia retelei. În figura 11.3, sursa S trimite un pachet către
destinaţia D prin primul ruter A; antetul pachetului conţine un index i spre
tabelul de retransmisie al lui A. Astfel, ruterul A precizează calea spre D prin
ruterul B, care la rândul lui conţine indicele j către destinaţia D. Astfel A
trimite pachetul spre B şi precizează indicele j al acestuia. Procesul se repetă
în fiecare ruter din reţea prin utilizarea indexului din pachet pentru căutarea
in tabelele de transmisie spre destinaţie.
Diferenţele majore între şirurile de indici, şi indicii circuitelor virtuale sunt
următoarele: şirurile de indici sunt per destinaţie, nu per perechea activa
sursă-destinaţie ca indicii circitelor virtuale tip ATM. Cea mai importantă
diferenţă este aceea că şirurile de indecsi cu sunt precalculate de către
protocolul de rutare de fiecare dată când se schimbă topologia. In figura
11.4 se prezintă topologia unui ruter ce utilizează protocolul Bellman – Ford
pentru găsirea distantelor spre destinaţie.
Cu protocolul Bellman–Ford (folosit de protocolul intradomeniu RIP,
Routing Information Protocol), un ruter R calculează calea cea mai scurtă
spre D, prin retinerea celui mai mic dintre costurile spre D, prin fiecare
vecin. Costul spre D printr-un vecin ca A, este costul lui A spre D (5 in cazul
nostru) la care se adună costul de la R la A (3, in exemplul nostru). În figura
11.4, costul cel mai scurt de la R spre D este prin ruterul B, cu costul 7. R
poate calcula aceste costuri deoarece fiecare vecin al lui R, (A si B aici)
paseaza costul propriu de la D spre R, cum se arată în figură. Pentru
calcularea si a indicilor, se modifică protocolul de baza astfel încât fiecare
vecin isi reportează indicii spre destinaţie in plus cu costul propriu spre
destinatie. În figura 11.4, A trimite pe i şi B pe j; astfel, cand R alege pe B, ii
foloseste si indexul j a lui B in intrarile tabelului de dirijare pentru D. Deci,
fiecare ruter utilizează indexul vecinului de cost minim pentru fiecare
destinatie, ca sir de indecsi pentru acea destinatie.
Cisco a introdus mai târziu comutarea etichetelor, a cărei concept se
aseamănă cu conceptul înşiruirii indecsilor, dar comutarea etichetelor
permite unui ruter utilizarea unei stive de etichete (indici) pentru ruterele
multiple care urmeaza. Cu toate acestea, ambele scheme nu lucrează bine cu
structurile ierarhice. Se consideră un pachet ce ajunge de la backbone la
primul ruter al domeniului de ieşire. Domeniul de iesire este ultima retea
condusa autonom traversata de pachet, sa zicem o retea de intreprindere
careia ii apartine destinatia pachetului.
Singura cale de evitare a căutării/lookup in primul ruter R, din domeniul de
ieşire, este sa avem un ruter inafara domeniului de ieşire care sa trimita
(pentru subreteaua destinatie) mai devreme un index către R. Acest lucru este
imposibil, deoarece ruterele backbone anterioare ar trebui să aibe doar o
intrare agregata de rutare către intreg domeniul destinaţie şi, astfel, poate
trimite doar un indice spre toate subretelele din acel domeniu. Soluţia este fie
adăugarea unor intrări suplimentare ruterelor din afara domenuilui (ceea ce e
nefezabil), sau sa se pretinda o cautare IP banala la punctele de intrare in
domeniu(soluţia folosită). Comutarea etichetelor s-a dezvoltat azi sub o
157
formă mai generala numită comutare de etichete multiprotocol (MPLS
MultiProtocol Label Switching). Totusi, nici comutarea de etichete nici
MPLS nu evita complet cautarea IP ordinara.

D i D j D -
B,j B, -
A, i

Nod A
Tabel rutare Tabel rutare
S A B D

Fig. 11.3 Căutarea destinaţiei prin utilizarea de către fiecare ruter a unui index de trecere
în tabelul de transmisie a următorului ruter

Cost 5 Nod D Cost 6

D, 5
D, 6
i Cost 3 j
Cost 1
Nod A
Nod B

D, 5, i D, 5, j D, 5, j

Tabel Tabel
trimis de A trimis de B

Nod R
Fig 11.4 Setarea indicelor înşiruirii, sau modificarea înşiruirii prin
rutarea Bellman Ford

11.2.2 Comutarea fluxurilor


O altă metodă de evitare a căutării este comutarea fluxurilor. Se utilizeaza
un ruter de salt precedent care trimite un indice următorului ruter de salt, dar
spre deosebire de comutarea etichetă, acest index este calculat la cerere,
atunci când ajung datele la ruter, si apoi e memorat in cache.
Comutarea fuxului începe cu ruterele care conţin un comutator ATM
intern(posibil lent), şi un procesor capabil sa realizeze retransmisia IP şi
rutarea. În figura 11,5 sunt prezentate două astfel de rutere R1 şi R2. Cand
ruterul R2 trimite prima data un pachet IP spre destinaţia D, care ajunge la
158
intrarea din stânga a ruterului R2, portul de intrare trimite pachetul
procesorului sau central. Aceasta este o cale lenta. Procesorul face o căutare
IP şi comuta intern pachetul spre legatura de iesire L. Mai departe nimic nu e
iesit din comun.

R2 R1
I
I,L
L

M
D
Procesor
Slow path
Fast path
Fig. 11.5 Comutarea IP

La comutarea IP, daca R1 doreste sa comute pachetele transmise spre D


destinate legaturii de iesire L, R1 alege un circuit virtual liber I, plaseaza
asocierea I,L in portul sau de intrare, si apoi trimite pe I inapoi la R2. Daca
acum R2 trimite lui D pachete cu eticheta VCI I, pachetul va fi comutat
direct spre legatura de iesire, fara sa mai treaca prin procesor.
Daca insa, daca momentan e un volum mare de trafic spre D, nodului R1
”ii vine idea” sa ”comute” pachetele ce merg spre D, situatia devine
ciudata.In acest caz, R1 alege mai intai un circuit virtual liber I, plaseaza
asocierea I ÆL in portul sau hardware, si apoi trimite I inapoi la R2. Daca
acum R2 trimite spre D pachete cu eticheta VCI I , spre portul de intrare a lui
R1, portul verifica asocierea lui I cu L si comuta pachetul direct spre
legatura de iesire L fara sa mai treaca prin procesor. Desigur, R2 poate repeta
acest proces de comutare cu ruterul precedent de pe cale, s.a.m.d. Eventual,
inaintarea IP poate lipsi complet in portiunea comutata a secventei de rutere
cu comutarea fluxurilor.
Comutarea fluxurilor, desi eleganta, probabil ca nu functioneaza prea bine
in retelele de tranzit/backbone, deoarece fluxurile backbone au o viata scurta
si o localizare slaba. Unii autori, Molinero-Fernandez si McKeown au o
parere contrara, si argumenteaza in favoarea unei reveniri a comutarii
fluxurilor bazata pe conexiunile TCP. Ei considera ca actuala folosire a
comutatoarelor optice cu comutarea circuitelor in ruterele de tranzit/core,
subutilizarea legaturilor de tranzit/backbone care lucreaza la 10% din
capacitate, si cresterea latimii de banda optce vor favoriza simplitatea
comutarii de circuit la viteze mari.
Ambele metode de comutare (IP şi etichetă), sunt folosite pentru evitare a
cautarilor IP prin pasarea de informatii in antetele protocoalelor. Ca şi la
ATM, ambele trimit indici (P10); dar comutarea etichetă precalculează
indicii in avans(P2a) (topologia se modifica in timp), spre deosebire de ATM
(chiar inainte de transferul datelor). Pe de alta parte, la comutarea IP indicii
sunt calculaţi la cerere (P2c, evaluare relaxata) după ce datele încep să fie
trimise. Cu toate acestea nici comutarea etichtelor nici comutarea IP nu evită

159
complet căutarea cu prefix şi fiecare adauga un protocol complex. Sa vedem
acum cum e cu presupusa complexitate a cautarii IP.

11.2.3 Situatia comutării etichetelor, comutarii IP şi comutării


multiprotocol a etichetelor
Desi comutarea etichetelor şi cea IP au fost initial introduse pentru
creşterea vitezei căutării, la comutarea IP s-a renuntat. Dar comutarea
etichetelor s-a reinventat sub forma mai generală a comutarii multiprotocol a
etichetelor (MPLS), pentru a diferentia fluxurile in scopul asigurarii calităţii
serviciilor. Asa cum un VCI indică o etichetă simpla pentru distingerea
rapidă a fluxului, eticheta permite ruterului să izoleze rapid un flux pentru
servicii speciale. De fapt, MPLS utilizează etichetele pentru a evita
clasificarea pachetelor, ceea ce este o problema mult mai dificila decăt
căutarea prefixelor. Asa ca, desi asocierea prefixelor este necesara inca,
metoda MPLS este utilizată actualmente in ruterele de tranzit/core.
Pe scurt, inaintarea/expedierea rapida MPLS a pachetelor se desfasoara
astfel. Dacă este identificat un pachet cu un antet MPLS, este extras din
acesta o etichetă de 20 biţi si eticheta este cautata intr-o tabela care asociaza
eticheta cu regula de inaintare/retransmisie. Regula specifică următorul
nod/hop şi operaţiunile ce vor fi efectuate asupra setului curent de etichete
din pachetul MPLS. Aceste operaţiuni pot include renunţarea la etichete
(”extragere din stiva etichetelor”)sau adăugarea de etichete (”introducere in
stiva etichetelor”).
Implementarea ruterelor MPLS trebuie realizată impunand anumite limitări
procesului general, pentru a garanta retransmisia la viteza liniei. O limitare
posibila se refera la impunerea unui unui spatiu dens pentru etichete , care
suporta un numar de etichete sub 220 (ceea ce permite un volum mai mic de
memorie pentru lookup, si evitarea tabelului hash), si limitarea numarului de
operatii cu stiva de etichete care pot fi efectuate asupra unui singur pachet.

11.3 Tehnici nonalgoritmice de asociere a prefixelor


Se prezintă aici alte două tehnici de căutare a prefixelor, nelegate de
metodele algoritmice: memorarea in cache-uri şi CAM ternar. Memorarea in
cache-uri se bazează pe localizarea referinţelor adreselor, iar CAM ternar pe
paralelismul hardware.

11.3.1 Memorarea in cache-uri


Căutarea/lookup poate fi mai rapidă prin utilizarea memoriilor cache,
(P11a) care mapează adresele de 32 biţi spre următorul salt/hop. Cu toate
acestea rata de utilizare a acestor memorii in backbone-uri este destul de
mică din cauza lipsei localizării prezentata de fluxurile din backbone.
Utilizarea unei memorii cache mari necesită folosirea unui algoritm de
asociere precis pentru cautare. Au aparut propuneri inteligente de modificare
a algoritmului de lookup pentru cahe-ul CPU, in acest scop. În esenta,
memorarea in cache-uri poate fi de folos dar nu elimină necesitatea unei
căutări rapide a prefixului.

11.3.2 Memorii ternare adresate prin conţinut.


Memoriile ternare adresate prin conţinut (TCAM) care permit biti ”care nu
conteaza” ofera o căutare paralelă printr-un singur acces la memorie. CAM-
160
urile actuale pot face căutarea si actualizarea/update intr-un singur ciclu de
memorie (10 nsec de exemplu) si manevreaza orice combinatie a 100.000 de
prefixe. Dar CAM-urile au următoarele probleme:
• Scalarea densitatii: un bit în TCAM-uri are nevoie de 10, 20 tranzistoare,
pe când un SRAM necesita doar 4, 6 tranzistoare. Astfel TCAM-urile vor fi
mai puţin dense decât SRAM-urile, adica vor avea o suprafaţă mai mare, iar
suprafaţa plăcilor este o problemă critica pentru rutere.
• Scalarea puterii: TCAM-urile consumă o putere mai mare din cauza
comparării paralele. Dar furnizorii de CAM-uri evita problema, gasind cai
de eliminare a unor parti ale CAM-ului, pentru a reduce puterea. Puterea
este o problema cheie in ruterele mari de tranzit/core.
• Scalarea timpului: logica de asociere intr-un CAM cere ca toate regulile
de asociere/matching sa lucreze astfel incat sa castige cea mai lunga
potrivire.Vechile generatii de CAM-uri aveau nevoie in jur de 10nsec pentru
o operatie, dar produsele anuntate actualmente se pare ca vor necesita doar 5
nsec, probabil prin pipelining-ul din partea de intirziere a asocierii.
• Chip-uri suplimentare: avand in vedere faptul ca multe rutere, ca CISCO
GSR si Juniper M160, au circuite integrate dedicate ASIC (Application
Specific Integrated Circuit) expedierii pachetelor, e tentanta integrarea
algoritmilor de clasificare cu cautare, fără adăugarea interfeţei CAM si chip-
urilor CAM. Tipic, CAM-urile necesita o punte ASIC in plus pe langa chip-
ul CAM de baza, iar uneori neceista chip-uri CAM multiple.
În concluzie, tehnologia CAM se îmbunătăţeşte rapid şi pot inlocui
metodele algoritmice în ruterele mai mici. Dar, pentru ruterele de tranzit mai
mari ce vor avea in viitor baze de date cu milioane de rute, ar fi mai bune
soluţiile care se scaleaza la tehnologiile memoriilor standard ca şi SRAM.
Probabil ca tehnologia SRAM va fi mereu mai ieftină, mai rapidă şi mai
densă ca şi CAM-urile. Desi e prea devreme sa prezicem rezultatul acestei
competitii dintre metodele algoritmice si cele TCAM, chiar si producatorii
de semiconductoare furnizeaza ambele solutii: algoritmice si cu CAM-uri.
P1=101*
P2=111*
P3=11001*
P4=1*
P5=0*
P6=1000*
P7=100000*
P8=100*
P9=110

Fig. 11.6 Baza de date cu prefixe.

11.4 Arbori ordonati unibit


Trie sau prefix tree este o structura de date ordonata de tip arbore pentru
memorarea tabelelor asociative la care cheile sunt siruri. Spre deosebire de
binary search tree, nici un nod din arbore nu memoreaza cheia asociata
nodului, dar in schimb, pozitia sa in arbore arata care este cheia asociata.Toti
descendentii oricarui nod au un prefix comun al sirului asociat acelui nod, iar
radacinii ii este asociat un sir vid. In mod normal, nu se asociaza valori
fiecarui nod, doar frunzelor (leaves) si anumitor noduri interne (inner
nodes) care corespund cheilor de interes.
161
Termenul trie deriva din retrieval. Din cauza etimologiei sale unii sustin ca ar trebui
pronuntat ca si ”tree”, dar altii sustin varianta ”try”, pentru a face distinctia fata de
termenul mai general ”tree”.
Este utila trecerea in revista a tehnicilor algoritmice (P15) pentru căutarea
prefixelor printr-o tehnică simplă: arborii ordonati unibit / unibit tries. Se va
considera o bază de date (fig 11.6), pentru ilustrarea soluţiile algoritmice din
acest capitol, cu 9 prefixe, P1-P9, cu sirurile de biţi ca in figură.
În practică, exista un hop/salt urmator asociat fiecărui prefix (omis in
figura). Pentru evitarea confuziei, numele prefixelor sunt folosite pentru
precizarea următorului hop. Astfel o adresă D ce începe cu 1 şi continua cu
31 de zerouri se va potrivi cu P6, P7, P8. Cea mai lungă potrivire este P7.
In fig.11.7 este prezentat un arbore ordonat unibit pentru exemplul de baza
de date din fig.11.6. Arborele ordonat unibit e un arbore la care fiecare nod e
un tabel/array care contine pointer-0 si pointer-1. La radacina toate prefixele
care incep cu 0 sunt memorate intr-un subarbore ordonat indicat de
pointerul-0 si toate prefixele care incep cu un 1 sunt memorate intr-un
subarbore ordonat indicat de pointerul-1.
Fiecare arbore ordonat este apoi construit recursiv, intr-un modul
asemanator, folosind restul de biti ai prefixului alocati subarborelui
ordonat.De exemplu, in fig.11.7, P1=101 e memorat intr-o cale parcursa
urmarind pointerul 1 ca radacina, pointerul 0 ca si copilul din dreapta al
radacinii, si pointerul-1 la urmatorul nod al caii.
Mai trebuie remarcate doua probleme. In unele cazuri, un prefix poate fi un
subsir al altui prefix. De exemplu, P4=1 ∗ este un subsir al lui P2=111 ∗ . In
acest caz, cel mai mic sir, P4, e memorat in interiorul nodului ordonat, pe
calea spre sirul mai lung.De exemplu P4 e memorat la copilul din dreapta a
radacinii; calea acestui copil din dreapta este sirul 1, care e acelasi cu P4.
La sfarsit, in cazul unui prefix ca P3=11001, dupa ce urmam primii trei
biti, naiv ne-am putea astepta sa gasim un sir de noduri corespunzand
ultimilor doi biti.Dar deoarece nici un alt prefix nu partajeaza cu P3 mai
mult decat primii trei biti, aceste noduri ar contine doar un pointer de
fiecare.Un astfel de sir de noduri ale arborelui ordonat cu cate doar un
pointer e numit ramura cu cale unica. In mod clar, ramurile uni-cale pot
creste spatiul pierdut de memorare, folosind intregul nod (ce contine minim
doi pointeri) cand de fapt ar fi suficient doar un bit. O tehnica simpla de a
elimina aceasta risipa evidenta (P1) este sa comprimam ramurile uni-cale.

Figura 11.7 Arbore ordonat unibit pentru baza de date din fig.11.6

In fig.11.7 acest lucru e facut folosind un sir text (de ex.”01”) pentru a
reprezenta pointerii care ar urma in ramura unicale. Astfel, in fig.11.7, doua
noduri ale arborelui ordonat (continand fiecare doi pointeri) din calea spre
P3, au fost inlocuiti de un singur sir text, de 2 biti. Dar nu s-a pierdut
informatie prin aceasta transformare (ca exercitiu, determinati daca mai e
vreo cale prin arbore care poate fi comprimata asemanator).
Pentru cautarea celei mai lungi potriviri a prefixului addresei destinatie D,
se folosesc bitii lui D pentru a trasa calea prin arbore. Calea incepe cu
radacina si continua pina ce cautarea esueaza prin terminare la un pointer vid
ori la un sir text care nu se potriveste complet.In timp ce urmareste calea,
algoritmul tine evidenta ultimului prefix numarat la un nod de pe cale. Cand
cautarea esueaza, acesta este cea mai lunga potrivire a prefixului si e
162
returnata.De exemplu, daca D incepe cu 1110, algoritmul porneste urmarind
pointerul-1 la radacina ca sa ajunga la nodul ce-l contine pe P4.Algoritmul
isi aminteste pe P4 si foloseste urmatorul bit al lui D ( un 1) pentru a urmari
pointerul -1 la urmatorul nod. La acest nod, algoritmul urmareste valoarea
(P4) prin prefixul (P2) gasit mai nou.In acest punct se termina cautarea,
deoarece P2 nu are pointeri de iesire.
Pe de alta parte, consideram ca facem o cautare pentru destinatia D’ ai
carui primii 5 biti sunt 11000. Inca odata, primul bit de 1, e folosit pentru a
ajunge la nodul ce-l contine pe P4. P4 este tinut minte ca ultimul prefix
contorizat, si pointerul 1 e urmarit pentru a ajunge la nodul cel mai din
dreapta de adincime 2.
Algoritmul urmareste acum al treilea bit din D’ (un 0) spre nodul sirului
text care contine ”01”. Astfel ne amintim ca P9 a fost ultimul prefix
contorizat. Al patrulea bit a lui D’ este 0, care se potriveste cu primul bit din
”01”. Dar, al cincilea bit a lui D’ este 0 (si nu 1 cum e al doilea bit din ”01” ).
Astfel cautarea se termina cu P9 ca cea mai lunga potrivire.
Literatura despre arborii ordonati nu foloseste sirurile text pentru a
comprima ramurile cu cale unica (fig.11.7).In schimb schema clasica
Patricia trie / testul Patricia, foloseste un contor de omisiuni / skip count.
Acest contor memoreaza numarul de biti din sirul text corespunzator, si nu
bitii in sine.De exmplu, ”01” din exemplul nostru va fi inlocuit cu contorul
de omisiuni ”2” in testul Patricia.
Algoritmul lucreaza bine atata timp cat testul Patricia e folosit pentru
pentru potriviri exacte, pentru care a si fost initial creat.Cand ajunge cautarea
la la un nod cu contor de omisiuni, omite numarul de biti potriviti si
urmareste pointerul nodului contorului de omisiuni pentru a continua
cautarea. Deoarece bitii omisi nu sunt comparati pentru potrivire, Patricia
pretinde ca la sfarsitul cautarii sa se faca o comparare completa dintre cheile
cautate si intrarile gasite de Patricia.
Din pacate, algoritmul lucreaza foarte prost pentru potrivirea prefixului/
prefix matching, pentru care Patricia nu a fost proiectat la inceput. De
exemplu, la cautarea lui D’, ai carui primii 5 biti sunt 11000 in echivalentul
lui Patricia a figurii 11.7, cautarea ar trebui sa omita ultimii doi biti si sa o ia
spre P3. In acest punct compararea va gasi ca P3 nu se potriveste cu D’ .
Cand se intampla acest lucru o cautare in arborele Patricia trebuie sa
revina inapoi si sa retesteze arborele pentru o posibila potrivire mai scurta. S-
ar parea ca in acest exemplu, cautarea ar trebui sa-si aminteasca P4. Dar
daca P4 a fost de asemenea contorizat pe calea care contine nodurile
contoarelor de omisiuni, algoritmul nu poate fi chiar sigur de P4. Astfel el
trebuie sa revina ca sa verifice daca P4 este corect.
Din pacate, implementarea BSD a inaintarii IP a decis sa foloseasca arborii
Patricia, ca baza pentru cea mai buna potrivire de prefix. Astfel,
implementarea BSD a folosit contoarele de omisiuni; de asemenea
implementarea memoreaza prefixele umplandu-le cu zerouri. Prefixele au
fost depozitate in frunzele arborelui, in loc sa fie in interiorul nodurilor, cum
e aratat in fig.11.7. Rezultatul e ca potrivirea prefixului poate, in cel mai rau
caz, sa duca la o reluare/ backtracking a arborelui pentru cazul cel mai
defavorabil de 64 de accesari ale memoriei (32 in josul arborelui si 32 in
susul arborelui).

163
Avand alternativa simpla a folosirii sirurilor text pentru evitarea
backtracking-ului, contoarelor de omisiuni sunt o idee proasta; in esenta,
deoarece transformarea contoarelor de omisiuni nu conserva informatia, in
timp ce transformarea sirurilor text o conserva. Totusi, din cauza influentei
enorme a BSD-ului, unii producatori si chiar algoritmi au aplicat contoarele
de omisiuni in implementarile lor.

11.5 Arbori ordonaţi multibit


Majoritatea memoriilor mari folosesc DRAM-uri; acestea au latenta
mare( ≈ 60 nsec), comparativ cu timpul de acces la registre (2-5 nsec).
Deoarece la arborii ordonati unibit testarea poate necesita 32 de accesari ale
memoriei pentru un prefix de 32 de biti, timpul de cautare in cazul cel mai
defavorabil la arborii unibit e de minim 32 ∗ 60 = 1,92μ sec , ceea ce
motiveaza cautarea in arborii ordonati multibit.
Pentru a face cautarea cu pasi/strides de 4 biti, principala problema este de
a lucra cu prefixe ca 10101 ∗ (cu lungimea 5 ), a caror lungime nu e multiplu
de 4 lungimea aleasa a pasilor. Cum putem fi siguri ca nu omitem prefixe ca
10101∗ , atunci cand la un moment dat cautam 4 biti ? Problema se rezolva
cu expansiunea controlata a prefixului, prin transformarea bazei de date de
prefixe existenta intr-o baza de date noua, cu mai putine prefixe dar posibil
cu mai multe prefixe. Eliminarea tuturor lungimilor care nu sunt multipli de
lungimea pasului ales, expansiunea permite o cautare multibit mai rapida, cu
pretul cresterii dimensiunii bazei de date.

Figura 11.8 Expansiunea controlata a bazei de date de prefixe originala (stanga, cu cinci
lungimi de prefixe 1,3,4,5,6) la o baza de date expandata (dreapta, cu doar doua lungimi de
prefixe 3 si 6).

De exemplu, extragand prefixele impare vechi se reduce numarul de


prefixe, de la 32 la 16, ceea ce ar permite cautarea in arbore a cate doi biti la
un moment dat. Pentru a extrage un prefix ca 101 ∗ de lungime 3, se observa
ca 101∗ reprezinta adresele care incep cu 1010 ∗ sau cu 1011∗ . Astfel 101∗
(de lungime 3) poate fi inlocuit de doua prefixe de lungime 4 ( 1010 ∗ si
1011 ∗ ), ambele mostenind intrarile de inaintare spre nodul/hop urmator
101∗ .
Dar prefixele extinse pot intra in coliziune cu un prefix existent care are
noua lungime.In acest caz se renunta la noul prefix expandat, si se da
prioritate vechiului prefix deoarece el a avut de la inceput lungimea mai
mare.
In esenta, expansiunea face un compromis memorie/timp in favoarea
timpului (P4b). Aceeasi idee poate fi folosita pentru a indeparta orice set ales
de lungimi, cu exceptia lungimii 32. Deoarece cautarea in arbore depinde
liniar de numarul de lungimi, expandarea reduce timpul de cautare.
Consideram mostra de baza de date din fig.11.6, cu noua prefixe, P1-P9.
Aceasta e trecuta si in stanga figurii 11.8. Baza de date din dreapta figurii
11.8 este o baza de date echivalenta, construita prin expandarea bazei de
date originale, pentru a contine doar prefixe de lungimea 3 si 6. De

164
mentionat este faptul ca prin expandarea lui P6= 1000 ∗ de la patru biti la
sase biti, se intra in coliziune cu P7= 100000 ∗ si astfel se renunta la P6.

11.5.1 Arbori ordonati cu pas fix


In fig.11.9 e prezentat un arbore ordonat pentru aceeasi baza de date ca cea
din fig.11.8, folosind un arbore expandat cu pas fix, de lungime 3. Astfel
fiecare nod al arborelui foloseste 3 biti. Intrarile replicate in nodurile
arborelui din fig.11.9 corespund exact prefixelor expandate din dreapta
figurii 11.8. De exemplu, P6 din fig.11.8 are trei expandari
(100001,100010,100011) .
Aceste trei prefixe expandate indica spre pointerul 100 a nodului radacina
din fig.11.9 (deoarece toate cele trei prefixe expandate incep cu 100 ) si sunt
memorate in intrarile 001, 010, 011 ale copilului din dreapta nodului
radacina. De mentionat este ca intrarea 100 in nodul radacina are prefixul
memorat P8 (pe langa faptul ca pointerul indica spre expansiunea lui P6),
deoarece P8= 100 ∗ este el insusi un prefix expandat.

Figura 11.9 Arbore ordonat expandat (care are doi pasi de cate 3 biti) corespunzator bazei
de date de prefixe din fig.11.8.

Astfel, fiecare element al nodului arborelui e o inregistrare continand doua


intrari: prefixul memorat si un pointer. Cautarea in arbore proceseaza 3 biti
la un moment dat de timp.De fiecare data cand e urmat un pointer, algoritmul
isi reaminteste prefixul memorat (daca are). Cand se termina cautarea la un
pointer vid, este returnat ultimul prefix memorat din cale.
De exemplu, daca adresa D incepe cu 1110 , cautarea pentru D incepe la
intrarea 111 a nodului radacina, care nu are un pointer de iesire ci doar un
prefix memorat (P2). Astfel, cautarea pentru D se termina cu P2. O cautare
pentru o adresa care incepe cu 100000 urmareste pointerul 100 in radacina
(si-si reaminteste P8). Acesta indica spre nodul de mai jos din dreapta, unde
intrarea 000 nu are pointer de iesire ci doar prefix memorat(P7).Astfel
cautarea se termina cu rezultatul P7. Atat pointerul cat si prefixul memorat
pot fi regasite printr-un singur acces la memorie folosind memorii mari (
P5b).
Un caz special a arborilor ordonati cu pas fix,foloseste pasi ficsi de 24,4 si
4.Autorii, Guptas.a., observa ca DRAM-urile cu mai mult de 224 de locatii
devin accesibile, astfel ca devine fezabila si cautarea cu pas de 24 de biti.

11.5.2 Arbori ordonati cu pas variabil


In figura 11.9, nodul frunza cel mai din stanga trebuie sa memoreze
expandarile lui P3= 11001∗ , iar nodul frunza cel mai din dreapta trebuie sa
memoreze P6 ( 1000 ∗ ) si P7 ( 100000 ∗ ). Astfel, dincauza lui P7, nodul
frunza cel mai din dreapta trebuie sa examineze mai mult de 2 biti, deoarece
P3 contine doar 5 biti, si oasul radacinii eate de 3 biti. Exista un grad de
libertate suplimentar care poate fi optimizat (P13).
In arborii ordonati cu pas variabil, numarul de biti examinati de fiecare nod
al arborelui poate varia, chiar si pentru noduri de acelasi nivel. Pentru a face
acset lucru, pasul nodului arborelui e codat cu un pointer spre nod. Figura
11.9 poate fi transformata inlocuind nodul cel mai din stanga cu un tabel cu

165
patru elemente si codand lungimea 2 cu pointerul spre nodul cel mai din
stanga. Codarea pasului necesita 5 biti. Dar, arborele ordonat cu pas variabil
din fig.11.10 are cu patru intrari in tabela mai putine decat arborele din
fig.11.9.
Exemplul motiveaza alegerea pasilor pentru minimizarea volumul de
memorie al arborelui. Deoarece expandarea face un compromis memorie-
timp, de ce sa nu minimizamnecesarul de memorie prin optimizarea gradului
de libertate (P13), pasii folositi in fiecare nod ? Pentru alegerea pasilor
variabili, proiectantul trebuie sa specifice mai intai numarul de accesari ale
memoriei in cazul cel mai defavorabil.De exemplu, cu un pachet de 40 de
octeti la 1 Gbps si un DRAM de 80 nsec, avand bugetul de timp de 320
nsec, sunt permise doar patru accesari ale memoriei. Acest lucru
conditioneaza numarul maxim de noduri in orice cale de cautare (patru
pentru exemplul considerat).
Daca inaltimea arborelui este data, pasul poate fi ales pentru a minimiza
memoria. Acest lucru poate fi facut in cateva secunde folosind programarea
dinamica, chiar si pentru baze mari de date de 150.000 de prefixe. Un grad
de libertate(pasii), e optimizat pentru minimizarea memoriei folosita pentru o
inaltime data cea mai defavorabila.
Un arbore ordonat se spune ca e optimal pentru o inaltime h si o baza de
date D, daca arborele are cel mai mic spatiu de memorare dintre toti arborii
ordonati cu pas variabil pentru baza de date D, si inaltimea nu mai mare
decat h. Se arata usor ca arborele din fig.11.10 e optimal pentru baza de date
din stanga figurii 11.8 si inaltimea 2.
Problema generala a alegerii pasul optim poate fi rezolvata recursiv (fig
11.11). presupunem ca inaltimea arborelui trebuie sa fie 3. Algoritmul alege
mai intai o radacina cu pasul s. Cei y = 2s pointeri posibili din radacina pot
conduce spre y subarbori nevizi T1 ,..., Ty . Daca multimea de s pointeri indica
spre subarborele Ti , atunci toate prefixele din baza de date originala D care
incep cu pi trebuie memorate in Ti . Numim setul de prefixe Di .
Presupunem ca putem gasi recursiv Ti optimal pentru inaltimea h − 1 si
baza de date Di . Avand un acces la memorie folosit la nodul radacina, au
ramas doar h − 1 accese la memorie pentru a parcurge fiecare subarbore Ti .
Notam cu Ci necesarul de spatiu de memorare, numarat in locatii de tabele,
pentru Ti optimal. Atunci, pentru un pas s al radacinii fixat, costul arborelui
optimal rezultat C ( s ) este 2 s (costul nodului radacina in locatii de tabel)plus
y

∑ C . Astfel, valoarea optimala a pasului initial este valoarea lui s , unde


i =1
i

1 ≤ s ≤ 32 , care minimizeaza s.
O folosire naiva a recursivitatii duce la subprobleme repetate. Pentru a
evita subproblemele repetate. Algoritmul construieste mai intai un arbore
ordonat 1-bit.De mentionat este ca oricare subarbore ordonat Ti (fig.11.11)
trebuie sa fie un subarbore N al arborelui 1-bit. Apoi, algoritmul foloseste
programarea dinamica pentru construirea costului optim si pasii arborelui
pentru fiecare subarbore N din arborele original 1-bit, pentru toate valorile
1-h ale inaltimii, construind de jos in sus, de la subarborele de cea mai mica
166
inaltime spre subarborele de cea mai mare inaltime. Rezultatul final sunt
pasii optimi pentru radacina (subarborelui 1-bit) cu inaltimea h.
( )
Complexitatea finala a algoritmului este O N ∗W 2 ∗ h , unde N este
numarul prefixelor originale din baza de date originala, W este latimea
adresei destinatie, iar h este ponderea dorita pentru cazul cel mai defavorabil.
Aceasta este deoarece exista N ∗ W subarbori in arborele 1-bit, fiecare din ei
trebuie rezolvat pentru inaltimi in gama 1-h, si fiecare solutie implica o
minimizare fata de cel mult W alegeri posibile, pentru pasul initial s.
Complexitatea este liniara fata de N (numarul cel mai mare cunoscut actual
fiind de 150.000)si h, (care ar trebui sa fie mic, maxim 8), dar patratica ca
functie de latimea adresei (32, in mod curent). In practica, dependenta
patratica de latimea adresei nu e un factor major.
De exemplu, pentru o inaltime de 4, baza de date optimizata Mae-East
necesita 432 KB, fata de 2003 KB a variantei neoptimizate. Varianta
neoptimizata foloseste pasii „naturali”de 8,8,8,8 . Programul dinamic ia 1,6
sec timp de rulare pe un Pentium Pro de 300 MHz. Programul dinamic poate
fi chiar mai simplu pentru arborii cu pas fix, rularea durand doar 1msec, dar
necesita 737KB in loc de 423 KB.
Dar 1,6 msec este mult prea mult ca programul dinamic sa ruleze la fiecare
actualizare si mai permite actualizari de ordinul milisecundelor. Cu toate
acestea, instabilitatile backbone sunt cauzate de patologii de genul in care
acelasi set de prefixe S sunt inserate repetat si sterse de ruterul care e
temporar suntat. Deoarece a trebuit sa se aloce memorie pentru intregul set,
inclusiv S, oricum, faptul ca arborele este suboptimal la folosirea sa a
memoriei cand S este sters, este irelevant. Pe de alta parte, rata la care noile
prefixe sunt sterse sau adaugate de manager, pare ca e mai probabil ca acest
lucru sa fie facut in ordinea zilelor. Astfel, un program dinamic care ia
cateva secunde timp de rulare, e rezonabil sa fie rulat zilnic, si nu va afecta
timpul pentru cazul cel mai defavorabil de inserare/stergere, permitand arbori
optimali rezonabili.

11.5.3 Actualizarea incrementala


Pentru arborii ordonati multibit exista algoritmi simpli de inserare/stergere.
Consideram adaugarea unui prefix P. Algoritmul simuleaza mai intai
cautarea unui sir/string de biti in noul prefix P, pina la si inclusiv ultimul pas
complet din perfixul P. Cautarea se termina fie prin terminarea cu ultimul
pas (posibil incomplet) sau prin ajungerea la un pointer vid/nil.Astfel, pentru
adaugarea lui P10= 1100 ∗ bazei de date din fig.11.9, cautarea urmeaza
poiterul 110 si termina la frunza cea mai din stanga a arborelui ordonat a
nodului X.
Pentru scopul inserarii/stergerii, pentru fiecare nod X a arborelui ordonat
multibit, algoritmul mentine un arbore 1-bit corespondent, cu prefixele
memorate in X. Aceasta structura auxiliara trebuie sa nu fie in memoria
rapida. Astfel, pentru fuecare element al tabelului de noduri, algoritmul
memoreaza lungimea celei mai bune potriviri ale sale prezenta.Dupa ce se
determina ca P10 trebuie adaugat nodului X, algoritmul expandeaza P10
spre pasul lui X. Orice element al tabelului spre care P10 este expandat (care
este etichetat curent cu un prefix avand o lungime mai mica decat P10)
trebuie suprascris cu P10.

167
Asfel, la adaugarea lui P10= 1100 ∗ , algoritmul trebuie sa adauge
expansiunea lui 0 ∗ inspre nodul X. In particular, intrarile 000 si 001 in
nodul X trebuie actualizate sa fie P10.
Daca se termina cautarea inainte de a atinge ultimul pas in prefix,
algoritmul creaza un nou nod al arborelui ordonat. De exemplu, daca prefixul
P11= 1100111 ∗ e adaugat, cautarea esueaza la nodul X, cand se gaseste un
pointer vid/nil la intrarea 011. Algoritul creaza atunci un nou pointer la
aceasta locatie, care e facut sa indice spre un nou nod al arborelui care
contine P11. Apoi P11 este expandat in acest nod nou.
Stergerea este similara adaugarii. Complexitatea inserarii si a stergerii este
data de timpul de realizare a unei cautari ( O (W ) ) plus timpul de reconstruire
completa a nodului (O ( S ) , unde S este dimensiunea maxima a nodului
arborelui). De exemplu, folosind noduri ale arborelui de 8-biti, acest al
doliea cost va necesita scanarea a aproximativ 28 = 256 intrari de nod ale
arborelui. Astfel, pentru a permite actualizari rapide, e crucial sa limitam de
asemenea dimensiunea oricarui nod din programul dinamic descris anterior.

11.6 Arbori ordonati comprimati pe nivel, LC(Level-Compressed


Tries)
Un arbore ordonat LC este un arbore comprimat cu pas variabil in care
fiecare nod al arborelui contine intrari nevide. Un arbore ordonat LC e
construit gasind mai intai pasul celei mai mari radacini care permite intrari
non-vide si apoi se repeta recursiv procedura in subarborii copii. Un exemplu
al acestei proceduri e dat in figura 11.12, pornind cu un arbore 1-bit in stanga
si rezultand intr-un arbore LC in stanga. P4 si P5 formeaza cel mai mare
subarbore cu radacini complete posibil – daca pasul radacinii este 2, atunci
primele doua intrari de tabele vor fi goale. Motivul este evitarea elementelor
goale de tabel, pentru a minimiza spatiul de memorare.
Arborii cu pas variabil pot fi perfectionati in continuare, permitand ca
memoria sa fie tratata pentru viteza. De exemplu, reprezentarea arborelui
ordonat LC folosind baza Mae-East (1997) are o inaltime de arbore de 7 si
un necesar de 700 KB de memorie. Prin comparatie, un arbore ordonat cu
pas variabil are o inaltime a arborelui de 4 si necesita 400 KB de memorie.
Reamintim deasemenea ca pasul variabil optim calculeaza cel mai bun
arbore pentru o inaltime tinta, si astfel ar putea intr-adevar produce un arbore
ordonat LC daca arborele LC ar fi optimal pentru inaltimea sa.

Figura 11.12 Schema arborelui ordonat LC descompune recursiv arborele 1-bit in


subarbori completi/full de cea mai mare dimensiune posibila. (stanga). Copiii din fiecare
subarbore complet (cu linie punctata) sunt apoi plasati in nodul arborelui pentru a forma un
arbore cu pas variabil specific bazei de date alese.
Figura 11.13 Reprezentarea tabelului arborelui ordonat LC

In forma sa finala, nodurile arborelui ordonat LC cu pas variabil in ordinea


de pe prima latime (prima data radacina, apoi toate nodurile arborelui si
nivelul secund de la stanga la dreapta, apoi nodurile din al treilea nivel, etc.),
ca in fig.11.13. Asezarea tabelei si conditiile impuse pentru subarborii
completi fac ca actualizarea sa fie lenta in cazul cel mai defavorabil. De
exemplu, stergerea lui P5 din fig. 11.13 cauzeaza schimbari in
168
descompunerea in subarbori. Si mai rau este ca face ca aproape fiecare
element din reprezentarea tabelului din fig.11.13 sa migreze in sus.

11.7 Arbori ordonati comprimati de tip Lulea


Desi arborii ordonati LC si arborii ordonati cu pas variabil incearca sa
comprime arborii ordonati multibit variind pasul la fiecare nod, ambele
scheme au probleme. Desi folosirea tabelelor complete permit arborii
ordonati LC sa nu iroseasca de loc memoria cu locatii vide, ele cresc
inaltimea arborelui ordonat, care nu poate fi potrivita. Pe de alta parte,
arborii ordonati cu pas variabil pot fi potriviti sa aiba o inaltime scurta, cu
costul irosirii memoriei datorata locatiilor goale din tabel in nodurile
arborelui ordonat. Abordarea Lulea, descrisa acum, este o schema de arbore
ordonat multibit care foloseste noduri de arbore cu pas ficsi mari, dar si o
compresie bitmap pentru a reduce considerabil necesarul de memorie.
Sirurile cu repetitie (e.g. AAAABBAAACCCCC) pot fi comprimate
folosind un bitmap care denota punctele de repetitie (i.e.10001010010000)
impreuna cu o secventa comprimata(i.e.ABAC). Similar, nodul radacina din
fig.11.9 contine o secventa repetata (P5,P5,P5,P5) cauzata de expandare.
Schema Lulea evita aceasta irosire evidenta comprimand informatia
repetata folosind un bitmap si o secventa comprimata, fara o penalizare mare
la timpul de cautare. De exemplu, aceasta schema foloseste doar 160 KB de
memorie pentru a memora baza de date Mae-East, ceea ce permite ca sa fie
memorata intreaga baza de date intr-o SRAM on-chip scumpa. Costul platit
este timpul mare de inserare.
Unele intrari de arbore expandate (e.g. 110 intrari la radacina din fig. 11.9)
au doua valori, un pointer si un prefix. Pentru a face compresia mai usoara,
algoritmul porneste facand ca fiecare intrare sa aiba exact o valoare, prin
impingerea informatiei de prefix jos in frunzele arborelui. Deoarece frunzele
nu au pointer, avem doar informatia despre urmatorul hop la frunza si doar
pointerii la nodurile non-frunza. Acest procedeu se numeste leaf-pushing.
De exemplu, pentru a evita prefixe memorate in intrarea 110 a nodului
radacina din fig.119, prefixul memorat P9 este impins spre toate intrarile
nodului arborelui, cu exceptia intrarilor 010 si 011(ambele continua sa
contina pe P3). Similar, prefixul memorat P8 in intrarea 100 a nodului
radacina, este impins in jos spre intrarile 100,101,110 si 111 ale nodului cel
mai din dreapta a arborelui. Odata realizata aceasta etapa, fiecare intrare de
nod contine fie un prefix memorat, fie un pointer.

11.10 Căutarea binară a lungimii prefixului


Aici se adaptează o altă schemă clasică de potrivire perfectă,
dispersarea/hashing-ul, la potrivirea celui mai lung prefix. Căutarea
binară asupra lungimii prefixului duce la găsirea celei mai lungi potriviri
utilizând log 2 W dispersări, unde W reprezintă lungimea maximă a
prefixului. Aceasta poate asigura o soluţie scalabilă pentru adresarea pe
128 biţi de tip IPv6. Pentru prefixele de 128 de biţi, acest algoritm
utilizează 7 accese la memorie spre deosebire de 16 accese la memorie
dacă se utilizează un arbore multibit ordonat cu pasul de 8 biţi. In acest
scop, în prima fază, algoritmul sortează prefixele după lungime, în tabele
separate de dispersare. Mai precis, utilizează o zonă L de tabele de

169
dispersare, astfel încât L [i ] e un pointer spre tabela de dispersare care
conţine toate prefixele de lungime i.
Considerăm acelaşi tabel mic de rutare din figura 11.17, cu doar două
prefixe, P4 = 1* şi P1 = 101*, de lungime 1 şi respectiv 3. Acesta este un
subset al figurii 11.18. Zona tabelelor de dispersare este orizontală, în
partea de sus (A) a figurii 11.18. Tabela de dispersare de lungime 1,
conţinând prefixul P4, este verticală, în partea stângă şi indică spre
poziţia 1 din tabel; tabela de dispersare de lungime 3, conţinând prefixul
P1, este în partea dreapta şi indică spre poziţia 3 din tabel; tabela de
dispersare de lungime 2 este vidă, deoarece nu există nici un prefix de
lungime 2.
O căutare naivă a adresei D începe cu tabelul de lungimea cea mai
mare (3 în exemplu), extrage primii l biti din D în Dl şi apoi caută Dl
in tabelul de dispersie de lungime l. Dacă cautarea are succes, a fost
găsită cea mai bună potrivire; dacă nu, algoritmul ia în considerare
lungimea imediat mai mică (de ex. 2). Algoritmul caută, decrementând
lungimea posibilă a prefixului, până când găseşte o potrivire în mulţimea
prefixelor posibile, sau până când epuizează lungimile.
Schema naivă realizează o căutare liniară a lungimilor de prefixe
distincte. Analogia sugerează un algoritm mai eficient: căutarea binară
(P15). Spre deosebire de căutarea binară a domeniului/gamei de prefixe,
aceasta este o căutare binară asupra lungimii prefixelor. Diferenţa este
majoră. Cu 32 de lungimi, căutarea binară asupra lungimii durează 5
accesări în cel mai rău caz ; cu 32000 de prefixe căutarea binară asupra
gamei de prefixe durează 16 accesări. Căutarea binară trebuie să inceapa
de la lungimea mediana a prefixelor si fiecare dispersare trebuie sa
împartă în două lungimile posibile ale prefixelor. O căutare poate da
doar două rezultate: găsit sau negăsit. Daca s-a găsit o potrivire la
lungimea m, atunci lungimile strict mai mari decât m trebuie căutate
pentru o potrivire mai lungă. In mod corespunzător, dacă nu s-a gasit
nici o potrivire, căutarea trebuie sa continue printe prefixele de lungimi
strict mai mici decât m.
De exemplu, în figura 11.18.A, presupunem că începe căutarea la
lungimea medie-2 a tabelei hash, pentru o adresă care începe cu 101.
Evident că această căutare nu duce la găsirea unei potriviri. Dar există o
potrivire mai lungă în tabelul de lungime 3. Din moment ce doar găsirea
unei potriviri duce la translatarea căutării la jumătatea din dreapta,
trebuie introdusă o “potrivire artificială”sau marker/indicator care să
forţeze căutarea în jumătatea din dreapta în cazul în care există
posibilitatea unei potriviri mai lungi.
De aceea în partea B se introduce o intrare de marcare 10 reprezentată
îngroşat, care corespunde primilor doi biţi ai prefixului P1= 101 din
tabelul de lungime 2. In esenţă, starea a fost adaugată pentru creşterea
vitezei (P12).Markerul permite eşecuri de testare în zona mediană
pentru a exclude toate lungimile mai mari decât cea mediană.

170
Căutarea unei adrese D care începe cu 101 se face corect. Căutarea
pentru 10 în tabelul de lungime 2 (fig.11.18.B) duce la găsirea unei
potriviri ; căutarea continuă mai departe în tabelul de lungime 3, se
găseşte o potrivire cu P1 şi se încheie căutarea. In general, trebuie plasat
un marker pentru un prefix P, la toate lungimile prin care va trece
căutarea binară pentru găsirea lui P. Se adaugă doar un număr
logaritmic de marcatori. Pentru un prefix de lungime 32, markerii
necesari sunt doar la lungimile 16, 24, 28 şi 30.
Din păcate, algoritmul este încă incorect. In timp ce markerii duc la
prefixe posibil mai lungi, pot de asemenea să ducă la o căutare pe piste
greşite.Se consideră căutarea unei adrese D’ (fig.11.18.B) ai cărei primi
trei biţi sunt 100. Deoarece mijlocul tabelului conţine 10, căutarea în
tabela hash mijlocie conduce la găsirea unei potriviri. Acest lucru
forţează algoritmul să caute 100 în continuare în al treilea tabel ceea ce
va duce la eşuarea căutării. Cea mai bună potrivire corectă a prefixului
se află în primul tabel (P4 = 1*). Markerii pot cauza deraierea căutarii.
Dar, o revenire a căutării în jumătatea stângă duce la un consum liniar
de timp.
Pentru a asigura un timp de căutare logaritmic, fiecare nod marker
M va conţine o variabilă M.bmp, unde M.bmp reprezintă cel mai lung
prefix care se potriveşte cu şirul M. Această variabilă este precalculată
când M este introdus în tabela hash. Când algoritmul urmareşte markerul
M şi caută prefixele de lungime mai mare decât M, dacă eşuează în
găsirea unui asemenea prefix mai lung, atunci răspunsul este M.bmp. In
esenţă, cel mai potrivit prefix asociat fiecărui marker este precalculat
(P2a). Acest lucru evită căutarea tuturor lungimilor mai mici decât
lungimea medie dacă potrivirea se face cu ajutorul markerului.

171
Versiunea finală a bazei de date ce conţine prefixele P4 şi P1 este
prezentată în fig.11.18.C. A fost adăugat un câmp bmp markerului 10
care indică spre cea mai bună potrivire de prefix a şirului 10 (ex. P4 =
1*). De aceea, când algoritmul caută 100 şi găseşte o potrivire în tabelul
median de lungime 2, îşi aduce aminte valoarea bmp corespunzătoare
intrării P4, înainte de a căuta în tabelul de lungime 3. Când căutarea
eşuează (în tabelul de lungime 3), algoritmul returnează câmpul bmp al
ultimului marcator întâlnit (ex. P4).
Un algoritm banal pentru construirea unei căutări binare a structurii
de date din memoria de lucru/scratch este următorul. Prima dată se
determină lungimile de prefix distincte; se determină astfel secvenţa de
lungimi de căutat. Apoi se adaugă fiecare prefix P tabelei hash
corespunzător lungimii P, length(P). Pentru fiecare prefix, se adaugă de
asemenea un marker fiecărui tabel corespunzător lungimilor L <
lungimea (P) pe care căutarea binară le va parcurge (dacă nu există una
deja). Pentru fiecare astfel de marker M se utilizează o testare
suplimentară 1-bit pentru determinarea prefixului cu cea mai bună
potrivire a lui M.
In timp ce acest algoritm de căutare utilizează cinci căutări în tabelele
hash, în cel mai defavorabil caz pentru IPv4, în cele mai probabile cazuri
majoritatea căutărilor ar trebui să aibă două accesări ale memoriei.
Aceasta se datorează observării cazului aşteptat O1 care arată că
majoritatea prefixelor sunt de lungine 16 sau 24 de biţi. De aceea căutarea
binară la 16 iar apoi la 24 de biţi va fi suficientă pentru majoritatea
prefixelor.
Utilizarea hashing-ului face căutarea binară asupra lungimii prefixelor să
fie într-o oarecare măsură dificil de implementat hardware. Oricum,
scalabilitatea tehnicii la lungimi mari de prefixe, cum sunt adresele IPv6,
a făcut-o suficient de atractivă pentru anumiţi producători.

11.11 Alocarea memoriei în scheme comprimate


Cu excepţia căutării binare şi a arborilor cu prefix/ordonati multibit cu
pas fix, multe din schemele descrise necesită alocări de memorie de
diferite dimensiuni. De aceea, dacă un nod al arborilor cu prefix
comprimat creşte de la două la trei cuvinte de memorie, algoritmul de
inserare trebuie să dezaloce vechiul nod de dimensiune 2 şi să realoce
noul nod de dimensiune 3. Alocarea memoriei în sistemele de operare
este oarecum euristică, utilizând algoritmi cum sunt best-fit si worst-fit
(cea mai bună/proastă potrivire) care nu garantează propietaţile cazului
cel mai defavorabil.
De fapt toate metodele standard de alocare a memoriei pot avea un caz
cel mai defavorabil, în care raportul de fragmentare a memoriei este
foarte rău. Este posibil ca locatorii şi delocatorii de memorie să
conlucreze pentru a fragmenta memoria în petice de zone goale şi
blocuri de memorie alocate de dimensiuni reduse. Dacă Max este
dimensiunea maximă de memorie a unei cereri de alocare, cazul cel mai
defavorabil apare când toate spaţiile libere de memorie au dimensiunea
Max – 1 şi toate blocurile alocate au dimensiunea 1. Această situaţie
poate să apară prin alocarea întregii memorii utilizând cereri de alocare
de dimensiune 1, urmate de dealocările corespunzatoare. Rezultatul
172
final este că doar 1/Max din memorie este utilizată sigur, deoarece toate
cererile de alocare viitoare pot fi de dimensiune Max.
Utilizarea locatorului de memorie o translatează direct în numărul
maxim de prefixe pe care un chip de căutare o poate suporta.
Presupunem că, ignorând locatorii, 20MB de memorie on-chip poate
suporta 640.000 de prefixe în cel mai defavorabil caz. Dacă ţinem
seama de locator, şi Max = 32, chip-ul poate garanta suportarea a doar
20.000 de prefixe.
Nu ajută la nimic că vânzătorii avertizau asupra celui mai defavorabil
caz de 100.000 de prefixe cu un timp de căutare de 10 nsec şi un timp
de actualizare de o microsecundă. Aşadar, având în vedere că algoritmii
de căutare a prefixelor comprimă frecvent structurile de date pentru a
încăpea într-o memorie SRAM, aceşti algoritmi trebuie şi să proiecteze
o alocare rapidă a memoriei şi cu o fragmentarea minimă.
Exista o limită care spune că: locatorii care nu compactează memoria
1
nu pot avea un raport al utilizării mai bun decât . De exemplu,
log 2 Max
acesta este 20 % pentru Max = 32. Deoarece această valoare este
inadmisibilă, soulţiile algoritmice ce implică structuri comprimate de
date trebuie să utilizeze compactarea memoriei. Compactarea memoriei
constă în mutarea blocurilor de memorie alocate, astfel încât să se
mărească dimensiunea spaţiilor libere din memorie.
Compactarea este puţin folosită în sistemele de operare din motivul
următor. Dacă o porţiune de memorie M este mutată, atunci trebuie
corectaţi toţi indicatorii care indică spre M. Din fericire, majoritatea
structurilor de căutare sunt structuri de tip arbore, în care oricare nod
este indicat ca vecin a cel mult unui nod. Menţinând un nod părinte
pentru fiecare nod al arborelui, nodurile care indică spre un nod M al
arborelui pot fi modificate corespunzător când M este realocat. Din
fericire, indicatorul părinte este necesar doar pentru actualizare si nu
pentru căutare. Deci, indicatorul părinte poate fi memorat într-o copie
externa chip-lui, a bazei de date utilizată pentru actualizări în procesorul
de dirijare, fără a ocupa memoria SRAM internă chip-lui.
Chiar după rezolvarea aceastei probleme, este necesară existenţa unui
algoritm simplu, care să decidă când să se compacteze o anumită
porţiune a memoriei. In literatura de specialitate există tendinţa de a
utiliza compactori globali, care pot scana toată memoria într-un singur
ciclu de compactare. Pentru a limita timpii de intercalare trebuie să
existe o formă de compactare locală, care compactează doar o mică
porţiune de memorie, din jurul regiunii afectate de actualizare.

11.11.1 Compactarea pe cadre


Pentru a arăta cât de simple pot fi schemele de compactare locale, e
descrisă mai întâi o schemă foarte simplă care, deşi face o compactare
minimă, acoperă totuşi 50% din cele mai defavorabile cazuri de utilizare
a memoriei. Apoi se va urmări îmbunătăţirea utilizării memoriei până la
o valoare cât mai apropiată de 100%.

173
In fuzionare cadrelor se presupune că toate cele M cuvinte de
M
memorie sunt divizate în cadre de dimensiunea Max. Fuzionarea
Max
cadrelor caută să păstreze utilizarea memoriei la cel puţin 50%. In acest
scop toate cadrele nevide ar trebui să fie cel putin 50% pline.
Fuzionarea cadrelor păstrează urmatoarea invarianţă : toate cadrele
neumplute cu excepţia unuia singur sunt cel putin 50% pline. Dacă
M
lucrurile stau în felul acesta şi este mult mai mare decât 1, atunci
Max
utilizarea garantată va fi de aproape 50%.
Cererile de alocare şi dealocare sunt realizate cu ajutorul etichetelor
ataşate fiecărui cuvânt, care ajută la identificarea blocurilor de memorie
libere şi ocupate. Singura restricţie suplimentară este că toate spaţiile
libere să fie continute în interiorul cadrelor; spaţiilor libere nu li se
permite să se extindă înafara cadrelor.
Un cadru este imperfect/defect dacă nu este gol şi are utilizarea mai
mică de 50 %. Pentru a se menţine invarianţa, fuzionarea cadrelor
conţine un indicator suplimentar care să urmareasca cadrele curente
imperfecte, daca există. Acum, o alocare poate face ca un cadru anterior
gol să devină un cadru imperfect dacă alocarea este mai mică decât
Max
.
2
Similar, o dealocare poate face ca un cadru plin mai mult de 50% sa
devină plin mai puţin de 50%. Se consideră un cadru care conţine două
blocuri alocate de dimensiune 1 şi respectiv Max – 1 şi deci are o
1
utilizare de 100%. Utilizarea s-ar putea reduce la dacă blocul de
Max
dimensiune Max – 1 este dealocat. Acest lucru ar putea face ca cele
două cadre să devină imperfecte, fapt ce ar încălca regula de invarianţă.
In continuare e dat un artificiu simplu pentru a păstra invarianţa. Se
presupune că există deja un cadru imperfect F şi că apare un nou cadru
imperfect F’. Invarianţa este păstrată prin fuzionarea conţinutului
cadrelor F şi F’ într-un nou cadru F. Acest lucru este posibil deoarece
ambele cadre F şi F’ au fost ocupate într-o proporţie mai mică de 50%.
De reţinut este faptul că singura compactare utilizată este cea la nivel
local şi este limitată la două cadre imperfecte F şi F’. Asemenea
compactare locală duce la obţinerea unor timpi mici de actualizare.
Gradul minim de utilizare în cazul fuzionării cadrelor poate fi
îmbunătăţit prin creşterea dimensiunii cadrelor la kMax şi prin
schimbarea definiţiei cadrelor imperfecte, astfel încât utilizarea acestor
k
cadre să devină mai mică decât . Schema descrisă anterior este un
k +1
caz special în care k=1. Crescând valoarea lui k se imbunătăţeşte
utilizarea, cu costul creşterii compactării.

11.12 Modelul chip-lui de căutare


Având în vedere creşterea vitezelor la cele din standardul OC-786,
schemele de căutare vor fi probabil implementate mai degrabă on-chip
decat în procesoarele de reţea, cel puţin la viteze ridicate. Figura 11.19
174
descrie un model al chip-lui de căutare/lookup care realizează căutarea
şi actualizarea. Chip-ul are un proces de Căutare şi unul de Actualizare,
amândouă accesând o memorie comună de tipul SRAM care poate fi
încorporată în chip sau poate fi plasată în exteriorul acestuia.
Procesul de Actualizare permite actualizări incrementale şi posibil,
realizarea alocării/dealocării memoriei, şi o compactare locală redusă
pentru fiecare actualizare. El poate fi implementat fie complet on-chip,
fie parţial /complet prin soft. Dacă o companie producătoare de
semiconductori doreşte să vândă un chip de căutare care utilizează un
algoritm complex de actualizare (de ex. pentru schemele comprimate),
ar fi de recomandat să furnizeze şi un algoritm de actualizare
implementat hardware. Daca chip-ul de căutare face parte dintr-un
motor de expediere/forwarding, ar fi mai simplu să se retrogradeze
complet procesul de actualizare pe un CPU separat.

Fiecare acces la memoria SRAM poate fi destul de mare dacă e


nevoie, chiar până la 1000 de biţi. Acest lucru este posibil de
implementat în zilele noastre utilizându-se o magistrală extinsă. Logica
de căutare şi actualizare poate procesa cu uşurinta 1000 de biţi în paralel
într-un singur ciclu de memorie. Accesul la cuvinte de dimensiuni mari
poate fi de folos, de exemplu, în căutarea binară, pentru a reduce timpii
de căutare.
Timpii de Căutare şi Actualizare sunt multiplexaţi pentru a se
împărţi accesul la memoria comună SRAM care conţine baza de date de
căutare. Aşadar procesului de Căutare i se permit S accese consecutive
la memorie, iar apoi procesului de Actualizare i se permit K accese la
memorie. Dacă S este 20 iar K este 2, se permite ca Actualizarea să
“fure” câteva cicluri de acces de la procesul Căutare prin scăderea
vitezei de căutare pentru o fracţiune din timp. De notat o creştere a
latenţei în cazul căutării de k ori în cel mai defavorabil caz. Deoarece
procesul Căutare este proiectat în tehnica pipeline, această întârziere
poate fi considerată o mică întârziere pipeline suplimentară.
Chip-ul are pini pentru semnalele de Căutare şi Actualizare şi poate
furniza rezultatele căutării la ieşiri. Modelul poate fi proiectat pentru
diferite tipuri de căutări, inclusiv căutarea IP, căutarea adreselor fizice
(adresele MAC pe 48 biţi ca şi chei şi porturile de ieşire ca rezultate) şi

175
clasificare (de ex. antetele pachetelor ca şi chei şi regulile de potrivire ca
rezultate).
Oricare adăugare sau ştergere a unei chei poate duce la cererea unei
dealocări a unui bloc şi de alocare a unui bloc de dimensiune diferită.
Fiecare cerere de alocare poate fi în domeniul 1 la Max cuvinte de
memorie. Sunt în total M cuvinte de memorie care pot fi
alocate.Memoria actuală poate fi incorporată în chip, în exteriorul chip-
lui sau ambele cazuri.Chiar dacă se utilizează opţiunea de memorare
off-chip, primele niveluri ale oricărui arbore de căutare vor fi memorate
în interiorul chip-lui. Memoria on-chip este mai atractivă din punct de
vedere al costului şi al vitezei. Din păcate, memoria on-chip este
limitată de procesele concurente la aproximativ 32 Mbiţi. Acest lucru
face dificilă suportarea unei baze de date de 1 milion de prefixe.
Intern, chip-ul va utiliza din plin tehnica pipeline. Structura de date
de căutare este partiţionată în câteva bucăţi, fiecare din aceste bucăţi
fiind prelucrată concurent de către niveluri diferite ale logicii. Deci
memoria SRAM va fi probabilîimpărţită în câteva memorii SRAM mai
mici care pot fi accesate independent de către fiecare etapă a pipeline.
Exista o problemă referitoare la partiţionarea statică a memoriei
SRAM între nivelurile pipeline, datorită faptului că necesarul de
memorie pentru fiecare nivel poate varia pe măsură ce prefixele sunt
inserate şi şterse. O soluţie posibilă este aceea de a împărţi memoria
SRAM într-un număr mare de bucăţi care pot fi alocate dinamic
nivelurilor, prin intermediul unui switch crossbar de partiţionare.
Proiectarea unui asemenea switch crossbar care să lucreze la viteze mari
este dificilă.
Toate schemele descrise pot utiliza metoda pipeline, deoarece toate
se bazează pe structuri de date de tip arbore. Toate nodurile de la acelaşi
nivel a arborelui pot fi alocate aceluiaşi nivel pipeline.

11.13 Concluzii
Situaţia actuală din domeniul căutarii/lookup şi principiile de bază utilizate:
Starea actuală a lookups. Schemele de căutare au apărut din cauza
ruterelor de tranzit (core) la care cresc rapid atât dimensiunile tabelei
(până la 1 milion de prefixe) cât şi viteza (până la 40Gbps). MPLS
(Multi Protocol Label Switching) s-a considerat ca fiind o modalitate de
finisare a tehnicilor de căutare, iar în momentul de faţă este utilizat
pentru evitarea clasificării pachetelor din ingineria traficului. CMS-le
sunt înlăturate chiar şi din nucleele ruterelor, dar costurile ridicate,
puterea consumată a CAM-lor de dimensiuni mari rămâne o problemă.
Asa ca mulţi producători de rutere încă utilizează şi proiectează
algoritmi de căutare.
Desi e ciudat, după ce o bogăţie de algoritmi au fost exploraţi în acest
capitol, arborii simpli cu prefix unibit împreună cu memoria SRAM şi
tehnica pipeline lucrează bine chiar şi la viteze de 40 Mbps. Lucrul se
datorează faptului că arborii simpli cu prefix unibit cu comprimarea căii
sunt relativ compacti; timpii mari de căutare pot fi deplasaţi prin
pipeline împreună cu o zonă de căutare iniţială. Folosirea pipeline este
dificilă deoarece necesită partiţionarea memoriei între niveluri.

176
La viteze de până la 10 Gbps, arborii simpli simpli cu prefix multibit
ce utilizează extinderea controlată a prefixelor, lucrează bine cu
memoriile de tip DRAM. Memoriile DRAM sunt mai încete, dar sunt
ieftine şi uşor de găsit. Tehnicile RAMBUS pot permite utilizarea
pipeline în căutări/lookup chiar şi cu procesoare de reţea. Simplitatea
acestor scheme s-a dovedit atractivă pentru mulţi producători.
Unii producători utilizează căutarea binară a cărei viteze şi utilizare a
memoriei sunt rezonabile, în mod special în cazul arborilor-B cu
memorie extinsă pentru a se reduce înălţimea arborilor. Schemele de
căutare binară a gamelor nu sunt patentate.
La viteze mai mari, numărul de niveluri pipeline poate fi redus de la 20
la 5 utilizând arborii simpli cu prefix multibit. Arborii simpli cu prefix
multibit trebuie comprimate pentru a putea fi memorate într-o memorie
SRAM on-chip sau off-chip. Compresia la Lulea este remarcabilă, dar
se pare că algoritmul poate fi utilizat astăzi numai în soluţiile
personalizate per client oferite de o companie numită Effnet. Schema
Lulea are de asemenea o actualizare înceată. Schemele bitmap de tip
arbore au actualizări rapide şi pot fi tolerate de o gamă largă de setări
hardware.De asemenea pot exista patente ce pot restricţiona folosirea
lor. E utilizat în prezent în ruterul Cisco CRS-1.
In concluzie, căutarea binară asupra lungimii prefixelor este atractivă
datorita proprietăţilor de scalabilitate la lungimi mari ale adreselor. Din
păcate, utilizarea tehnicii de hashing face dificilă obţinerea unor timpi
buni de căutare. In mod similar, introducerea adresării IPv6 şi multicast
ce cresc importanţa căutării adreselor lungi, au făcut ca această schemă
să fie puţin atractivă. Schema este utilizată de anumiti producători in
implementări software.
Soluţiile algoritmice împreună cu pipeline pot ţine pasul cu vitezele
legăturilor atât timp cât memoriile SRAM pot respecta timpii de sosire a
pachetelor. Toate schemele studiate pot fi pipeline pentru a asigura o
căutare/lookup per timp de acces la memorie. Alegerea între CAM şi
schemele algoritmice va fi greu de cuantificat şi probabil alegerea se va
face ad hoc în funcţie de fiecare produs.
Dacă schemele cu arborii cu prefix comprimate pot utiliza mai puţin
de 32 de biţi per prefix atunci arborii cu prefix comprimaţi par a utiliza
mai puţine tranzistoare şi o putere mai mică decât CAM. Acestea se
datorează faptului că în CAM, logica de căutare/lookup este distribuită
în fiecare celulă N de memorie, în timp ce într-o solutie algoritmica
logica de căutare, deşi mai complicată, este distribuită de-a lungul unui
număr mai mic şi constant de niveluri. O analiză VLSI atentă a celor
două implementări ar fi foarte utilă.
Deşi capitolul e dedicat tehnicilor de căutare, in concordanta cu
tendinţele curente ale pieţei, să nu se uite că la baza acestora stau
anumite principii. Este plauzibil ca toate ruterele din viitor să utilizeze
comutarea şi procesarea optică chiar şi în cazul căutarii. In acest caz,
algoritmii specifici descrişi ar putea fi depaşiţi, dar principiile de
proiectare vor rămâne valabile.
Schemele descrise în acest capitol necesită într-o anumită măsură
gândirea algoritmică precum şi alte principii ce au fost prezentate.
Schemele utilizează precalcularea, care scurteaza timpii de
177
inserare/ştergere. Schemele se folosesc de asemenea de anumite
caracteristici hardware cum ar fi memoria extinsă, utilizarea dupa caz a
memoriilor rapide sau lente şi optimizarea gradului de libertate într-o
proiectare data (vezi fig. 11.1).

178
COMUTAŢIA

În primii ani ai telefoniei, operatorul de telefonie a ajutat la realizarea


structurii sociale într-o comunitate. Dacă X a dorit să vorbească cu Y, a
apelat operatorul şi a întrebat de Y; operatorul a trebuit să introducă
manual un fir într-un tablou de comutaţie care conectează telefonul lui X
cu al lui Y. Tabloul de comutaţie permite desigur şi conexiuni paralele
între perechi disjuncte, iar X ' a putut vorbi cu Y ' în acelaşi timp. S-a putut
întâmpla ca fiecare apel nou să fie întârziat pentru un interval de timp
scurt, timp în care operatorul finalizează tratarea apelului anterior.
Când a fost descoperit tranzistorul în laboratoarele Bell, faptul că fiecare
tranzistor era de fapt un comutator controlat în tensiune a fost imediat
exploatat în realizarea comutatoarelor electronice utilizând o matrice cu
tranzistori. Operatorul de telefonie a fost folosit pentru funcţii care
necesită intervenţia omului, ca de exemplu colectarea apelurilor. Utilizarea
electronicii a crescut cu mult viteza şi fiabilitatea comutaţiei telefonice.
Un ruter este de fapt un oficiu poştal automat pentru pachete. Ruter are
sensul generic de componentă generală de interconectare, ca de exemplu
gateway sau comutator SAN. In esenţă ruterul este o cutie care comută
pachete între legăturile de intrare şi de ieşire (figura 13.1). Procesul de
căutare (B1) determină linia de ieşire la care un pachet va fi comutat.
Trebuie facuta planificarea pachetelor la linia de iesire (B3). Esenta
ruterului este sistemul intern de comutaţie (B2), descris în acest capitol.

RUTER
B2
Comutaţie

Linie intrare i B3
Linie ieşire
Planificare
B1
Căutare adrese
FIGURA 13.1. Modelul ruterului

Literatura în domeniul comutaţiei este vastă, şi acest capitol s-a axat pe


comutatoarele analizate în literatură, utilizate în prezent în industria
reţelelor. Comutatoarele descrise, Gigacomutatorul DEC, GSR-ul lui
Cisco, seriile T ale lui Juniper, TSR-ul lui Avici, reprezintă o introducere
în problemele teoretice şi practice implicate în structurile de comutatoare
pentru ruterele de viteză. Tehnicile de comutaţie şi principiile
corespunzătoare invocate sunt rezumate în figura 13.2.
Număr Principiu Comutator
P5b Acces extins la memorii Datapath
pentru viteză de lucru

179
P13 Control distribuit al cozilor Gigaswitch
P5a prin bilete
Planifică în paralel ieşiri şi
grupuri de vânătoare
P11 Optimizat pentru o Knockout
P15 concurenţă de maxim k<N
la ieşiri
P3 Utilizarea unui arbore de
concentratoare aleatorizat
pentru corectitudine
Relaxează specificatiile
bufferului de ieşire
P13 Utilizeaza cozi de intrare AN-2
P14 per-ieşire
P15 Comunicaţie N2 , fezabilă
pentru N mic
Utilizeaza asociere iterativă
aleatorizata
P14 PPE-uri pentru corectitudine iSLIP
round-robin,
P3 fezabilă pentru N mic
Relaxează specificatiile
dependenţei
de acceptare-alocare
P15 Utilizează o reţea Clos cu Juniper
trei nivele T 640
P3b pentru reducerea costurilor
Aleatorizează distributia
sarcinii
pentru a reduce k de la 2n
la n
P15 Utilizează o reţea Benes cu Structura
log N -nivele Growth
P3a pentru reducerea costurilor
P15 Utilizează o schemă de
rutare aleatoare rapidă
Utilizează multicast cu
copiere dublă şi arbore binar
P13 Realizează grila utilizând Avici TSR
fire scurte

FIGURA 13.2. Principii utilizate pentru comutatoare

13.1 COMPARAŢIE RUTERE -COMUTATOARE TELEFONICE


Avand analogia între rutere şi comutatoarele telefonice, subliniem
asemănările şi diferenţele majore între comutatoarele ruterelor şi ale
telefoanelor. Primele rutere au utilizat o magistrală simplă pentru
conectarea intrărilor şi a ieşirilor, care permite unei singure intrări să
transmită spre o ieşire la un moment dat. Astazi, aproape orice ruter core
utilizează o matrice crossbar internă, care permite perechilor distincte să
comunice în paralel, crescand astfel traficul. Electronica are rolul
operatorului, activând comutatoare cu tranzistoare care interconectează
intrările şi ieşirile.
În telefonie o conexiune telefonică tipică durează secunde, sau chiar
minute. Dar în comutatoarele Internet, fiecare conexiune este mentinută pe
durata pachetului. Aceasta înseamnă 8 nsec pentru un pachet de 40 bytes

180
la 40 Gbps. Memoriile cache nu reduc căutările, datorită rarităţii
secventelor de pachete de mari dimensiune in aceeaşi direcţie. De
asemenea, este improbabil ca două pachete consecutive la un port de
intrare al comutatorului să fie destinate aceluiaşi port de ieşire. Aceasta
îngreunează echilibrarea sarcinii de comutaţie pentru pachetele multiple.
Deci, pentru a opera la viteza liniei, sistemul de comutaţie trebuie să
decidă intrările şi ieşirile ce trebuie asociate, în timpul minim de sosire al
unui pachet. Astfel, partea de control a unui comutator de Internet (care
stabileşte conexiunile), e mult mai greu de realizat decât în cazul unui
comutator telefonic. O altă diferenţă importantă între comutatoarele
telefonice şi cele de pachete este necesitatea conexiunilor multicast pentru
comutatoarele de pachete. Multicastul complică suplimentar problema
planificării, deoarece unele intrări vor să transmită spre ieşiri multiple.
Pentru a simplifica problema, majoritatea ruterelor segmentează intern,
pachetele de dimensiune variabilă în celule de dimensiune fixă, înainte de
a fi transmise spre structura de comutaţie. Matematic, comutaţia ruterului
este redusă la rezolvarea unei probleme de asociere bipartita: ruterul
trebuie să asocieze cât mai multe linii de intrare (la cât mai multe linii de
ieşire), în limita posibilităţilor, într-un timp fix de sosire a celulelor.
Deoarece algoritmii optimali pentru asociere bipartită rulează într-un timp
de ordinul milisecundelor, rezolvarea acestei probleme la fiecare 8 nsec
necesită gândirea unor sisteme. Soluţiile descrise în acest capitol
realizeaza un compromis precizie/timp (P3b), utilizeaza paralelismul
hardware (P5) şi aleatorizarea (P3a), şi exploateaza faptul că
comutatoarele tipice au 32-64 porturi, pentru a face operaţii rapide cu cozi
prioritare utilizând bitmap-uri(P14).

13.2. COMUTATOARE CU MEMORIE COMUNĂ


Înainte de-a descrie comutatoarele cu magistrale, respectiv crossbar,
analizam una dintre cele mai simple implementări de comutator, cea cu
memorie partajată. Pachetele sunt scrise in memorie de pe linia de intrare
si citite spre linia de iesire. Acest tip de proiectare a fost utilizat in
comutatoarele cu interschimbarea diviziunilor de timp din telefonie.
Funcţionează bine şi pentru reţele de date în cazul comutatoarelor de
dimensiuni mici.
Principala problemă este latimea de banda a memoriei (viteza de lucru).
Dacă chip-ul are 8 linii de intrare şi 8 linii de ieşire, va trebui sa citeasca/
scrie fiecare pachet/celulă. Deci memoria trebuie să ruleze la o viteza de
16 ori mai mare decat viteza fiecărei linii. Până la un punct, problema se
poate rezolva cu o memorie cu viteza mare de acces. Biţii sosesc serial la
o linie de intrare şi sunt memoraţi întru-un registru de intrare cu deplasare.
Cand toată celula a fost memorată, ea poate fi încărcată într-o memorie cu
dimensiunea unei celule. Apoi aceasta poate fi citită în registrul de
deplasare de ieşire al liniei corespunzătoare şi transmisă pe linia de ieşire.
Comutatorul Datapath utilizează o memorie centrală de 4 Kcelule, care
evident nu asigură memoria tampon adecvată. Totusi, această memorie
poate fi implementată uşor într-un singur chip si mărită, utilizând controlul
fluxului şi memorii tampon de pachete externe chipului. Dar proiectarea
acestor memorii partajate, ca aceasta, nu se scaleaza peste memoriile de
dimensiunea unei celule, deoarece pachetele de dimensiune minimă pot
181
avea cel mult dimensiunea unei celule. Un comutator care trimite mai
multe pachete de dimensiune minimă înspre diferite destinaţii poate
împacheta mai multe asemenea pachete într-un singur cuvânt, dar nu e
sigur că le poate extrage în acelaşi timp.
In ciuda acestui fapt, comutatoarele cu memorie partajată pot fi relativ
simple pentru un număr mic de porturi. Marele lor avantaj este că pot fi
optimale ca memorie şi putere, deoarece datele sunt stocate/extrase in/din
memorie o singură dată. Comutatoarele hard sau crossbar, aproape
întotdeauna necesită memorarea pachetelor de două ori dublând potenţial
costurile de memorie şi de putere. Este posibilă extinderea ideii de
memorie partajată la comutatoarele de dimensiuni mai mari, folosind
întreţeserea aleatoare DRAM.

13.3 RUTERELE: DE LA MAGISTRALE LA CROSSBAR


Comutatoarele pentru rutere au evoluat de la cele mai simple
comutatoare cu mediu partajat (magistrale sau memorii), (fig.13.3a) la
comutatoarele crossbar mai moderne (fig.13.3 d). Un card de linie într-un
ruter sau comutator conţine logica interfeţei pentru o legătură de date
(linie pe fibră optică sau un Ethernet). Primele comutatoare au conectat
intern toate cardurile de linie, printr-o magistrală de viteză mare
(asemanator cu o reţeea locala LAN interna) pe care numai o pereche de
carduri de linie pot comunica simultan. Astfel dacă cardul de linie 1 a
transmis un pachet înspre cardul de linie 2, nici o altă pereche de carduri
de linie nu mai poate comunica.

Pachet Pachet
Card de linie 1 CPU Card de linie 1 CPU 1
MAGISTRALĂ

MAGISTRALĂ

Card de linie 2 Card de linie 2

CPU M
Card de linie N Card de linie N
a) Magistrală, CPU comun b) Magistrală, CPU-uri comune

CPU (uri) CPU (uri)


de rutare de rutare

Pachet Pachet
CPU 1 FE 1
MAGISTRALĂ

Card de linie 1 Card de linie 1


CPU 2 FE 2
Card de linie 2 Card de linie 2

CPU N FE N
Card de linie 2 Card de linie 2
c) Magistrală, CPU-uri pe fiecare d) Crossbar, motoare de înaintare pe fiecare
card de linie card de linie

FIGURA 13.3. Evoluţia comutatoarelor pentru reţele, de la comutatoarele cu magistrală


comună cu un CPU comun, la comutatoarele crossbar cu un motor de înaintare pe fiecare
card de linie

182
Mai rău, în ruterele şi comutatoarele mai vechi, decizia de înaintare a
pachetelor a fost dată unui CPU cu scop general, partajat. CPU-urile cu
scop general au un software de înaintare mai simplu şi uşor de modificat.
Dar ele erau lente, datorită nivelurilor suplimentare de interpretare a
instrucţiunilor cu scop general. Le lipsea capacitatea de lucru/control in
conditii de timp real la procesarea pachetelor, din cauza
nedeterminismului cauzat de mecanisme ca memoriile cache. Fiecare
pachet traversează magistrala de două ori, o dată pentru a ajunge la CPU şi
o dată pentru a ajunge de la CPU la destinaţie, deoarece CPU se află pe un
card separat, accesibil numai prin intermediul magistralei.
Deoarece CPU a fost o strangulare, pasul urmator a fost adăugarea unui
grup de CPU-uri comune pentru înaintare, dintre care fiecare poate
expedia un pachet. De exemplu un CPU poate înainta pachetele de pe
cardurile de linie 1 până la 3, cel de-al doilea de pe cardurile de linie 4
până la 6 şi aşa mai departe. Aceasta creşte eficienta totală, sau reduce
cerinţele pentru fiecare CPU individual, conducând potenţial spre o
variantă cu un cost mai scăzut. In orice caz, fără o atenţie corespunzătoare
se poate ajunge la o ordonare greşită a pachetelor, ceea ce nu este de dorit.
Cu toate acestea, magistrala rămâne o strangulare. O singură magistrală
partajată are limitări de viteză, datorită numărului de surse şi destinaţii
distincte care trebuie tratate de o singură magistrală comună. Aceste surse
şi destinaţii adaugă sarcină electrică suplimentară, înrăutăţind timpii de
crestere ai semnalelor, şi în final viteza de transmitere a biţilor pe
magistrală. Alte efecte electrice includ conectorii multipli (de la fiecare
card de linie) şi reflexiile pe linie.
Metoda clasică de-a evita această strangulare este comutatorul crossbar
(fig. 13.3.d) care conţine în esenţă 2N magistrale paralele, o magistrală
pentru fiecare card de linie sursă, şi o magistrală pentru fiecare card de
linie destinaţie. Matricea de magistrale sursă (orizontale) şi destinaţie
(verticale) formează crossbar-ul.
Teoretic viteza pe o magistrală creşte de N ori, deoarece în cel mai bun
caz toate cele N magistrale vor fi utilizate în paralel, simultan pentru a
transfera date, în locul unei singure magistrale. Bineînţeles pentru a obţine
această creştere de viteză este necesară găsirea a N perechi distincte sursă-
destinaţie in fiecare diviziune de timp. Apropierea de această margine
reprezintă probema majoră de planificare studiată acum.
O altă modificare a comutatoarelor crossbar, produse in perioada 1995-
2002, este utilizarea de circuite integrate speciale dedicate (ASIC) şi a
motoarelor de înaintare, în locul CPU-urilor cu scop general, deşi
asocierea nu e obligatorie. Dezavantajele acestor motoare de înaintare ar fi
costurile de proiectare ale ASIC-urilor, şi lacunele de programabilitate
(ceea ce face ca eventuale modificări să fie dificile sau chiar imposibile).
Aceste probleme au condus din nou la procesoare de reţea rapide, dar
programabile .

13.4. PLANIFICATORUL CROSSBAR „IA-ŢI UN BILET”


Cel mai simplu crossbar este o matrice cu N magistrale de intrare şi N
magistrale de ieşire (fig.13.4). Atunci cand cardul de linie R doreşte să
transmită date cardului de linie S, magistrala de intrare R trebuie să fie
conectată la magistrala de ieşire S. Cea mai simplă cale de-a face această
183
conexiune este aducerea unui tranzistor în stare de conducţie (fig.13.5).
Pentru fiecare pereche de magistrale de intrare şi de ieşire, la fel ca la R şi
S, există un tranzistor care, în conducţie, conectează cele două magistrale.
O astfel de conexiune este cunoscută sub denumirea de punct de
conexiune/crosspoint. Un crossbar cu N intrări şi N ieşiri are N2 puncte de
conexiune, iar fiecare dintre acestea necesită o linie de control de la
planificator, pentru a-l aduce în stare de conducţie şi pentru a-l bloca.

Intrare 1

Intrare 2

Intrare 3

Ieşire 1 Ieşire 2 Ieşire 3

FIGURA 13.4. Comutatorul crossbar fundamental

Intrare R

Se setează la adevărat pentru a


conecta intrarea R la ieşirea S

Ieşire S

FIGURA 13.5. Conectează intrarea de pe cardul de linie R la cardul de linie S, prin aducerea
tranzistorului în stare de conducţie. La modelele moderne de crossbar această variantă simplă
este înlocuită cu arbori de multiplexoare pentru a reduce capacitatea.

Desi N2 pare mult, implementările VLSI simple cu tranzistoare fac ca


limitarile în realizarea comutatoarelor de dimensiune mare sa fie impuse
mai ales de numărul de pini, tehnologiile de conectare a cardurilor, etc.
Majoritatea ruterelor şi comutatoarelor dinainte de 2002 utilizează
comutatoarele crossbar simple ca suport pentru 16-32 porturi. Multicastul
se face prin conectarea magistralei de intrare R la toate magistralele de
ieşire care doresc să recepţioneze de la R. Dar planificarea multicast e
complicată.
In practică numai variantele mai vechi de crossbar utilizează tranzistoare
pentru conexiuni (capacitatea totala creşte cu numărul de porturi). Aceasta
duce la creşterea întârzierii la transmisia unui semnal, ceea ce devine o
problemă la viteze mai mari. Implementările moderne utilizează pe ieşiri
arbori mari de multiplexoare, sau buffere cu 3 stări. Sistemele performante
utilizează, pentru trecerea fluxului de date prin crossbar, nişte memorii
(de ex. o poartă) la punctele de conexiune.Astfel considerentele de nivel
fizic fac proiectarea comutatoarelor crossbar moderne complicată.
Va fi tratata doar partea de planificare a comutatoarelor. Pentru
corectitudine controlul logic trebuie să asigure conectarea fiecărei
magistrale de ieşire la cel mult o magistrală de intrare (pentru a preveni
mixarea intrărilor). Pentru performanţă, partea logică trebuie să

184
maximizeze numărul de perechi de carduri de linie care comunică în
paralel. Deşi paralelismul ideal asigura că toate cele N magistrale de ieşire
sunt ocupate în acelaşi timp, în practică paralelismul este limitat de doi
factori. In primul rând s-ar putea să nu existe date pentru unele carduri de
linii de ieşire. Apoi e posibil ca două sau mai multe caduri de linii de
intrare să dorească să transmită date aceluiaşi card de linie de ieşire. Dar o
singură intrare poate câştiga la un anumit moment dat, si astfel se
limitează traficul pentru intrările care au pierdut si nu pot transmite date.
Astfel, în ciuda paralelismului extins, concurenţa majoră are loc la portul
de ieşire. Cum poate fi soluţionată concurenţa pentru porturile de ieşire în
acelaşi timp cu maximizarea paralelismului? O variantă simplă şi elegantă
de planificare a fost pentru prima dată inventată şi utilizată în
Gigacomutatorul DEC. Un exemplu al aşa numitului algoritm „ia-ţi un
bilet” utilizat de DEC este dat în figura 13.6.
Ideea este ca fiecare card de linie de ieşire S să menţină o coadă
distribuită pentru toate cardurile de linie de intrare R care aşteaptă să
transmită înspre S. Coada pentru S este memorată in cardul liniei de intrare
(în loc de S) utilizând un mecanism simplu cu numere de bilet. Dacă
cardul de linie R doreşte să transmită un pachet spre cardul de linie S, mai
întâi trimite o cerere printr-o magistrală de control separată înspre S; S
trimite apoi un număr de coadă înspre R pe magistrala de control. Numărul
de coadă este numărul poziţiei lui R în coada de ieşire pentru S.
Apoi R monitorizează magistrala de control; când S termină acceptarea
unui nou pachet, S trimite pe magistrala de control numărul de coadă
curent pe care o serveşte. Dacă R observă că numărul său este „servit”, R
îşi plasează pachetul pe magistrala datelor de intrare pentru R. In acelaşi
timp S se asigură că punctul de conexiune între R şi S a fost activat.
In fig. 13.6 se vede algoritmul în acţiune ; cadrul de la cardul de linie A
conţine trei pachete destinate ieşirilor 1, 2 şi 3, B are 3 pachete destinate
aceloraşi ieşiri, iar C are pachete destinate ieşirilor 1, 3 şi 4. In acest
exemplu pachetele au aceeaşi dimensiune (acest lucru însă nu este necesar
pentru ca algoritmul „ia-ţi un bilet” să funcţioneze).
Fiecare port lucrează numai pentru pachetul din fruntea cozii sale. Deci
algoritmul începe cu fiecare intrare transmiţând o cerere, printr-o
magistrală de control înspre portul de ieşire căruia îi este destinat pachetul
din fruntea cozii de intrare. Astfel, în exemplu, fiecare intrare trimite o
cerere înspre portul 1 pentru permisiunea de-a transmite un pachet.
Numărul de bilet este un întreg mic care depinde de, şi este eliberat în
ordinea sosirii. Presupunem că cererea lui A ajunge prima pe magistrala de
control serială urmată de B, iar apoi de C. In figura din stânga sus aparent
cererile sunt transmise în mod concurenţial, nu secvential. Deoarece portul
de ieşire 1 poate servi un singur pachet la un moment dat, el pune cererile
în ordine serială şi returnează T1 lui A, T2 lui B, şi T3 lui C.
Astfel, în figura din mijloc în rândul de sus, portul de ieşire 1 difuzează
numărul de bilet curent care este deservit (T1) pe o altă magistrală de
control. Când A observă că are un număr corespunzător pentru intrarea 1,
în figura din partea dreaptă sus, A conectează magistrala sa de intrare la
magistrala de ieşire a lui 1 şi îşi trimite pachetele pe magistrala sa de
intrare. Deci la sfârşitul primului rând de imagini, A a trimis pachetul din
fruntea cozii sale de intrare la portul de ieşire 1.
185
Al doilea rând din figura 13.6 începe cu transmisia unei cereri de către
A pentru pachetul care este acum în fruntea cozii sale înspre portul de
ieşire 2. Lui A i se returnează un număr de bilet, T1, pentru portul 2. În
imaginea din mijlocul rândului 2, portul 1 anunţă că este pregătit pentru
T2, şi portul 2 anunţă că este pregătit pentru biletul T1. Aceasta rezultă din
figura din partea dreaptă a celui de-al doilea rând, unde A este conectat la
portul 2 şi B este conectat la portul 1 şi pachetele corespunzătoare sunt
transferate.

Etapa 1
3 2 1 A 1 3 2 1 A T1
1 3 2 1 A 1
B 2 2 2
3 2 1 3 3 2 1 B T2 3 3 2 1 B 3
4 3 1 C 4 4 3 1 C T3 4 4 3 1 C 4
Cerere Acordare bilet Conectare
Etapa 2
A A A 1
3 2 1 3 2 T2 1 3 2
2 2 2
3 2 1 B 3 2 1 B
T1
3 2 1 B 3 T1 3 3
4 3 1 C 4 4 3 1 C 4 4 3 1 C 4
Cerere Acordare bilet Conectare
Etapa 3
A 1 A 1 A 1
3 3 T1 T3 3
B 2 B T22 B 2
3 2 3 3 2 T2 3 3 2 3
4 3 1 C 4 4 3 1 C T14 4 3 1 C 4
Cerere Acordare bilet Conectare

FIGURA 13.6. În mecanismul de planificare „ia-ţi un bilet”, toate porturile de intrare au o singură
coadă de intrare care este etichetată cu numărul portului de ieşire căruia fiecare pachet îi este destinat.
Astfel în primul cadru, intrările A, B şi C trimit cereri înspre portul de ieşire 1. Portul de ieşire 1 (sus,
mijloc) îi acordă primul număr lui A, cel de-al doilea lui B, etc., şi aceste numere sunt utilizate pentru
a stabili o ordine serială pentru accesul la porturile de ieşire.
Al treilea rând de imagini din figura 13.6 începe similar cu transmisia de
către A şi B a unei cereri pentru porturile 3 şi 2, respectiv. Numai C mai
este încă blocat în aşteptarea pentru numărul său de bilet T3, care este
obţinut cu două iteraţii înainte pentru a fi anunţat la portul de ieşire 1.
Astfel C nu mai trimite alte cereri până ce pachetul din fruntea cozii sale
de aşteptare este deservit. Lui A i se returnează T1 pentru portul 3 şi lui B i
se returnează T2 pentru portul 2. Apoi portul 1 difuzează T3, portul 2
difuzează T2, şi portul 3 difuzează T1. Aceasta duce la ultima imagine din
partea stângă a rândului 3, unde comutatorul crossbar conectează A cu 3,
D cu 2 şi C cu 1.
Varianta „ia-ţi un bilet” este avantajoasă în starea de control necesitând
doar două numărătoare de log 2 N biţi la fiecare port de ieşire pentru a
reţine numărul de bilet curent care este deservit şi cel mai mare număr de
bilet dispensat. Aceasta a permis să se ajungă la o dimensiune de 36 porţi
la Gigacomutatorul DEC chiar şi în anii ’90 când dimensiunea memoriei
pe chip a fost limitată. Varianta a utilizat un planificator distribuit, iar
arbitrarea fiecărui port de ieşire a fost realizată de un aşa numit chip GPI
pe fiecare card de linie; chip-urile GPI comunică printr-o magistrală de
control.
Chip-urile GPI trebuie să arbitreze magistrala de control (serială) cu
scopul de-a prezenta o cerere la un card de linie de ieşire şi de-a obţine un
număr de ordine în coadă. Deoarece magistrala de control este o magistrală
186
de difuziune, un port de intrare îşi dă seama când îi vine rândul observând
activitatea acelora care au fost înaintea lui, şi apoi poate instrui
comutatorul să realizeze o conexiune.
Pe lângă simplitatea de realizare a controlului, varianta „ia-ţi un bilet”
are avantajul de-a fi capabilă să opereze direct cu pachete de dimensiune
variabilă. Porturile de ieşire pot difuza asincron numărul următor de bilet
în momentul în care acestea finalizează recepţionarea pachetului curent;
porturi distincte de ieşire pot difuza numărul lor de bilet curent la
momente de timp arbitrare. Astfel spre deosebire de toate variantele care
vor fi descrise mai târziu nu există antet şi trafic de control pentru
împărţirea pachetelor în „celule” şi reasamblarea lor ulterioară. Pe de altă
parte în cazul „ia-ţi un bilet ” paralelismul este limitat de „blocarea în
fruntea liniei”, fenomen analizat în următoarea secţiune.
Un alt avantaj al variantei „ia-ţi un bilet” se numeşte grupuri de
vânătoare. Orice set de carduri de linie (nu numai cardurile de linie
învecinate fizic) pot fi agregate pentru a forma o legătură cu banda mai
mare, numită grup de vânătoare. Astfel 3 legături de 100 Mbps pot fi
grupate într-o singură legătură de 300 Mbps.
Implementarea ideii necesită doar mici modificări ale algoritmului de
planificare original, deoarece fiecare dintre chip-urile GPI din grup poate
observa mesajele trimise unul altuia pe magistrala de control şi astfel să
păstreze copii locale ale numărului de bilet (comun). Următorul pachet
destinat grupului este servit de primul port de ieşire liber din grupul de
vânătoare. In timp ce grupurile de vânătoare de bază pot reordona
pachetele transmise spre legături diferite, o mică modificare permite ca
pachetele de la o intrare să fie transmise spre un singur port de ieşire într-
un grup de vânătoare, printr-o dispersare deterministă simplă. Modificarea
evită reordonarea cu preţul reducerii paralelismului.
Timp (conform cu
durata pachetelor)
3 2 1
A 1 A B C
3 2 1 2 A B
B
3 A B C
4 3 1
C 4 C

FIGURA 13.7 Blocare „în fruntea liniei” cauzată de variante de algoritmi „ia-ţi un bilet”.
Pentru fiecare port de ieşire, o scară de timp orizontală este etichetată cu portul de intrare
care trimite un pachet către acel port de ieşire în perioda de timp corespunzătoare sau cu un
pătrat gol dacă nu se transmit pachete. Se observă numărul mare de pătrate goale, ceea ce
denotă o risipă de oportunităţi, ceea ce limiteaza paralelismul .

Gigacomutatorul fiind o punte, a trebuit să trateze LAN-ul multicast.


Deoarece mecanismul de planificare „ia-ţi un bilet” utilizează planificarea
distribuită, prin chip-uri GPI separate pe ieşire, este dificil să se
coordoneze toate planificatoarele pentru a asigura ca fiecare port de ieşire
să fie liber.In plus aşteptarea ca fiecare port de ieşire să aibă un bilet liber
pentru un pachet de multicast ar duce la blocarea unor porturi care au fost
gata de-a servi pachetul mai înainte, pierzându-se debit util. Aşadar
multicastul a fost tratat de către un procesor central prin software, şi în
consecinţă a fost asociat ’’stării de clasa a doua’’.

187
13.5 BLOCAREA ÎN FRUNTEA LINIEI (HOL)

Facand abstractie de mecanismul intern din figura 13.6, se observă că în


3 iteraţii au fost 9 posibilităţi de transmisie (3 porturi de intrare şi 3
iteraţii), dar după ultima imagine, cea din dreapta jos, rămâne un pachet în
coada lui B şi două pachete în coada lui C. Deci au fost transmise numai 6
din 9 pachete posibile ; rezultă că paralelismul este limitat.
Această concentrare doar pe comportamentul de intrare-ieşire este
ilustrată în figura 13.7, care arată pachetele transmise în fiecare durată de
pachet la fiecare port de ieşire. Fiecare port de ieşire are o linie de timp
asociată, etichetată cu portul de intrare care transmite un pachet în durata
corespunzătoare de timp ; pătratele albe au semnificaţia că nici un port de
intrare nu transmite pachete în acel interval de timp. Este continuat
exemplul din figura 13.6 pentru încă trei iteraţii, până ce toate cozile de
intrare sunt goale.
In dreapta figurii se observă că doar aproximativ jumătate din
posibilităţile de transmisie sunt utilizate (9 din 24). Bineînţeles nici un
algoritm nu o poate face mai bine pentru anumite scenarii. Totuşi, alţi
algoritmi ca de exemplu iSLIP (figura 13.11) poate duce la un paralelism
mai bun, finalizând aceleaşi nouă pachete în patru iteraţii în loc de şase.
In prima iteraţie, (fig.13.7), toate intrările au pachete în aşteptare pentru
ieşirea 1. Deoarece numai una singură (de exemplu A) poate să transmită
un pachet către ieşirea 1 la un anumit moment de timp, întreaga coadă la B
(şi C) este blocată aşteptând finalizarea transmisiei lui A. Deoarece
întreaga coadă este blocată de progresul pachetului din fruntea cozii,
aceasta se numeşte blocare în fruntea liniei HOL (head of line). ISLIP şi
PIM ocolesc această limitare permiţând pachetelor din spatele pachetului
blocat să progreseze (de exemplu pachetul destinat portului de ieşire 2 în
coada de intrare a lui B poate fi transmis înspre portul 2 în prima iteraţie a
figurii 13.7) cu preţul unui algoritm de planificare mai complicat.
Pierderea de debit util prin blocarea HOL poate fi descrisă analitic cu un
model simplu, uniform de trafic. Presupunem că în fruntea fiecărei cozi de
intrare este un pachet destinat unei ieşiri din N cu probabilitatea 1/N.
Astfel, dacă două sau mai multe porturi de intrare transmit către acelaşi
port de ieşire, toate cu excepţia unei intrări sunt blocate. Intregul trafic al
celorlalte intrări este „pierdut” datorită blocării în fruntea liniei.
Considerăm pachetele de dimensiune egală şi o încercare iniţială unde
un proces aleator asociază un port destinaţie fiecărui port de intrare,
uniform de la 1 la N. In loc sa ne concentrăm atenţia asupra porturilor de
intrare, ne concentrăm asupra probabilităţii că un port de ieşire O este
nefolosit, care e de fapt probabilitatea ca nici unul dintre cele N porturi de
intrare să aleagă portul de ieşire O. Deoarece fiecare port de intrare nu
alege O cu probabilitatea 1 − 1/ N , probabilitatea ca toate cele N porturi de
intrare nu vor alege O este (1 − 1/ N ) . Această expresie converge rapid
N

înspre 1/ e . Deci probabilitatea ca O să fie ocupat este 1 − 1/ e , adică 0,63.


Deci debitul util al comutatorului nu este N ∗ B (ideal atins doar dacă
toate cele N intrări ar fi ocupate, operând la B bps). El este 63 % din
această valoare maximă, deoarece 37 % din legături sunt nefolosite.
Analiza este simplistă şi presupune (incorect) că fiecare iteraţie este
independentă. In realitate, pachetele selectate să nu fie transmise într-o
188
iteraţie, trebuie luate în consideraţie în iteraţia următoare (fără o altă
selecţie aleatoare a destinaţiei). O analiză clasică, care elimină prezumţia
de independenţă, arată că debitul util se apropie de 58 % .
Dar sunt realiste distribuţiile uniforme de trafic? Analiza depinde de
distribuţia traficului, deoarece nici un comutator nu poate funcţiona bine
dacă tot traficul e destinat unui singur port. Analiza arată că efectul HOL
poate fi redus utilizând grupuri de vânătoare, mărind viteza comutatorului
crossbar comparativ cu a legăturilor, şi lucrând cu distribuţii mai realiste,
la care un număr de clienţi transmit trafic către câteva servere.
Există insa distribuţii pentru care blocarea în fruntea liniei poate afecta
grav traficul. Să ne imaginăm că fiecare linie de intrare are B pachete spre
portul 1 urmate de B pachete spre portul 2 şi aşa mai departe, şi în final B
pachete spre portul N. Aceeaşi distribuţie de pachete este prezentă la toate
porturile de intrare. Dacă planificăm pachetele destinate portului 1 în
grupul iniţial, blocarea în fruntea liniei va limita comutatorul, fiind
posibilă transmisia unui singur pachet pe intrare în fiecare interval de
timp. Astfel, comutatorul reduce traficul la 1/ N din traficul total posibil,
dacă B este destul de mare. Pe de altă parte vom vedea că variantele de
comutatoare care utilizează cozi de ieşire virtuale (VOQ) pot atinge în
situaţii similare aproape 100 % din debitul util, deoarece fiecare bloc de B
pachete se distribuie în cozi separate la fiecare intrare.

13.6 EVITAREA HOL PRIN COZI LA IEŞIRE


Pentru evitarea blocării HOL s-au propus cozi de aşteptare la ieşire în
locul cozilor la intrare. Presupunem că pachetele pot fi trimise oricum
către un port de ieşire fără coadă de aşteptare la intrare. Un pachet P
destinat unui port de ieşire ocupat, nu poate bloca un alt pachet care-i
urmează, deoarece pachetul P, trimis în coada de aşteptare a portului de
ieşire, poate bloca doar pachetele destinate aceluiaşi port de ieşire.
Cea mai simplă cale de-a realiza acest lucru ar fi de-a face comutaţia de
N ori mai rapidă comparativ cu viteza liniilor de intrare. Atunci chiar dacă
toate cele N intrări transmit către aceeaşi ieşire, într-un anumit timp de
celulă, toate cele N celule pot fi trecute prin comutator spre a fi introduse
în coada de aşteptare la ieşire. Deci, pentru a realiza cozi de aşteptare la
ieşiri e nevoie de comutatoare cu viteze de N ori mai mari. Soluţia e
scumpă sau irealizabilă.
O implementare practică a cozilor de aşteptare la ieşire este comutatorul
„knockout”. Presupunem că recepţia a N celule pentru aceeaşi destinaţie,
în orice interval de timp de celulă, este rară şi că numărul aşteptat este k,
iar k << N . In acest caz (P11) este necesar să se proiecteze un comutator
doar de k ori mai rapid decât viteza liniei de intrare, nu de N ori. Aceasta
duce la economii substanţiale pentru comutator, şi se poate realiza prin
paralelism hardware (P5), folosind k magistrale paralele.
In afara de varianta „ia-ţi un bilet” toate celelalte variante, inclusiv
varianta knockout, se bazează pe divizarea pachetelor în celule de
dimensiune fixă. Se va folosi noţiunea de celulă nu de pachet, dar se
subînţelege faptul că exista o etapă iniţială de segmentare a pachetelor în
celule, şi apoi de reasamblare la portul de ieşire.
Pe langa un comutator mai rapid, varianta knockout necesită o coadă de
aşteptare la ieşire capabilă să accepte celule cu o viteză de k ori mai mare
189
decat viteza liniei de ieşire. O implementare naivă ar fi cu un FIFO rapid,
dar costul e ridicat. De altfel ideea unui FIFO rapid este exclusă, deoarece
bufferul nu rezistă la un dezechilibru pe termen lung, între viteza de
intrare şi cea de ieşire. Astfel specificarea bufferului poate fi relaxată (P3)
permiţându-i-se să opereze astfel doar pe perioade scurte, în care celulele
sosesc de k ori mai rapid decât sunt transmise mai departe. Aceasta se
poate rezolva prin intreţeserea memoriei şi folosirea a k memorii paralele.
Un distribuitor (care rulează de k ori mai repede) împarte celulele ce
sosesc la k memorii, în ordine round-robin, iar la ieşire celulele sunt
extrase în aceeaşi ordine.
In final trebuie tratat corect şi cazul în care nu este respectată condiţia
impusă, şi N > k celule sunt transmise la aceeaşi ieşire, în acelaşi timp.
Pentru a înţelege soluţia generală expunem trei cazuri mai simple.
Doi concurenţi, un câştigator: In cel mai simplu caz dacă k=1 şi N=2,
arbitrul trebuie să aleagă echitabil o singură celulă din două variante
posibile. Aceasta se poate face prin realizarea unui echipament elementar
de comutaţie 2x2, numit concentrator care selectează aleatoriu un
câştigător si un necâştigător. Ieşirea câştigătoare este celula selectată.
Ieşirea necâştigătoare este folositoare în cazul general în care sunt
combinate mai multe concentratoare elementare 2 x 2.
Multi concurenţi, un singur câştigător: Acum considerăm k=1 (numai o
singura celulă poate fi acceptată) şi N>2 (sunt mai mult de două celule
între care arbitrul trebuie sa facă o selecţie echitabilă). O strategie simplă
se bazează pe varianta dezbină şi cucereşte (P15 structuri de date
eficiente) pentru a crea un arbore knockout de concentratoare 2x2.
Celulele sunt grupate două câte două utilizând N / 2 copii ale
concentratorului elementar 2x2. Acestea formează primul nivel al
arborelui. Câştigătorii primei runde trec la runda a doua unde sunt N/4
concentratoare şi aşa mai departe, pînă se ajunge la un singur concentrator
(root) care stabileşte câştigătorul. Se observă că ieşirile necâştigătoare au
fost ignorate pană acum.
Mai mulţi concurenţi, mai mult de un castigator: In final, consideram
cazul general in care k celule trebuie selectate din N celule posibile, pentru
valori arbitrare ale lui k si N. O idee simpla este de-a crea k arbori
knockout separati pentru a calcula primii k castigatori. Pentru
corectitudine, celulele necâştigătoare la arborii knockout de nivel inferior
trebuie trimise la urmatorul nivel pentru locurile urmatoare. Aceasta este
posibil deoarece concentratorul knockout 2x2 are doua iesiri, una pentru
castigator si una pentru necâştigător, nu doar una pentru castigator. Iesirile
necâştigătoare sunt direcţionate înspre arborii pentru poziţiile următoare.
Dacă trebuie selectate 4 celule din 8 candidaţi, cea mai simplă variantă
va atribui cei 8 candidaţi (în perechi) la 4 concentratori knockout 2x2.
Acestea logic vor selecta patru câştigători, cantitatea dorită. Aceasta
variantă este mult mai simplă faţă de utilizarea a patru arbori separaţi, dar
poate deveni inechitabilă. De exemplu presupunem două surse care
transmit continuu informaţie, S1 si S2 şi întâmplarea face ca aceste două
surse să formeze o pereche, în timp ce o altă sursă cu debit mare S3
formează o pereche cu o sursă cu debit mic. In acest caz sursele S1 si S2
vor obţine aproximativ jumătate din traficul pe care îl obţine S3. Trebuie
evitate aceste situaţii inechitabile prin utilizarea a k arbori separaţi, unul
190
pentru fiecare poziţie.Implementarea naivă a arborilor este: se începe
rularea logicii pentru arborele din poziţia j, strict după ce s-a terminat
rularea logicii pentru arborele din poziţia j-1. Aceasta asigură că toti
necâştigătorii eligibili au fost colectaţi. Echitatea este dificil de
implementat.
Inţelegerea comutatorului knockout e importantă datorită tehnicilor pe
care le introduce, dar implementarea lui este complexă şi face presupuneri
asupra distribuţiei de trafic. Presupunerile nu sunt valabile pentru topologii
reale, unde mai mult de k clienţi concurează frecvent în cazul transmisiilor
către un server foarte mult accesat. Dar cercetătorii au găsit căi simple de a
evita blocarea HOL fără utilizarea cozilor la ieşire.

13.7.EVITAREA HOL CU ASOCIERE PARALELĂ ITERATIVĂ


Ideea principală la PIM, asocierea paralelă iterativă, este de-a
reconsidera cozile de aşteptare la intrare, adaptându-le să evite blocarea în
fruntea liniei. Unui port de intrare i se planifica nu doar celula din fruntea
cozii sale de intrare, ci şi toate celelalte celule ce pot progresa când prima
celula este blocată. Pare foarte dificil: pot fi o sută de mii de celule în
fiecare coadă si chiar cu un singur bit de planificare a stării fiecărei celule
rezulta prea multa memorie pentru stocare şi procesare.
Prima observaţie e că celulele din coada fiecărui port de intrare pot fi
destinate numai pentru N porturi de ieşire posibile. Presupunem că celula
P1 este situată înaintea celulei P2 în coada de intrare X, şi ambele P1 si P2
sunt destinate aceleiaşi cozi de ieşire Y. Cu FIFO, P1 trebuie planificată
înainte de P2. Deci, nu se obţine nici un câştig din încercarea de-a
planifica P2 înainte de P1. Risipa evidentă poate fi evitată (P1) prin
planificarea doar a primei celule destinate fiecărui port de ieşire distinct,
iar restul celulelor raman neplanificate.
Primul pas la fiecare port de intrare, este descompunerea cozii unice de
intrare din fig.13.6 în cozi de intrare separate pentru fiecare ieşire numite
cozi virtuale la ieşire (VOQ) (fig.13.8) ; in stânga sus sunt aceleaşi celule
de intrare ca şi în fig.13.7, dar plasate în cozi separate.
Pentru comunicarea planificării porturilor de intrare e nevoie de un
bitmap de doar N biţi (N este dimensiunea comutatorului). Un 1 in poziţia
i a bitmap-ului arată că există cel puţin o celulă destinată portului de ieşire
i. Astfel dacă fiecare port de intrare, din N, comunică folosind un vector de
N biţi pentru descrierea planificării necesare, planificatorul trebuie să
proceseze numai N2 biţi. Pentru N<32, nu sunt prea mulţi biţi de transmis
pe magistralele de control sau de depus în memoriile de control.
Comunicarea cererilor e în stânga sus in figura 13.8. Se observă că A nu
transmite o cerere portului 4 deoarece nu are celule destinate portului 4.
De asemenea portul de intrare C transmite o cerere pentru celula destinată
portului de ieşire 4, deşi aceeaşi celulă este ultima în coada de intrare C, în
cazul scenariului cu o singura coadă de intrare din figura 13.6.
E necesar acum un algoritm de planificare ce asociază resurse nevoilor.
Deşi inteligent, adevăratul progres îl constituie evitarea blocării HOL
utilizând VOQ la intrare. Pentru a menţine analogia între exemplul din
fig.13.8 şi fig.13.6, presupunem că fiecare pachet din fig.13.6 e convertit
într-o singura celulă în fig.13.8.

191
Pentru a motiva algoritmul de planificare utilizat in PIM, se observă în
fig.13.8 stânga sus, că portul 1 de ieşire primeşte 3 cereri, de la A, B şi C,
dar poate servi numai una singură în următorul slot. O soluţie simplă este
selecţia aleatoare (P3a). Astfel în faza de alocare (imaginea din mijlocul
primului rând din fig. 13.8), portul de ieşire 1 îl selectează aleatoriu pe B.
Similar presupunem că portul 2 îl selectează pe A aleatoriu (dintre A şi B),
şi portul 3 îl selectează pe A aleatoriu (dintre A, B şi C). In final, portul 4 îl
selectează pe singurul său solicitant C.
Etapa 1
1 A 1 1 A 1 1 A 1
2 2 2
3 2 3 2 3 2
1 B 1 B 1 B
2 3 2 3 2 3
3 3 3
1 C 4 1 C 4 1 C 4
3 3 3
4 Cerere 4 Acord 4 a=1 Acceptare

Etapa 2
1 A 1 1 A 1 1 A 1
- - -
3 2 3 2 3 2
- B - B - B
2 3 2 3 2 3
3 3 3
1 C 4 1 C 4 1 C 4
3 3 3
- a=1 Cerere - Acord - Acceptare

Etapa 3
1 A 1 1 A 1 1 A 1
- - -
- 2 - 2 - 2
- B - B - B
- 3 - 3 - 3
3 1 3 3
- C 4 - C 4 - C 4
3 3 3
4 a=1 - -
Cerere Acord Acceptare

FIGURA 13.8 Varianta iterativă paralelă de asociere (PIM ), presupune ca toate intrările să trimită în
paralel cereri către toate ieşirile la care doresc să se conecteze. PIM utilizează aleatorizarea pentru a
face asocierea, astfel că ieşirile care primesc cereri multiple aleg aleatoriu o anumită intrare, iar
intrările care obţin acorduri multiple aleg aleatoriu ieşirea înspre care să transmită. Acest proces în trei
etape poate fi repetat pentru a îmbunătăţi dimensiunea asocierii.
Nu e suficientă rezolvarea competiţiei de la porturile de ieşire, deoarece
are loc o competiţie şi la porturile de intrare. Două porturi de ieşire pot
aloca aleatoriu resurse aceluiaşi port de intrare, care trebuie să selecteze un
port înspre care să transmită o celulă. De exemplu în imaginea din
mijlocul primului rând din fig. 13.8, A primeşte alocări din partea ieşirilor
2 şi 3. Astfel este necesară o a treia fază Acceptare, în care fiecare port de
intrare selectează aleatoriu un port de ieşire (dacă aleatorizarea a fost
utilizată în faza de alocare, de ce să o nu utilizăm din nou?).
Astfel, în imaginea din dreapta sus a figurii 13.8, A selectează în mod
aleatoriu portul 2, B şi C nu au de ales şi selectează porturile 1 şi respectiv
4. S-a realizat conexiunea crossbar, şi pachetele de la A la portul 2, B la
portul 1 şi C la portul 4 sunt transferate. Deşi în acest caz asocierea a fost
maximală (adică nu mai poate fi îmbunătăţită), în unele cazuri selecţiile
192
aleatoare pot da o asociere ce mai poate fi îmbunătăţită ulterior. De
exemplu în cazul improbabil ca porturile 1, 2 şi 3 să selecteze toate A, vor
fi asociate doar două.
In aceste cazuri, neprezentate în figură, algoritmul poate fi
îmbunătăţit prin mascarea tuturor intrărilor şi ieşirilor asociate, după care
se fac mai multe iteraţii (pentru acelaşi interval de timp care urmează).
Dacă asocierea în cazul iteraţiei curente nu este maximală, o iteraţie
următoare va îmbunătăţi asocierea cu cel puţin 1. Iteraţiile următoare nu
pot înrăutăţi asocierea, deoarece asocierile existente se păstrează de la o
iteraţie la alta. Deşi cel mai defavorabil caz pentru a atinge o asociere
maximală pentru N intrări este de N iteraţii, se arată că numărul cel mai
probabil de asocieri este mai apropiat de logN. Implementarea DECAN 2 a
utilizat 3 iteraţii pentru un comutator cu 30 de porturi.
Exemplul din fig.13.8 utilizează o singură iteraţie pentru fiecare
asociere. Rândul din mijloc arată a doua asociere pentru al doilea timp de
celulă, în care, de exemplu A şi C ambele transmit o cerere pentru portul 1
(dar nu şi B deoarece celula dinspre B înspre 1 a fost transmisă în ultimul
timp de celulă). Portul 1 îl selectează aleatoriu pe C şi asocierea finală este
A-3, B-2 şi C-1. Al treilea rând arată a treia asociere, de data aceasta de
dimensiune 2. Dupa a treia asociere numai celula destinată portului 3 în
coada de intrare B nu este transmisă. Astfel în patru timpi de celulă (din
care al patrulea nu e utilizat optimal şi nu poate fi utilizat pentru a se
transmite mai mult trafic) s-a transmis întreg traficul. Varianta aceasta e
mai eficientă decât cea cu bilete din figura 13.6.

13.8 EVITAREA ALEATORIZĂRII CU iSLIP


Asocierea paralelă iterativă, PIM este extrem de importantă, deoarece
introduce ideea că blocarea HOL poate fi evitată, cu un cost hardware
rezonabil. Dar PIM are doua probleme. Utilizează aleatorizarea, şi e greu
de creat o sursă acceptabilă de numere aleatoare la viteze foarte mari.
Apoi, e necesar un număr logaritmic de iteraţii pentru a obţine rezultate
maxime. Dar fiecare iteraţie necesită trei faze, iar decizia de asociere
trebuie luată în timpul minim de sosire al pachetelor, aşa că ar fi mai
eficientă o variantă care se apropie de asocierea maximă într-una sau două
iteraţii.
ISLIP este o variantă foarte răspîndită, care „dezaleatorizează” PIM,
astfel ca se obţine un rezultat foarte apropiat de asocierile maxime într-
una sau două iteraţii. Ideea este extrem de simplă. Când un port de intrare
sau de ieşire recepţionează cereri multiple, el va selecta cererea
„caştigătoare” în mod uniform până la aleatoriu pentru echitate. Ethernet
asigura echitatea folosind aleatorizarea, iar token-ring prin utilizarea unui
pointer round-robin, implementat prin tocken.
ISLIP asigura echitatea, alegând următorul câştigător dintre multiplii
competitori, în maniera round-robin, utilizând un pointer circular. Deşi
pointerii round robin pot fi iniţial sincronizaţi şi dau ceva asemănător cu
HOL, aceştia tind să devină liberi şi, pe termen lung, dau asocieri
maximale. Deci, subtilitatea în iSLIP nu este utilizarea pointerilor round-
robin, ci aparenta lacună de sincronizare pe termen lung între N pointeri ce
rulează în mod concurent.

193
Mai precis fiecare port de ieşire/intrare menţine un pointer g, setat iniţial
spre primul port de intrare/ieşire. Daca un port de ieşire trebuie să
selecteze multiplele cereri de intrare, el va selecta cel mai mic număr de
intrare egal sau mai mare ca g. Similar dacă un port de intrare are de ales
între multiple cereri provenite de la porturile de ieşire, el alege cel mai mic
număr de port de ieşire mai mare sau egal cu a, unde a este pointerul
portului de intrare. Cand un port de ieşire este asociat unui port de intrare
X, atunci pointerul portului de ieşire este incrementat la primul număr de
port mai mare decât X, în ordine circulară (de ex. g devine X+1 în afară de
cazul în care X a fost ultimul port, caz în care g se va afla în jurul primului
număr de port).
„Prioritatea rotitoare” permite fiecărei resurse (port de ieşire, port de
intrare) să partajeze destul de echitabil toţi competitorii, cu preţul a 2N
pointeri, de dimensiune log2N, suplimentari fata de N2 stări de planificare
necesare fiecărei iteraţii.
In figurile 13.9 si 13.10 se prezintă acelaşi scenariu ca în figura 13.8 (şi
figura 13.6), dar cu doua iteraţii iSLIP. Dacă fiecare rând este o iteraţie a
unei asocieri, fiecare asociere este prezentată utilizând două rânduri. Deci,
cele trei rânduri ale figurii 13.9 prezintă primele 1,5 asocieri ale
scenariului. Figura 13.10 prezintă celelalte 1,5 asocieri rămase.
Etapa 1, iteratia 1
1 A 1 g=A 1 A 1 1 A 1 g=B
2 2 2
3 a=1 3 3 a=2
2 g=A 2 2 g=A
1 B 1 B 1 B
2
a=1 3 g=A 2 3 2
a=1 3 g=A
3 3 3
1 C 4 g=A 1 C 4 1 C 4 g=A
3 3 3
4 a=1 Cerere 4 4 a=1
Alocare Acceptare

Etapa 1, iteratia 2
1 A 1 g=B 1 A 1 1 A 1
2 2 2
3 a=2 3 3
2 g=A 2 2
1 B 1 B 1 B
2 3 g=A 2 3 2 3
3 a=1 3 3
1 C 4 g=A 1 C 4 1 C 4
3 3 3
4 a=1 4 4 a=1
Cerere Alocare Acceptare

Etapa 2, iteratia 1
- A 1 g=B - A 1 - A 1 g=C
2 2 2
3 a=2 3 3 a=3
2 g=A 2 2 g=B
1 B 1 B 1 B
-
a=1 3 g=A - 3 -
a=2 3 g=A
3 3 3
1 C 4 g=A 1 C 4 1 C 4 g=A
- a=1 - - a=1
Cerere Alocare Acceptare

FIGURA 13.9 O etapă şi jumătate a unui exemplu de scenariu iSLIP

194
Imaginea din stânga-sus a figurii 13.9 este identică cu figura 13.8,
fiecare port de intrare transmiţând cereri spre fiecare port de ieşire pentru
care are celule destinate. O diferenţă este că fiecărui port de ieşire i se
asociază un pointer de alocare g, iniţializat pentru toate ieşirile la valoarea
A. Similar, fiecărui port de intrare i se asociaza un asa-numit pointer de
acceptare a, iniţializat la valoarea 1 pentru toate intrările.
Determinismul în iSLIP cauzează o divergenţă, imediat în faza de
Alocare. Compară imaginea din mijloc sus a figurii 13.9 cu imaginea din
mijloc sus a figurii 13.8. De exemplu dacă ieşirea 1 recepţionează cereri
de la toate cele trei porturi de intrare, portul selectat va fi A, deoarece A
este cel mai mic număr de intrare mai mare sau egal cu g1 = A. In figura
13.8, portul 1 selectează aleatoriu portul de intrare B. In această etapă
determinismul la iSLIP se dovedeşte a fi un dezavantaj real deoarece A a
trimis cereri şi înspre porturile 3 şi 4. Deoarece 3 şi 4 au de asemenea
pointerii de alocare g3 = g4 = A porturile 3 şi 4 îl vor selecta deasemnea
pe A ignorând pretenţiile lui B şi C. C fiind singurul care trimite o cerere
înspre portul 4 va primi alocarea din partea portului 4.
Daca A a primit trei alocări din partea porturilor 1, 2 şi 3, A îl acceptă pe
1, deoarece 1 este prima ieşire egală sau mai mare ca pointerul de
acceptare a lui A, aA, care a fost egal cu 1. Similar C îl alege pe 4. Acestea
fiind făcute A îl incrementează pe aA la 2 si C incrementeaza aC la 1
(incrementarea circulară a lui 4 este 1). Numai la aceasta etapă ieşirea1
incrementează pointerul său de alocare, g1, la B (cu 1 mai mare decât
ultima alocare ce a avut loc cu succes) iar portul 4 în mod similar face
incrementarea la A (cu 1 mai mare decat C în ordine circulară). Se observă
că celelalte porturi care au dat alocări portului A, adică 2 şi 3 nu îşi
incrementează pointerii de alocare deoarece A a refuzat alocările lor. Dacă
aceştia ar fi făcut incrementarea, ar fi fost posibilă construirea unui
scenariu în care porturile de ieşire menţin incrementarea pointerului lor de
alocare, dincolo de un anumit port de intrare I dupa alocări fără succes,
astfel înfometând continuu portul de intrare I. Asocierea are doar
dimensiunea 2; dar spre deosebire de figura 13.8 acest scenariu poate fi
îmbunătăţit printr-o a doua iteraţie prezentată în figura 13.9. La sfarşitul
primei iteraţii intrările şi ieşirile asociate nu sunt conectate prin linii
îngroşate (însemnând transfer de date), ca în dreapta sus la figura 13.8.
Acest transfer de date va aştepta sfârsitul ultimei iteraţii (iteraţia a doua).
A doua iteraţie (rândul din mijoc al figurii 13.9) începe cu intrările care
nu au fost asociate într-o iteraţie precedentă (aceasta înseamnă B) adresând
cereri numai porturilor de ieşire neasociate până acum. Astfel B trimite
cereri lui 2 şi 3 (şi nu lui 1 deşi B are de asemenea o celulă destinată lui 1).
Ambele porturi de ieşire 2 şi 3 trimit alocări portului B, iar B îl selectează
pe 2 (cel mai mic număr de port care este mai mare sau egal cu pointerul
său de acceptare). Pentru evitarea înfometării, iSLIP nu-şi incrementează

195
pointerii în celelalte iteraţii în afară de prima. Deci B nu-şi va incrementa
pointerul de acceptare la 3 (1 plus 2-ultimul acceptat).
După conectarea lui B la 2, pointerul de alocare a lui 2 rămâne A şi
pointerul de acceptare al lui B rămâne 1. Aceasta fiind iteraţia finală, toate
perechile asociate incluzând perechi cum ar fi A,1 asociate în iteraţii
anterioare, sunt conectate şi are loc transferul de date (liniile îngroşate).

Etapa 2, iteratia 2
- A 1 g=C - A 1 - A 1 g=C
2 2 2
3 a=3 3 3 a=3
2 g=B 2 2 g=B
1 B 1 B 1 B
-
a=2 3 g=A - 3 -
a=2 3 g=A
3 3 3
1 C 4 g=A 1 C 4 1 C 4 g=A
3 3 3
- a=1 Cerere - - a=1
Alocare Acceptare

Etapa 3, iteratia 1
- A 1 g=C - A 1 - A 1 g=A
- - -
3 a=3 3 3 a=4
2 g=B 2 2 g=B
- B - B - B
- 3 g=A - 3 - 3 g=B
3 a=2 3 3 a=2
1 C 4 g=A 1 C 4 1 C 4 g=A
- - -
- a=1 - - a=2
Cerere Alocare Acceptare

Etapa 3, iteratia 2
- A 1 g=A - A 1 - A 1 g=A
- - -
3 a=4 3 3 a=4
2 g=B 2 2 g=B
- B - B - B
-
a=2 3 g=B - 3 -
a=2 3 g=B
3 3 3
1 C 4 g=A 1 C 4 1 C 4 g=A
- - -
- a=2 - - a=2
Cerere Alocare Acceptare

FIGURA 13.10 Cealalta etapa si jumatate din exemplul de scenariu iSLIP

In al treilea rând se observă cum sincronizarea iniţială a pointerilor de


alocare şi acceptare e întreruptă. Deoarece un singur port de ieşire a trimis
un mesaj de alocare înspre A, acest port 1 trebuie să acorde prioritate
celorlalte porturi de intrare. Dacă A are al doilea pachet destinat lui 1 (nu
se prezintă în exemplu), 1 îi va trimite mesaj de alocare lui B.
Rândurile rămase din figura 13.9 şi figura 13.10 trebuie examinate cu
atenţie pentru a intelege regulile de împrospătare a pointerilor de alocare şi
acceptare şi care pachete vor fi comutate la fiecare etapă. In ultima
imagine din figura 13.10 se observă ca singura celulă care a rămas de
comutat este celula dinspre B înspre 3. Acest lucru poate fi realizat într-un
al patrulea timp de celulă.
In figura 13.11 se prezintă un sumar al planificării finale (făcându-se
abstracţie de mecanismele interne) a scenariului iSLIP pentru comparaţie
cu figura 13.7. Deoarece acestea sunt doar nişte exemple particulare, ele
nu sugerează că iSLIP (la fel ca şi PIM) ar reduce risipa de diviziuni de

196
timp prin evitarea blocării în fruntea liniei. Se observă că iSLIP la fel ca şi
PIM comută acelaşi trafic de intrare în 4 timpi de celulă, si nu 6.
Comparand figurile 13.9 şi 13.8, iSLIP pare să fie inferior lui PIM,
deoarece sunt necesare două iteraţii pe asociere la iSLIP pentru a obţine
acelaşi număr de perechi asociate pentru care la PIM este nevoie de-o
singură iteraţie pe asociere. Aceasta ilustrează mai degrabă penalizarea pe
care iSLIP trebuie s-o plătească la pornire şi nu o penalizare pe termen
lung. In practică, pe moment ce pointerii iSLIP se desincronizează, iSLIP
functioneaza foarte bine cu o singură iteraţie şi unele implementări
comerciale utilizează doar o singură iteraţie: iSLIP este extrem de popular.

Timp (in sloturi de celula)


3 2 1
A 1 A B C
3 2 1 2 B A
B
3 C A B
4 3 1
C 4 C

FIGURA 13.11 Evitarea blocării HOL de către iSLIP creşte debitul util din scenariul din figura 13.7

Foarte sumar, iSLIP se deosebeşte de PIM prin înlocuirea aleatorizării


cu o planificare folosind o planificare circulară (round robin) folosind
pointeri de intrare şi de ieşire. Acestei caracterizări îi lipsesc două aspecte
subtile ale iSLIP.
• Pointerii de alocare sunt incrementaţi numai în faza a treia,
după acceptarea alocării Intuitiv, daca O acordă o alocare portului de
intrare I, nu există nici o garanţie că I o va accepta. Dacă O şi-ar
incrementa pointerul de alocare se poate ajunge la o înfometare
permanentă a traficului între I şi O. Şi mai grav, Mc Keown a arătat că
această variantă round-robin reduce debitul util la numai 63 % (pentru
sosiri Bernoulli) deoarece pointerii tind să se sincronizeze şi să se ajungă
la blocaj.
• Toţi pointerii sunt incrementaţi numai după recunoaşterea
acceptării primei iteraţii: regula previne înfometarea, dar scenariul este
mult mai subtil decât exerciţiul prezentat.
Asocierile realizate în iteraţii, altele decât prima din iSLIP, sunt
considerate un bonus crescând debitul util fără a fi contorizate în
dezavantajul cotei unui port.

13.8.1 Extinderea iSLIP la multicast si priorităţi


LIP poate fi extins pentru a trata trafic multicast şi pe bază de priorităţi.

PRIORITĂŢI
Priorităţile sunt utile pentru a transmite traficul critic sau de timp real
mai rapid prin comutator. Cisco GSR permite traficului voice over IP, să
fie planificat cu o prioritate mai mare decat restul traficului, deoarece are o
rată limitată şi deci nu poate înfometa alte categorii de trafic.

197
Implementarea Tiny Tera manevrează patru niveluri de prioritate.
Fiecare port de intrare are 128 de cozi virtuale (una pentru fiecare
combinaţie de 32 de ieşiri şi 4 niveluri de prioritate). Deci, la planificator,
fiecare intrare are 128 de biţi pentru controlul intrărilor.
Algoritmul iSLIP este modificat foarte simplu pentru a trata priorităţi.
Pentru început fiecare port de ieşire păstrează un pointer de alocare separat
gk pentru nivelul de prioritate k, şi fiecare port de intrare păstrează un
pointer separat de acceptare ak pentru fiecare nivel de prioritate k. In
esenţă se aplică algoritmul iSLIP, astfel încât fiecare entitate (port de
intrare, port de ieşire) să aplice algoritmul iSLIP la cel mai mare nivel de
prioritate existent la intrări.
Fiecare port de ieşire face alocarea doar pentru cererea cea mai prioritară
recepţionata. Un port de intrare I poate face o cerere la nivelul de prioritate
1 pentru ieşirea 5, şi o cerere la nivelul de prioritate 2 pentru ieşirea 6,
deoarece acestea sunt cererile de cea mai înalta prioritate pe care le-a avut
portul pentru ieşirile 5 şi 6. Daca ambele ieşiri 5 şi 6 fac alocări pentru I, I
nu va face selecţia bazându-se pe pointerii de acceptare deoarece cele
două celule au priorităţi diferite, ci o va selecta pe cea de prioritate mai
mare. La o acceptare, în prima iteraţie, pentru prioritatea k, între intrarea I
şi portul de ieşire O, pointerul de acceptare a priorităţii k la I şi pointerul
de alocare corespunzător priorităţii k la O, sunt incrementaţi.

MULTICAST
Datorita aplicaţiilor (videoconferinta) şi protocoalelor (IP multicast) in
care ruterele transmit pachetele înspre un număr de porturi de iesire mai
mare ca unu, multicast în comutatoare, ca entitate de prima clasă, a
devenit importantă. Algoritmul ia-ţi un bilet a tratat multicastul ca o
entitate de clasa a doua, prin transmiterea întregului trafic de multicast la o
entitate/entităţi centrală, care apoi va transmite traficul de multicast
pachet-cu-pachet ieşirilor corespunzătoare. Mecanismul ia-ţi un bilet
iroseşte resurse de comutatie (lăţime de bandă de control, porturi) şi
asigură performanţe mai scăzute (latenţa mai mare, trafic mai scăzut)
pentru multicast.
Pe de alta parte, figura 13.4 arată că multicastul este suportat cu uşurinţă
pe calea de date a unui comutator crossbar. De exemplu dacă intrarea 1
(fig.13.4) transmite un mesaj pe magistrala sa de intrare şi punctele de
conexiune sunt conectate astfel încât magistrala de intrare a lui 1 este
conectată la magistralele de ieşire verticale ale ieşirilor 2 si 3, atunci
ieşirile 2 şi 3 recepţionează o copie a pachetului /celulei la acelaşi moment
de timp. Dacă pachetele au lungimea variabilă, mecanismul distribuit de
control al variantei ia-ţi un bilet, trebuie să aleagă între a aştepta eliberarea
simultană a tuturor porturilor, şi transmiterea pachetelor unul după altul.
Extensia iSLIP pentru multicast, numită ESLIP, acorda multicastului
aproape acelaşi statut ca şi unicastului. Ignorând priorităţile pentru
moment, există o coadă suplimentară pentru multicast pentru fiecare
intrare. Pentru evitarea blocarii HOL, ar fi ideal să existe o coadă separată
pentru fiecare subset posibil de porturi de ieşire, ceeace ar duce la un
număr impracticabil de cozi (216 pentru 16 porturi). Astfel se utilizează o
singură coadă pentru multicast şi poate apărea blocarea HOL, deoarece nu

198
se poate începe procesarea unui pachet pană ce pachetele de multicast
anterioare lui nu sunt transmise.
Presupunem că intrarea I are pachete destinate ieşirilor O1, O2 si O3 în
fruntea cozii de multicast a lui I. Se spune că I are un fan-out de 3. Spre
deosebire de cazul unicastului, ESLIP păstrează un singur pointer de
alocare comun pentru multicast si nici un pointer de acceptare pentru
multicast. Utilizarea pointerilor comuni implică o implementare
centralizată, diferită de planificatorul ia-ţi un bilet. Pointerul comun
permite întregului comutator să favorizeze o intrare particulară, încât să-şi
poată completa întregul fan-out mai repede decât daca ar fi mai multe
porturi de intrare, care transmit porţiuni mici din fanout-ul lor de multicast
în aceleaşi momente de timp.
In exemplu, I va transmite o cerere de multicast înspre O1, O2 si O3.
Dar porturi de ieşire cum este O2 pot recepţiona cereri de unicast de la alte
porturi de intrare, de exemplu J. Cum va decide un port de ieşire între
pachete de unicast şi de multicast? ESLIP acorda traficului de multicast şi
de unicast prioritate mai mare în sloţi alternanţi de celulă. De exemplu în
sloţii impari O2 va selecta unicastul iar in sloţii pari multicastul.
Pentru a continua exemplul presupunem un slot impar şi presupunem că
O2 are cereri de unicast în timp ce O1 şi O3 nu au cereri de unicast.
Atunci O2 va transmite un mesaj de alocare de unicast în timp ce O1 şi O3
vor transmite alocări de multicast. O1 şi O3 aleg pentru alocare intrarea
corespunzătoare primului număr de port mai mare sau egal faţă de
pointerul comun de alocare G. Presupunem că s-a selectat I şi astfel O2 şi
O3 vor trimite alocări de multicast înspre I. Spre deosebire de unicast, I
poate accepta toate alocările de multicast.
Spre deosebire de planificarea unicast, pointerul de alocare la multicast e
incrementat doar după ce I şi-a completat fanoutul. Astfel în următorul slot
de celulă, când multicastul are prioritate, I va putea transmite la O2. Acum
fanout-ul e completat, pointerul de alocari multicast se incrementeaza la
I+1, si planificatorul transmite inapoi lui I un bit prin care-i spune ca s-a
terminat transmisia din capul cozii sale multicast, deci poate fi luat in
lucru urmatorul pachet multicast.Un multicast nu trebuie terminat intr-o
singura diviziune de celula(ceea ce ar presupune ca toate iesirile implicate
sa fie libere), dar prin folosirea fanout splitting pe mai multe diviziuni.
Desigur ca iSLIP poate induce blocarea HOLpentru multicast. Daca
pachetul P1din capul cozii multicast este destinat iesirilor O1si O2 si
ambele sunt ocupate, atunci urmatorul pachet P2, destinat lui O3 si O4
libere amandoua, trebuie sa astepte dupa P1. Ar fi mult prea dificila
implementarea fanout splitting-ului pemtru alte pechete decat cele din
capul cozii si asta deoarece nu ne putem permite cate un VOQ separat
pentru fiecare combinatie posibila de porturi destinatie.
Algoritmul final ESLIP care combina patru niveluri de prioritate si
multicastingul este implementat in ruterul CISCO GSR.

13.8.2 Implementarea iSLIP


Inima hardware-ului ce implementeaza iSLIP este arbitrul care alege
intre cele N cereri (codate ca bitmap), cautandu-l pe primul mai mare sau
egal cu un pointer fix ; acesta este codorul programabil de prioritati.
Planificarea comutatorului poate fi facuta cu un arbitru de alocari similar,
199
pentru fiecare port de iesire (pentru arbitrarea cererilor) si un arbitru de
acceptari pentru fiecare port de intrare (pentru a arbitra intre alocari).
Prioritatile si multicastul sunt reintroduse intr-o structura de baza prin
adaugarea unui filtru la intrari, inainte de arbitru ; de exemplu, filtrul de
prioritati va anula toate cererile cu exceptia celor de prioritate maxima.
Desi, in principiu, planificatoarele unicast pot fi proiectate folosind
cate un chip separat per port, starea este suficient de redusa ca sa fie
manevrata de un singur chip planificator cu fire cablate de control
spre/dinspre fiecare port. Algoritmul multicast necesita un pointer
multicast partajat per nivel de prioritate, care implica si el un planificator
centralizat. Centralizarea implica insa intarzieri sau latenta, pentru a
trimite cererile si deciziile de la cardurile liniilor de port spre/dinspre
planificatorul centralizat.
Pentru a tolera aceasta latenta, planificatorul lucreaza cu un pipeline de m
celule (8 in Tiny Tera), de la fiecare VOQ si n celule (5 in Tiny Tera) de la
fiecare coada multicast.Acest lucru presupune ca fiecare card de linie in Tiny
Tera trebuie sa comunice 3 biti per unicast VOQ, care inseamna dimensiunea
VOQ , maxim pina la 8. Cu 32 de iesiri si patru nivele de prioritate, fiecare port
de intrare are de trimis 384 de biti de informatie unicast. Fiecare card de linie isi
comunica si fanout-ul (32 de biti per fanout), pentru fiecaredin cele cinci pachete
multicastdin fiecare cele patru nivele de prioritate, ajungandu-se la 640 biti. Cei
32 ∗1024 biti ai informatiei de intrare sunt memorati intr-un SRAM pe chip.
Dar pentru accelerare, informatia despre capul fiecarei cozi (stari mai mici, de
exemplu, doar 1 bit per VOQ unicast) e memorata in bistabile, mai rapide dar
mai putin dense.
Consideram acum manevrarea ieratiilor multiple. Faza de cerere apare doar in
prima iteratie si trebuie modificata in celelalte iteratii prin mascare a intrarilor
associate.Astfel, K iteratii apar ca au nevoie de 2K pasi de timp, din cauza ca
pasii de alocare si acceptare necesita fiecare cate un pas de timp. La prima
vedere pare ca faza de grant a iteratie k+1 incepe dupa faza de acceptare a
iteratiei k ; si acest lucru, deoarece e nevoie sa stim daca un port de intrare I
afost acceptat in faza k, astfel incat e permisa o alocare pentru acea intrare in
iteratia k+1.
Urmatoarea observatie face partial posibil pipelining-ul: daca I intrari
receptioneaza orice grant in iteratia k, atunci I trebuie sa accepte exact una, si
deci este nedisponibila in iteratia k+1. Astfel pot fi relaxate specificatiile
implementarii (P3) pentru a permite fazei de grant din iteratia k+1 sa inceapa
imediat dupa faza de grant din iteratia k, si deci sa se suprapuna cu faza de
acceptare din iteratia k. pentru a face acest lucru, folosim un Sau intre toate
granturile intrarii I (la sfarsitul iteratiei k) pentru a masca toate cererile la I (in
iteratia k+1). Se reduce astfel timpul total cu un factor apropiat de 2 pasi de timp
pentru k iteratii, de la 2k la k+1. De exemplu, la implementarea iSLIP Tiny Tera
sunt trei iteratii in 51nsec ( ≈ viteza OC-192) cu un ceas de 175 Mhz ; fiecare
ciclu de ceas fiind de ≈ 5,7 nsec, iSLIP are 9 cicluri de ceas pentru a termina.
Deoarece fiecare pas de grant si acceptare necesita doua cicluri de ceas,
pipelining-ul e crucial pentru manevrarea a trei iteratii in 9 cicluri ;
implementarea naiva ar avea nevoie de minim 12 cicluri de ceas.

13.9.2 Scalarea la switch-uri mai mari


Pana acum au fost tratate switch-urile mici care necesitau rutere cu pana
la 32 de porturi, categorie in care intrau si actualele rutere Internet.
Deocamdata construirea codurilor cablate tinde sa limiteze numarul de
oficii care pot fi servite de un dulap cablat. Astfel, switch-urile pentru
200
LAN-uri plasate in dulapuri cablate sunt bine deservite de porturi de
dimensiuni reduse.
Dar in general, retelele telefonice foloseau cateva switch-uri foarte mari
care pot comuta 1.000-10.000 legaturi. Folosirea switch-urilor mari tinde
sa elimine legaturile switch-la-switch, reducand astfel latenta si crescand
numarul de porturi diponibile utilizatorilor. Astfel unii cercetatori pledeaza
pentru switch-uri mari, dar pana recent nu a existat suportul industrial
pentru ele.
Exista trei tendinte recente in favoarea proiectarii switch-uri mari:
1. DWDM: folosirea multiplexarii cu diviziune prin lungimea de
unda, pentru a asocia multiple lungimi de unda in legaturi optice
intre ruterele core, care va creste numarul de legaturi logice
comutate de ruterele core.
2. Fibra la domicilii: e o sansa reala ca in viitorul apropiat
conexiunile intre switch-uri si case/oficii sa fie cu fibre optice .
3. Rutere modulare multisasiu: exista un interes crescut in
dezvoltarea clusterelor de rutere, formate din rutere interconectate
prin retele de mare viteza. De exemplu, multe puncte de acces in
retele conecteaza ruterele prin legaturi FDDI sau Gigaswitch-uri.
Clusterele de rutere sau ruterele multisasiu cum mai sunt numite,
sunt de interes deoarece permit dezvoltarea incrementala.
Tipic, timpul de viata al unui ruter core e estimat la 18-24 de luni, dupa
care traficul creste, determinand ISP-urile sa renunte la vechile rutere si sa
se orienteze spre unele noi. Ruterele multisasiu pot prelungi timpul de
viata al unui ruter core la 5 ani sau mai mult, permitand ISP-urilor sa
inceapa cu rutere mici si apoi sa adauge altele clusterelor, conform cu
necesarul de trafic. Retelele Juniper au creat seria T de rutere, care permit
asamblarea a pana la 16 rutere unisasiu (fiecare cu pana la 16 porturi),
formand astfel un ruter cu 256 de porturi. In inima unui ruter multisasiu
se afla un sistem de comutare scalabil 256x256. CRS-1 este versiunea
Cisco a unui astfel de ruter.

13.9.1 Masurarea costului switch-ului


Inainte de problema scalarii, este importanta intelegerea principalelor
metrici de cost ale switch-ului. La inceputul telefoniei, punctele de
interconectare erau realizate cu switch-uri electromagnetice, si astfel cele
N 2 puncte de interconectare ale comutatorului crossbar reprezentau un
cost major. Chiar si astazi acesta este principalul cost pentru ruterele foarte
mari, de dimensiune 1000. Dar fiind vorba de interconectari cu
tranzistoare, dimensiunea lor e redusa.
Limitarea reala a switch-urilor electronice este data de numarul de pini
al circuitului integrat. Limita curenta este de 1000 de pini, dintre care o
mare parte sunt alocati altor functii, alimentare/pamintare, si e imposibil
de pus pe un singur chip chiar si un switch de 500x500. Desigur ca s-ar
putea multiplexa cateva intraride crossbar pe un singur pin, dar asta ar
incetini viteza fiecarei intrari la jumatate din viteza posbila de I/O a
fiecarui pin.
Cele N 2 puncte de interconectare ale comutatorului crossbar, constituie
o problema pentru N suficient de mare, dar pentru N<200, majoritatea
complexitatii interconectarii e continuta de chip. Astfel ca implementarea
201
numarul mari se fac fac prin interconectarea chip-urilor care contin
comutatoare crossbar mici. Costul dominant ramine numarul pinilor si
numarul de legaturi intre chip-uri. O masura mai rafinata a costului tine
cont si de tipul de pini, fiecare avand un cost diferit.
Alti factori care limiteaza constructia switch-urilor monolitice mari este
incarcarea capacitiva a magistralelor, complexitatea planificarii, spatiul in
dulapuri si puterea. Daca se construieste un switch 256x256 de tip
crossbar cu 256 magistrale de intrare si iesire, incarcarea acestora va duce
la scaderea vitezei. Apoi, un algoritm de planificare centralizat ca iSLIP,
care are nevoie de N 2 biti de planificare a starii, nu se adapteaza prea bine
la valori mari ale lui N.
O alta limitare este conditia, in multe rutere, de a avea putine (de
preferinta unul singur) porturi pe un card de linie. Situatia e la fel, din
condsiderente de putere disipata, si pentru numarul de carduri de linie ce
pot fi plasate intr-un dulap. Solutiile multirack si multisasiu sunt logice.

13.9.2 Reţele CLOS şi rutere multişasiu


Pentru switch-urile VLSI e important numărul total de comutatoare şi de
legături care le interconectează. Costul comutatoarelor realizate in tehnica
VLSI e constant indiferent de numărul punctelor de intersecţii. Firma
Juniper foloseşte o reţea Clos pentru a realiza un ruter multişasiu 256x256
interconectând 16x16 rutere T în primul etaj. O reţea Clos standard ar avea
nevoie de 16 rutere în primul etaj, 16 în etajul al treilea şi k=2*16-1=31
comutatoare în etajul mijlociu. Costul acestei configuraţii s-ar putea
reduce dacă s-ar impune k=n, dar atunci reţea Clos se va putea bloca.
Reţeaua Clos poate deveni neblocabilă prin rearanjare dacă oricare
intrare i poate fi conectată cu oricare ieşire o atâta timp cât există
posibilitatea rearanjării unor conexiuni din etajul mijlociu.Folosind k=n în
loc de k=2n-1 se reduce aproape de două ori numărul total de comutatoare
de pe etajul mijlociu, dar creşte complexitatea alogoritmului de rearanjare.
În cazul apelurilor telefonice se presupune că toate apelurile sosesc la
intrare în acelaşi timp. Când soseşte un nou apel, convorbirile existente
trebuie rearanjate.
Pentru a profita de complexitatea redusă si pentru a permite tratarea
apelurilor telefonice problema se poate rezolva elegant dacă mai întâi se
înlocuieşte fiecare apel cu pachete şi fiecare pachet cu celule de
dimensiune fixă. In fiecare interval de celulă, apar celule la fiecare intrare
destinate fiecărei ieşiri: fiecare celulă nouă are nevoie de o aplicare nouă a
algoritmului de potrivire, care să nu fie restricţionat de celulele
precedente. In loc rularii unui proces lent, ce foloseşte tehnica edge
coloring, se redistribuie traficul fiecarei intrari pe comutatoarele de pe
nivelul mijlociu (echilibrarea încarcarii, deterministă sau aleatoare).
Divizând pachetele în celule şi apoi distribuindu-le peste întreaga
structură Clos se obţine acelaşi efect ca prin manevrarea conexiunilor în
reţeaua cu comutare de circuit, deoarece toate căile sunt folosite simultan.
Afirmaţia este parţial adevărată deoarece pot apărea comutatoare
congestionate pe nivelul mijlociu.

202
Mai există posibilitatea de folosire a unui algoritm aleator, prin care
fiecare comutator de pe nivelul de intrare alege aleator un comutator de pe
nivelul mijlociu, pentru fiecare celulă care trebuie transmisă. În acest caz,
d.p.d.v. probabilistic fiecare comutator de pe nivelul mijlociu va
recepţiona N/n celule, numărul de ieşiri ale comutatorului de pe nivelul
mijlociu. Astfel, dacă avem un flux de celule care merg spre ieşiri diferite
vom avea un debit de 100% pentru algoritmul aleator.
Dar algoritmul de aleatorizare e greu de realizat, existând şi
probabilitatea de coliziune, când mai multe comutatoare de intrare doresc
să folosească acelaşi comutator de ieşire. Acest fenomen va reduce debitul
şi va impune folosirea bufferelor.Scăderea debitului poate fi compensată
prin cresterea vitezei de operare internă a comutatoarelor.
Un algoritm de aleatorizare relativ uşor de implementat şi cu eficienţă
crescută este generatorul de numere aleatoare Tausworth compus din trei
registre de deplasare cu reacţie care apoi sunt insumate SAU-EXCLUSIV.
Algoritmul de reasamblare a celulelor în pachete este mai complex dacă
se foloseşte echilibrarea aleatoare a încărcării, decât dacă se foloseşte
metoda deterministă de echilibrare a încărcării. În ultimul caz, logica de
reasamblare ştie de unde tebuie să sosească următoarea celulă.

13.9.3 Reţele Benes pentru rutere mari


Având în vedere faptul că switch-ul No.1.ESS pentru telefoane comută
65.000 de legături de intrare, Turner susţine faptul că Internet-ul ar trebui
să fie construit cu ajutorul a câtorva rutere mari în locul de mai multe
rutere mici. Aceste topologii pot duce la reducerea legăturilor risipite
pentru realizarea conexiunilor ruter-la-ruter între ruterele mici, reducându-
se costul; aceste topologii pot deasemenea reduce lungimea terminal-cale
cele mai defavorabile, reducând latenţa şi îmbunătăţind timpul de răspuns
al utilizatorului.
În esenţă, o reţea Clos are o dimensiune de aproximativ N N , din
punctul de vedere al complexităţii punctelor de intersecţii, folosind doar 3
nivele. Experienţa algoritmicii generale sugerează faptul că o reţea ar
trebui să aibă o complexitate a puctelor de intersecţii de N ∗ log N , în
acelaşi timp mărind adâncimea de comutare la logN. Astfel de reţele de
comutare pot exista şi au fost cunoscute ani de-a rândul în telefonie şi în
industria de operare paralelă. Alternative precum reţelele Butterfly, Delta,
Banyan şi Hypercube reprezintă o concurenţă bine cunoscută.
Acest subiect este foarte vast, astfel ca acest capitol se va concentra doar
asupra reţelelor Delta şi Benes. Asemenea reţele sunt folosite în multe
implementări. Spre exemplu switch-ul Gigabit de la Universitatea din
Washington foloseşte o reţea Benes, care poate fi considerată ca 2 copii a
unei reţele Delta. Cea mai simplă modalitate de a înţelege o reţea Delta
este prin recursivitate. Să presupunem că avem N intrări în stânga, iar
această problemă trebuie redusă la problema construirii a două reţele Delta
mai mici, de dimensiune N/2. Presupunem că există un prim nivel de 2 x 2
comutatoare.

203
Figura 13.14: Construirea recursivă a unei reţele Delta

Dacă ieşirea este în jumătatea superioară (MSB al ieşirii este 0), atunci
intrarea este direcţionată către reţeaua Delta N/2 superioară, dacă însă
ieşirea este în jumătatea inferioară (MSB=1) atunci intrarea este
direcţionată către reţeaua Delta N/2 inferioară.
Pentru a economisi la primul nivel 2 comutatoare de intrare, trebuie
grupate intrările în perechi consecutive, fiecare pereche făcând uz de
acelaşi comutator cu 2 intrări, cum se vede în fig.13.14. În acest fel, dacă
cele 2 celule de intrare dintr-o pereche merg către 2 jumătăţi de ieşire
diferite, atunci ele pot fi comutate în paralel, altfel una va fi comutată iar
cealaltă va fi abandonată sau stocată într-un buffer. Bineînţeles, acelaşi
proces poate fi repetat în mod recursiv pentru fiecare dintre reţelele Delta
de dimensiune N/2, unde din nou există un nivel de 2 x 2 comutatoare,
apoi 4 N/4 comutatoare e.t.c. Extinderea completă a unei reţele Delta este
vizibilă în prima jumătate a figurii 13.15. Se poate observa cum
construcţia recursivă din fig.13.14 se regăseşte în conexiunea dintre
primul nivel şi cel de-al doilea nivel din fig.13.15.

204
Figura 13.15: Efectuarea rutării multicast copiază de două ori folosind o reţea Benes.
Prima jumătate este o reţea Delta şi a doua jumătate este o reţea Delta oglindită.

Astfel, reducerea problemei la 2x2 comutatoare necesită logN nivele;


deoarece fiecare nivel are N/2 intersecţii, reţeaua Delta binară are o
complexitate a intersecţiilor şi legăturilor de N ∗ log N . Evident putem
construi o reţea Delta şi folosind d x d comutatoare în primul nivel şi
împărţind apoi reţeaua iniţială în d reţele Delta, fiecare de dimensiune N/d.
Se reduce numărul de nivele la logdN iar complexitatea legăturilor la
n*logdN. Având în vedere costurile VLSI, e mai ieftin de construit un chip
comutator cu o valoare d cât mai mare, pentru a reduce costul legăturilor.
Reţeaua Delta, ca şi reţeaua Banyan şi Butterfly, dispune de o
proprietate numită auto-rutare (self-routing). În cazul reţelei Delta binare,
se poate găsi calea unică de la o intrare dată la o ieşire dată o = o1,o2,...,os ,
exprimată în binar, urmărind legătura corespunzătoare valorii oi din
nivelul/stadiul i. Acest lucru reiese clar din fig.13.14, unde folosim MSB
la primul nivel, cel de-al doilea bit la cel de-al doilea nivel, etc. Pentru
d ≥ 2, se scrie adresa de ieşire ca un număr în baza d şi se urmăresc digiţii
succesivi în aceaşi manieră.
O proprietate interesantă a reţelei Delta este faptul că este reversibilă. Se
poate urmări a cale de la o ieşire către o intrare, urmărind biţii de la intrare
în acelaşi fel. În fig. 13.14 se poate observa că, mergând de la ieşire spre
intrăre, penultimul bit al intrării selectează unul dintre 2 comutatoare din
primul nivel, iar ultimul bit selectează intrarea. Proprietatea de
reversibilitate e importantă pentru că permite utilizarea unei versiuni
oglindite a reţelei Delta, cu proprietăţi similare cu reţeaua Delta originală.
O problemă a reţelei Delta o reprezintă congestia. Deoarece există o cale
unică de la fiecare intrare către fiecare ieşire, reţeaua nu este o reţea de
permutare. Spre exemplu, dacă fiecare pereche succesivă de intrări doreşte
să trimită o celulă către aceaşi jumătate de ieşire, doar jumătate din celule
vor ajunge la al doilea nivel. Dacă se repetă situaţia, doar un sfert din
celule vor ajunge la al treilea nivel, etc. Deci există combinaţii de cereri de

205
ieşire pentru care debitul reţelei Delta se reduce la o legătură, spre
deosebire de N legături.
O cale de a face reţeaua Delta mai puţin susceptibilă la congestionare în
cazul permutărilor arbitrare a cererilor de intrare, este adăugarea mai
multor căi între o intrare şi o ieşire. Generalizând ideile discutate la reţeua
Clos (fig.13.12), putem construi o reţea Benes (fig,13.15), care constă din
2 reţele de adâncime logN: jumătatea din stânga este o reţea Delta standard
iar jumătatea din dreapta este o reţea Delta inversată prin oglindire. Dacă
privim invers la jumătatea din dreapta, mergând de la ieşiri spre stânga
putem observa că conexiunile dintre ultimul nivel şi penultimul nivel sunt
identice cu conexiunile dintre primul şi cel de-al doilea nivel.
Se poate privi reţeaua Benes şi recursiv, adăugând la fig.13.14 al treilea
nivel de comutatoare 2 x 2, conectând acest nivel la cele 2 reţele de
dimensiune N/2 din mijloc, în aceaşi manieră în care comutatoarele din
primul nivel sunt conectate la cele 2 reţele de dimensiune N/2 (fig.13.16).
Această procedură recursivă poate fi folosită pentru a creea direct schema
din fig.13.15, fără a crea 2 reţele Delta separate.

Figura 13.16: Construirea recursivă a unei reţele Benes

Există asemănări între versiunea recursivă a reţelei Benes din fig.13.16


şi reţeaua Clos din fig.13.12, care pot fi folosite pentru a demonstra faptul
că reţeaua Benes poate ruta orice permutare de cereri de ieşire, similar cu
proprietatea de rearanjare fără blocare a reţelei Clos.
În fiecare din două iteraţii se caută o potrivire perfectă a primului şi
ultimului etaj din figura 13.16, ca mai înainte, şi se alege una din cele
două comutatoare de pe nivelul mijlociu. Dar algoritmul nu se opreşte aici
ci urmăreşte recursiv acelaşi algoritm de rutare în reţeaua Benes de
dimensiune N/2. Alternativ, întreg procesul poate fi formulat folosind
metoda edge coloring. Mesajul final este că rutarea permutărilor arbitrare
într-o reţea Benes este posibilă; dar metoda e complexă şi greu de obţinut
ieftin într-un interval minim de sosire a pachetului.
La reţeaua Clos se putea alege aleator comutatorul din etajul mijlociu; se
poate alege şi aici o destinaţie aleatoare pentru fiecare celulă în prima
jumătate. Apoi în a doua jumătate se poate folosi metoda de rutare Delta
inversată, pentru rutarea de la comutatorul intermediar aleator la
destinaţie.

206
Ca şi la reţelele Clos, se poate arăta că dacă se foloseşte un algoritm
aleator, nici o legătură internă nu devine congestionată atâta timp cât nici
o intrare sau ieşire nu devine congestionată. Intuitiv: jumătatea
responsabilă de împărţirea încărcării preia traficul destinat oricărei ieşiri
de la oricare intrare şi îl distribuie în mod egal spre toate cele N legături de
ieşire ale primei jumătăţi. În a doua jumătate, datorită structurii simetrice,
tot traficul este dirijat spre legătura de ieşire destinaţie.
De exemplu, considerăm că legătura de sus ajunge la primul comutator
de pe etajul final din figura 13.15. Această legătură de sus va purta
jumătate din traficul legăturii de ieşire 1 (deoarece legătura de sus
transportă tot traficul destinat ieşirii 1 din jumătatea de sus a nodurilor de
intrare şi datorită proprietăţii de împărţire a încărcării se obţine jumătate
din traficul destinat ieşirii 1). Tot aşa se poate observa că legătura de sus
poartă jumătate din traficul destinat ieşirii 2, etc.
În timp ce unele dintre aceste proprietăţi erau cunoscute dinainte, Turner
a extins aceste idei pentru traficul multicast. Problema nu a fost tratată la
ruterele multişasiu bazate pe reţeaua Clos. Tratarea multicast este bazată
pe modelul server ca în schema de acordare a unui bilet („take a ticket”).
Pentru a extinde ideile de rutare Benes şi pentru multicast, Turner a
elaborat o formă mai simplă de multicast numită „copiază de două ori”. În
această formă, fiecare intrare poate specifica două ieşiri. Mai departe este
treaba reţelei să trimită două copii ale celulei de la intrare spre porturile de
ieşire specificate. Dacă acest lucru este posibil şi legăturile de ieşire pot fi
reciclate spre intrare, atunci cele două copii create la prima parcurgere pot
fi extinse la 4 copii în parcurgerea a doua şi la 2i copii la parcurgerea i prin
reţeaua Benes.
Spre exemplu în figura 13.15 se observă faptul că cea de-a cincea intrare
are o celulă care este pusă în legătură atât cu 1000 (adică ieşirea 8) cât şi
cu 1111 (adică ieşirea 15). În prima jumătate, celula este rutată în mod
aleator către ieşirea 7 a celei de-a doua jumătăţi. În cea de-a doua
jumătate, se urmăresc biţii ieşirii reale, întâi MSB, până se ajunge prima
dată într-un loc în care cele două adrese de ieşire diferă. Deobicei, dacă
bitul curent are valoarea 0 se comută în sus, iar dacă are valoarea 1 se
comută în jos. De aceea, la primul nivel al reţelei copiate, celula este rutată
spre legătura de jos, pentru că ambele adrese de ieşire încep cu un 1.
Lucrurile se complică la nivelul doi, deoarece la poziţia a doua dinspre
dreapta adresele diferă. Astfel, comutatorul din nivelul doi (adică
comutatorul al patrulea de la vârf) trimite celulele în două direcţii. Cele
două căi de ieşire s-au despărţit la primul bit care diferă. Din acest
moment, fiecare dintre cele două celule urmăreşte adresa corespunzătoare
ieşirii sale. În figura 13.15, dacă se iau în considerare ultimii 2 biţi ai 1000
şi 1111, se observă faptul că cele 2 celule ajung la ieşirea 8 şi 15.
Se poate demonstra că dacă copierea se face la nivelul primului bit care
diferă, aceasta nu duce la nici o supraîncărcare a vreunei legături interne,
dacă legăturile de intrare şi ieşire nu sunt supraîncărcate. Rezultatul e
valabil şi la reţele copy-3 (reţea capabilă să producă 3 copii la o trecere).
În momentul în care o celulă ajunge la un comutator, ea poartă un
specificator multicast cu legătura de ieşire, care trebuie transformat în 2
sau mai mulţi specificatori unicast pentru fiecare parcurgere. În mod
similar, fiecare celulă trebuie să poarte cu ea 2 sau mai multe adrese pe
207
parcursul drumului. Utilizarea unui număr mic, precum 2 limitează
complexitatea operaţiei de mapare a porturilor şi dimensiunea antetului.
Utilizarea unor numere mai mari va duce la micşorarea numărului de
parcurgeri necesare pentru a replica o celulă muticast.
O proprietate îmbucurătoare a proiectării multicast a lui Turner este
faptul că fanout-uri cu valori mari, de tip multicast, pot fi prelucrate în mai
multe iteraţii. Acest lucru poate fi vizualizat ca un arbore binar multicast,
din cadrul mai multor reţele Benes conectate în serie. În realitate, este
refolosită aceaşi reţea Benes, astfel reducând costul. Imaginea
demonstrează eficienţa adăugării de noi conexiuni multicast. Aceasta
presupune de fapt adăugarea unei frunze la arborele existent, cauzând o
perturbare minimă la nivelul celorlalte noduri existente ale arborelui şi
celorlalte conexiuni.
În consecinţă, comutatorul Turner tinde să folosească resursele în mod
optim datorită reciclării. Proiectul permite o scalabilitate a resurselor de
N ∗ log N , poate trata orice configuraţie de trafic unicast şi multicast şi
poate adăuga sau şterge un punct terminal dintr-un arbore multicast într-un
interval de timp constant. Alte arhitecturi de comutatoare nu îndeplinesc
aceste condiţii, iar în practică pot face faţă doar la un trafic multicast de
dimensiune limitată. În prezent importanţa traficului multicast este
limitată, însă având în vedere creşterea importanţei videoconferinţelor prin
Internet se poate spune că va exista un viitor, în care comutatoare
multimedia de mari dimensiuni vor avea un rol cheie.
În concluzie trebuie menţionat faptul că la fel ca la comutatorul Clos,
simplitatea conceptuală a unei strategii de echilibrare aleatoare a
încărcării pentru o reţea Benes aduce o complexitate de implementare. În
primul rând, deoarece echilibrarea aleatoare a încărcării nu este perfectă,
comutatorul Turner necesită buffere şi controlul fluxului. În al doilea rând,
aleatorizarea trebuie facută corect. Primul prototip al comutatorului de la
Universitatea Washington a folosit echilibrarea simplă a încărcării, bazată
pe un numărător, însă atunci când a fost reproiectat la compania Growth
Networks, comutatorul a folosit un circuit de aleatorizare mult mai
sofisticat, care să poată rezolva chiar şi cele mai patologice modele de
intrare. În final, o resecvenţiere eficientă a celulelor a necesitat un doesebit
efort în această arhitectură.

208

S-ar putea să vă placă și