Cozile de Execuţie sunt un set de obiecte ce oferă facilităţile necesare pentru a planifica evaluarea algoritmilor pentru datele de intrare specificate. Acestea sunt create pe mai multe fire de execuţie, de obicei pe fiecare procesor pe care îl posedă sistemul, însă nu sunt limitate la număr.
-
Porţile oferă facilităţile necesare pentru utilizarea unui alt calculator în reţea pe post de unitate de evaluare. Conţine atât entităţile necesare pentru implementarea unui server cât şi pentru a utiliza un astfel de server în o aplicaţie tip client.
-
Planificatorul oferă un set obiecte necesare pentru a planifica distribuţia datelor la mai multe unităţi de evaluare. Tot aici intră şi modulele de echilibrare a încărcării.
-
Dispecerul este entitatea care leagă toate modulele şi expune un model extrem de simplificat pentru programatorul ce va lucra cu această librărie.
3.4. Crearea şi utilizarea algoritmilor
Algoritmii sunt o parte esenţială a librăriei DCalcCore. În continuare vor fi descrise tehnicile utilizate în ce priveşte descrierea, implementarea şi utilizarea algoritmilor.
Librăria DCalcCore expune 3 interfeţe publice pentru programator: IScript, IAlgorithm şi IAlgorithmProvider.
Interfaţa IScript în sine nu este utilă dar ajută la abstractizarea conceptului de funcţie dinamică (script). Orice clasă care doreşte să expună o funcţie spre a fi compilată dinamic trebuie să implementeze această interfaţă. Asambloarele au nevoie de datele expuse de către un obiect ce implementează IScript pentru a putea compila acea funcţie dinamic.
Interfaţa IAlgorithm este o extensie al interfeţei IScript adăugându-şi metodele şi proprietăţile necesare implementării unui algoritm. Algoritmul, deci, va expune librăriei funcţia ce va trebui compilată dinamic (prin intermediul IScript) dar va expune şi metodele care vor fi utilizate de către dispecerat şi planificator pentru a prelua seturi de intrare şi de a pune înapoi seturile de ieşire.
Fig. 3.1
Interfaţa IAlgorithmProvider nu este necesară în sine. Ea specifică un set de reguli care va fi util atunci când programatorul va implementa un program care poate să consume mai multe probleme. Acele probleme vor fi definite ca "provideri".
Fig. 3.2
Necesitatea introducerii interfeţei IScript ca punct de plecare pentru IAlgorithm rezultă din structură extrem de flexibilă a acestei librării. Se va observa în capitolele următoare că există o separare intenţionată între componentele de dispecerat a datelor şi a celor ce executive. Componentele executive au fost proiectate să lucrează doar cu funcţia propriu-zisă (adusă de IScript), pe când dispeceratul trebuie să ştie câte date de intrare există, care sunt luate şi alte astfel de decizii care pot fi luate doar dacă este cunoscut întregul algoritm (IAlgorithm).
Mai jos este adus un exemplu de o clasă care expune un algoritm prin intermediul interfeţei IAlgorithm:
public sealed class ExampleAlgorithm : IAlgorithm
{
public String MethodName
{
get { return "Test"; }
}
public String MethodBody
{
get { return @"int Test(int i) { return i; }"; }
}
public Int32 InputSetCount
{
get { return m_InputSetCount; }
}
public ScalarSet GetInputSet(Int32 setNumber)
{
return new ScalarSet(setNumber, setNumber);
}
public void ReceiveOutputSet(ScalarSet set, Int32 setNumber){}
public void PrepareToStart() {}
public void PrepareToFinish() {}
}
|
După cum se poate observa în acest exemplu, un algoritm (concept utilizat în DCalcCore) îşi expune atât funcţia ce trebuie compilată dinamic cât şi metodele de obţinere a datelor de intrare şi metode de plasare a datelor de ieşire. Observăm de asemeanea utilizarea clasei ScalarSet:
Fig. 3.3
Acesta entitate este utilizată pe larg în interiorul librăriei pentru ca permite gruparea seturilor de date, fie ele de intrare sau ieşire. Asta fiind spus, orice algoritm implementat trebuie să-şi împacheteze datele de intrare într-un astfel de set care va fi rutat mai departe către unităţile de evaluare. Trebuie precizat faptul că un algoritm în acest sistem poate primi doar parametri ce sunt date primitive cum ar fi numere întregi sau numere cu virgulă mobilă. De asemenea trebuie precizat că fiecare set primeşte un Id unic de la algoritmul ce îl oferă. Acesta va fi folosit ulterior pentru a identifica setul de date rezultat ce va primi acelaşi Id de la unităţile de execuţie.
O consecinţă utilă al utilizării ScalarSet este faptul că algoritmii pot întoarce un set de date şi nu doar o singură valoare. Spre exemplu:
int APlusB(int a, int b) { return a + b; }
|
Algoritmul mai sus specificat va întoarce simplă sumă între 2 numere întregi, pe când următorul algoritm va întoarce mai multe rezultate simultan pentru 4 operaţii aritmetice de bază:
Object[] AOperB(Double a, Double b)
{ return new Object[] { a * b, a / b, a + b, a - b }; }
|
3.5. Asambloarele
Un asamblor reprezintă o entitate care compilează o funcţie dinamică descrisă de IScript şi produce un obiect rezultat ce implementează interfaţă ICompiledScript. Urmând conceptul de separare şi flexibilitate utilizat în întreaga librărie, există posibilitatea utilizării a mai multor tipuri de asambloare. Motivaţia acestei decizii pare a fi evidenta: Posibilitatea de a compila funcţii dinamice scrise în limbaje diferite. Un alt avantaj al utilizării mai multor asambloare este posibilitatea de a specifică diferite nivele de acces la librăriile adiţionale. Spre exemplu, se poate defini un asamblor care compilează funcţii şi le oferă doar strictul necesar de funcţionalităţi, cum ar fi operaţiile matematice asupra numerelor întregi. Un alt asamblor poate compila funcţii scrise în acelaşi limbaj dar poate să le ofere acces la librăriile de manipulare al documentelor Xml.
Librăria DCalcCore dispune de un asamblor proiectat pentru compilarea dinamică a funcţiilor scrise în limbajul C# şi cu acces minim la facilităţile sistemului.
În figura de mai jos acestea sunt reprezentate:
Fig. 3.4
După cum sa spus mai sus, în urma compilării unui script va rezulta o instanţă a clasei specifice asamblorului.
Pentru CSharpScriptAssembler, aceasta este DotNetCompiledScript. Este obligatoriu ca aceste clase rezultante să implementeze interfaţa ICompiledScript, altfel interfeţele de execuţie nu vor putea opera cu acestea. În figura de mai jos este reprezentată clasa DotNetCompiledScript şi relaţia acesteia cu interfaţa ICompiledScript.
Fig. 3.5
După cum se poate observa, unica metodă cerută de interfaţă este Execute(). Aceasta va fi „puntea” care va fi folosită de cozile de execuţie pentru a evalua seturile de date de intrare.
O mare importanţă trebuie dată compilării dinamice pe platforma .NET 2.0. Orice funcţie dinamic compilată va fi plasată într-un nou librărie (assembly) care va fi încărcată în aplicaţia ce va utiliza librăria DCalcCore. Întru-cât platformă nu oferă facilităţi de descărcare ulterioară a acestei librării, se recurge la creare unui nou Application Domain (echivalentul .NET a proceselor) în care acea va fi încărcată. Închiderea ulterioară a acelui Application Domain va duce şi la descărcarea librăriei. Însă acest model are şi minusurile sale:
-
Nevoia unei librării de "lipire" separate care va fi încărcată în DCalcCore cât şi în librăria generată din compilarea funcţiei. Această este necesară pentru putea comunică între Application Domain-ul în care este plasată librăria DCalcCore şi Application Domain-ul nou generat.
-
Pierderile de viteză rezultate din transportul datelor între procese.
Însă, datorită faptului că un framework bazat pe DCalCore trebuie să poată rulă o perioadă îndelungată fără a fi re-pornit, acele minusuri pot fi ignorate.
3.6. Interfeţele de execuţie
Pentru a putea ruta algoritmii către unităţile de execuţie, a fost introdus conceptul de interfaţă de execuţie. În această versiune a librăriei sunt definite doar două interfeţe de execuţie:
-
Execuţia directă reprezentată de clasa LocalMachineRunner. Această clasa este defapt nucleul întregului sistem prezentat în această lucrare. Chiar şi serverele bazate pe această librărie vor ruta algoritmii şi datele provenite de la clienţi acestei interfeţe spre a fi evaluate. LocalMachineRunner preia toate cererile de compilare al funcţiilor le efectuează pe loc returnând eşecul sau succesul acestei operaţii. În acelaşi timp această clasă foloseşte cozile de execuţie pentru evaluarea efectivă a datelor.
-
Rutarea la distanţa se face prin intermediul clasei RemoteMachineRunner. Aceasta oferă aceleaşi facilităţi ca şi LocalMachineRunner doar ca toate operaţiunile sunt defapt rutate unei porţi (descrise că următorul capitol).
În figura ce urmează sunt prezentate cele două clase şi interfaţa pe care o implementează:
Fig. 3.6
Pentru RemoteMachineRunner se poate observa că există un membru specific acestuia: Gate. Această proprietate permite obţinerea instanţei IRemoteGateClient căreia i se rutează toate cererile.
Se poate observa şi evenimentul QueueWorkCompleted la care se abonează consumatorii pentru a primi notificările aferente terminării execuţiei unei cereri. Acest eveniment utilizează clasa QueueEventArgs:
Fig. 3.7
După cum observăm acest container are 2 proprietăţi: OutputSet şi SetId. Deşi SetId pare redundant (set conţine în OutputSet deja), se precizează că OutputSet poate fi plasat intenţionat pe valoarea null în cazul când evaluarea setului a eşuat.
În concluzie, conceptul de „interfaţă de execuţie” simplifică mult munca pe care trebuie să o depună programatorul în ce priveşte conectarea şi utilizarea unei unităţi de execuţie, fie ea la distanţă sau locală. Acestea însă nu vor fi pe larg utilizate în afară librăriei datorită unităţii de dispecerat care ascunde majoritatea acestor detalii.
3.7. Cozile de execuţie
O coadă de execuţie este utilizată pentru a „programa” seturile de date de intrare spre a fi evaluate utilizând algoritmul specificat. Această coadă foloseşte un nou fir de execuţie pe care aşteptă datele şi le execută una câte una. Motivul de bază pentru care s-a recurs la utilizare acestui concept este de a simplifica planificarea evaluării fiecărui set de date utilizând algoritmul dat. O mare atenţie să dat stabililităţii acestei clase: sunt utilizate zone critice protejate de către un lock. Acestea se datorează faptului că coada poate fi accesată şi din alte fire de execuţie ceea ce poate crea momente de inter-blocare sau chiar corupere a stării, ceea ce nu este de dorit.
Asincronitatea stă la bază cozilor de execuţie, ceea ce impune anumite dificultăţi de implementare. Spre exemplu oprirea unui algoritm de la execuţie impune în primul rând căutarea tuturor cererilor de execuţie planificate şi extragerea lor dacă nu sunt deja în execuţie. Problema va apărea atunci când rămâne o cerere de evaluare care nu poate fi extrasă din coadă, acesta fiind în cele din urmă rutată spre consumator din firul de execuţie al cozii. O deosebită important trebuie acordată acestor cazuri.
În figura de mai jos este reprezentată interfaţă IWorkQueue şi implementarea acesteia: WorkQueue.
Fig. 3.8
Datorită recentei evoluţiei al procesoarelor multi-miez (multi-core) aceste sisteme devin din ce în ce mai populare. Împărţirea unui program pe mai multe fire de execuţie care ar putea rula pe acele miezuri ar mari considerabil viteza sumară.
Pentru acest caz a fost implementată clasa TheadedWorkQueue. În esenţă această coadă de execuţie nu are logic proprie, ci foloseşte mai multe cozi WorkQueue pentru a le planifica date spre evaluare. Tot aici se va întâlni pentru primă dată şi utilitatea echilibrării încărcării (Load Balancing) care va fi utilizată pentru a ţine toate miezurile la un grad de ocupare egal.
Fig. 3.9
3.8. Porţile
Porţile oferă facilităţile necesare pentru a conecta alte calculatoare în procesul de evaluare în unui algoritm. Motivul din care s-a efectuat o astfel de separare este de a oferi o flexibilitate sporită in ce priveşte numărul de opţiuni de conectare pe care le poate alege programatorul.
În figura de mai jos este prezentată interfaţa IRemoteGateClient:
Fig. 3.10
Modul de comunicare pe care îl va folosi acel client rămâne la discreţia programatorului. Există însă câteva reguli de bază care trebuie respectate:
-
Nu se vor accepta date de intrare dacă nu există conecţiune la server. Această regulă nu este impusă însă este de preferat să fie respectată. Motivul principal este că datele planificate vor sta mult timp într-o coadă până vor fi înapoiate planificatorului ceea ce va reduce performanţa calculului.
-
Trebuie ţinut cont de datele trimise către server. În caz că acela nu mai funcţionează, datele trebuie re-întoarse în sistem.
Pentru a putea realiza o aplicaţie de tip server se va implementa interfaţa IRemoteGateServer:
Fig. 3.11
Librăria DCalcCore pune la dispoziţia programatorului 2 clase deja create pentru a uşura dezvoltarea aplicaţiilor client-server.
Aceste clase client şi server sunt bazate pe protocolul de comunicare HTTP. Acesta a fost ales din mai multe motive:
-
Tranparenţa în ce priveşte transportul prin reţele securizate. De regulă, companiile au setate reţelele interne pentru a filtra majoritatea traficului ce nu este considerat standard. HTTP este considerat standard şi deci nu este supus unei astfel de filtrări.
-
Modelul deconectat pe care îl presupune HTTP ofere o scară largă de beneficii precum faptul că clientul şi serverul nu trebui să fie conectaţi tot timpul ci doar în cazurile când este nevoie. Acest lucru o oferă un mod unic de planifica datele spre execuţie, serverul va ţine datele rezultate un timp îndelungat până clientul se va conecta şi le va cere, rezultând intr-un sistem cu mult mai puţine erori.
-
Securitatea SSL oferită de protocolul HTTPS poate fi introdusă într-un mod extrem de simplu. Deşi clasele expuse de către DCalcCore nu oferă o astfel de protecţie, acestea pot fi extinse extrem de simplu.
3.9. Planificatorul
Deşi spus la singular, planificatorul defapt este un set de clase ajutătoare în sistem care oferă servicii de planificare a datelor de intrare şi un set de clase utilizat la balansarea încărcării. În figură de mai jos este prezentată clasa DataPlanner:
Fig. 3.12
Această clasă oferă toate metodele necesare pentru a putea afla la orice moment dat câte seturi de date de intrare au fost eliberate spre evaluare, câte seturi rămân şi alte informaţii vitale pentru o bună activitate a sistemului. Este important de înţeles că DataPlanner-ul nu va stoca informaţii privind cine a fost consumatorul acelor date, acest lucru rămânând pe seama interfeţelor de execuţie sau a gate-urilor.
Deşi planificatorul de date este o soluţie viabilă pentru controlul stării sistemului, acesta nu oferă metode de balansare a încărcării pentru că nu ştie cine este consumatorul propriu-zis. În acest scop au fost introduse elementele de echilibrarea încărcării (Load Balancers).
Fig. 3.12
Se poate observa în figura de mai sus că sunt definite doar 3 module de echilibrare în librărie. Acestea au fost create pentru a fi folosit în cele mai multe cazuri, însă sistemul poate fi cu uşurinţă extins pentru alte metode de echilibrare al încărcării.
O descriere mai detaliată al modului de funcţionare al modulelor de echilibrare se va face în capitulul următor.
3.10. Dispecerul
Ultima componentă dar şi cea mai importantă în librărie este dispecerul. Acesta este punctul de intersecţie a tuturor modulelor sistemului. Dispecerul oferă o interfaţă simplificată programatorului spre a putea utiliza facilităţile expuse cu uşurinţă:
Fig. 3.13
Dispecerul poate rula în 3 moduri:
-
Doar Local
-
Doar la distanţă
-
Combinat
În modul „doar local” toate evaluările se vor efectua doar utilizând LocalMachineRunner, deci direct în cozile de execuţie fără transferul prin reţea. În modul „doar la distanţă” se vor utiliza doar serverele conectate pentru a evalua un algoritm. Modul „combinat” implică ambele moduri.
Se poate observa că dispecerul oferit în librăria DCalcCore este o clasă generică peste 3 tipuri de date:
-
Modulul de echilibrare al încărcării local – cel ce va fi utilizat în scopul echilibrării încărcării în LocalMachineRunner între cozi.
-
Modulul de echilibrare al încărcării global – care va fi utilizat pentru a echilibra încărcarea între toate unităţile de calcul (local şi servere).
-
Asamblorul utilizat – asamblorul ce va fi utilizat pentru compilarea locală al algoritmilor. Acest tip, deşi obligatoriu, nu este folosit în modul „doar la distanţă”.
Pentru a crea o instanţă al clasei Dispatcher, în care tipurile modulelor de echilibrare al încărcării să fie alese la execuţie, se face apel la facilităţile .NET în ceea ce priveşte tipurile generice:
Type dynamicDispatcher = typeof(Dispatcher<,,>).MakeGenericType(
new Type[] { typeof(CSharpScriptAssembler),
m_WorkSpace.LocalBalancer,
m_WorkSpace.RemoteBalancer
});
m_Dispatcher = (IDispatcher)Activator.CreateInstance(
dynamicDispatcher,
new Object[] {
m_WorkSpace.QueueSize,
(m_WorkSpace.NumberOfThreads == 0) ?
Environment.ProcessorCount :
m_WorkSpace.NumberOfThreads, mode
});
|
După cum se observă în codul de mai sus tipul actual al Dispatcher-ului este generat în timpul execuţiei din tipul sau generic. Apoi acel nou tip generat dinamic se va instanţia şi utiliza în programul consumator.
4. Framework-ul DCalc
4.1 Framework software
Un framework software este un set de funcţionalităţi oferite programatorului pe care acesta le poate utiliza şi customiza. Că şi în cazul librăriilor software un framework oferă programatorului un mod simplificat de a-şi atinge scopul prin expunerea unui set de funcţionalităţi pe care acesta le poate utiliza. Diferenţa cheie, însă, dintre o librărie şi un framework este faptul că cea dintâi se comportă ca un "sclav" pentru programul dezvoltat, pe când un framework inversează această relaţie:
Fig. 4.1
Motivaţia de bază pentru care s-a introdus conceptul de framework este de a simplifica dezvoltarea aplicaţiilor şi de a reduce timpul necesar dezvoltării acesteia. Însă, datorită inversării acelei relaţiei de "vasalitate" între aplicaţie şi framework, un programator va trebui să adauge cod de "lipire" care va mări complexitatea aplicaţiei în majoritatea cazurilor. Aşa-dar dacă e să se utilizeze un framework, beneficiul trebuie să fie mult mai mare decât sacrificiile aduse.
Conform lui Pree, un framework software este compus din componente de 2 tipuri:
-
Componente fixe (frozen spots) – sunt acele părţi ale framework-ului care nu pot fi modificate şi sunt expuse programatorului în formă lor iniţială. Programatorul deci este obligat să-şi adapteze codul sau pentru a se potrivi cu acele componente.
-
Componente flexibile (hot spots) – oferă programatorului posibilitatea de a extinde funcţionalitatea de bază oferită de către acel framework şi de al adapta nevoilor proprii. Există şi componente care vor trebuie scrise de acesta de la zero.
În cazul framework-ului DCalc, flexibilitatea majorităţii punctelor vin din utilizarea librăriei DCalcCore ce a fost prezentată în capitolul precedent. Se va putea observa pe parcursul acestui capitol că framework-ul expus aici se va construi doar din componentele expuse de acea librărie.
4.2. Descrierea funcţională
Pentru a înţelege mai bine diferenţele cheie între DCalc şi alte tipuri de framework-uri pentru calcul distribuit, se va prezenta mai jos o schema care prezintă structura cea mai acceptată în timpul de faţă:
Fig. 4.2
DCalc, spre deosebire de schema convenţională, nu foloseşte procesul master pentru a distribui calculele la procesele worker. Această decizie a fost luată pentru a uşura modul în care se poate organiza întregul sistem:
Fig. 4.3
Deşi lipsă unui proces master poate fi considerat un dezavantaj, în arhitectura framework-ului DCalc s-a încercat eliminarea a cât mai multor puncte intermediare. Aşadar, procesele client înglobează în sine şi procesul master. Acest lucru nu este obligatoriu desigur: cum s-a putut observa în structura librăriei DCalcCore, majoritatea componentelor pot fi extinse sau rescrise.
Mai jos este prezentată o schema succintă a procesului client aşa cum au fost concepute pentru această lucrare:
Fig. 4.4
După cum se poate observa toate componentele ce fac parte din această schemă sunt preluate din librăria DCalcCore. Cum s-a specifica în timpul prezentării, aceasta a fost în special concepută pentru a fi utilizată ca bază pentru a scrie un framework. De asemenea se observă utilizarea componentelor pre-configurate cum ar fi clientul HTTP pentru accesul altor servere şi al dispecerului.
Mai jos va fi expusă o listă al modificărilor pe care le poate aduce programatorul pentru a-şi crea propriul proces client:
-
Selectarea modulelor de balansare a încărcării. Programatorul poate alege unul din cele trei moduri incluse de balansare a încărcării sau îşi poate scrie propria clasă care trebuie să implementeze interfaţa ILoadBalancer.
-
Definirea şi implementarea algoritmului pe care îl doreşte distribuit pe mai multe unităţi de calcul. Acest lucru revine în totalitatea programatorului, pentru că ăsta e scopul framework-ului. Nu există algoritmi pre-definiţi expuşi.
-
Conectarea la mesajele emise de Dispatcher privind progresul execuţiei. Programatorul trebuie să se aboneze la acele mesaje emise de către dispecer pentru a putea afla când se termină un algoritm de executat şi alte cazuri relevante.
-
Conectarea manuală a serverelor utilizate în dispecer. Aici programatorul va trebui să specifice dispecerului toate serverele care vor intra în cluster şi pe care vor fi distribuite calculele.
-
Configurarea relevantă a proceselor server. Fiecare proces server trebuie să ruleze pe un alt calculator. E de datoria programatorului sau al admnistratorului să pună la punct întregul grid pentru a putea fi utilizat de către framework.
După cum s-a descris mai sus, procesele server nu pot fi modificate structural, ci doar configurate în modul în care se doreşte planificarea grid-ului. Structura acestora rămâne rigidă, el reprezentând una din componentele fixe ale framework-ului:
Fig. 4.5
4.3. Echilibrarea automată al încărcării
Pe parcursul acestei lucrări s-a observat de mai multe ori utilizarea termenului de „echilibrare al încărcării”. Acest proces este esenţial într-un proces de calcul distribuit. Mai jos va fi descris acest concept, avantajele şi în cele din urmă ce tip de echilibrare s-a folosit în acest framework.
Nevoia echilibrării automate a apărut din cauza că este posibil ca unele unităţi de execuţie să-şi îndeplinească mai devreme sarcinile şi să devină inactive deoarece atribuţiile sunt împărţite inegal sau unele unităţi lucrează mai repede decât altele. În mod ideal, s-ar dori ca toate unităţile de execuţie să lucreze continuu asupra problemei ceea ce ar aduce cu sine un timp minim de execuţie.
Echilibrarea încărcării este utilă mai ales atunci când volumul de activitate nu este cunoscut înaintea începerii execuţiei. De asemenea, ea ajută la diminuarea efectelor datorate diferenţelor de viteză dintre unităţile de execuţie, chiar şi atunci când volumul de activitate este cunoscut în avans.
Echilibrarea poate fi realizată în două moduri:
-
Static – înaintea execuţiei oricărui proces. Echilibrarea statică a încărcării poartă în general numele de problemă mapării (Bokhari, 1981) sau problemă planificării (scheduling problem). Această problem se dovedeşte a fie foarte complexă şi nu are o rezolvare unică.
-
Dinamic – în timpul execuţiei proceselor.
Pentru implementarea echilibrării încărcării în framework-ul DCalc sa ales modul dinamic centralizat pentru simplitatea implementării acesteia dar şi pentru precizia mai bună care poate fi obţinută.
Framework-ul aduce 3 moduri diferite de echilibrare al încărcării:
-
Fair Load Balancer este cel mai simplist dar şi cel mai corect. Acest mod va selecta fiecare unitate de execuţie care are pe moment cele mai puţine calcule planificate. Minusul major al acestei metode constă în faptul că nu se ia în calcul starea unităţii de execuţie. Acest lucru rezultă într-o continuă încercare de a selecta acea unitate de execuţie chiar şi dacă acesta va re-întoarce datele planificate înapoi direct după aceea. Rezultatul final al acestui minus este că dacă un server nu este accesibil, performanţa sistemului întreg va scădea foarte mult datorită încercărilor continue de a selectă acel server.
-
Round Robin Load Balancer a fost implementat în general pentru a fi utilizat în scopul echilibrării încărcării între procesoarele locale. Este un mod complet inadecvat în echilibrarea serverelor.
-
Predictive Load Balancer oferă acelaşi grad de acurateţe ca şi Fair Load Balancer dar nu va lăsa serverele inaccesibile să scadă performanţă întregului sistem. Acest lucru este realizat utilizând un sistem de „punctare” al fiecărei unităţi de execuţie ce a cerut date.
Desigur există un număr nelimitat de algoritmi ce pot fi utilizaţi în scopul echilibrării încărcării. Însă aceste implementări sunt lăsate în latitudinea programatorului care doreşte să extragă maxim de performanţă.
4.4. Toleranţa la defecte
Toleranţa la defecte este un punct esenţial într-un sistem de calcul distribuit. Întotdeauna există posibilitatea că anumite componente ale reţelei utilizate să nu mai fie accesibile. Aceste defecte nu ţin de framework-ul prezentat, deşi şi o eventuală eroare apărută în procesele server pot duce la pierderea nodului şi cu el al datelor planificate acestuia. În cazul în care un astfel de defect a fost întâlnit, un sistem distribuit trebuie să-şi reconfigurează automat structura şi să excludă acel nod.
În cazul framework-ului DCalc, detectarea nodurilor „moarte” este de datoria aplicaţiei client şi anume al obiectelor HttpGateClient. Când un astfel de nod este detectat sistemul va:
-
Interoga obiectul HttpGateClient ataşat acelui nod pentru a afla care sunt seturile de date ce au fost planificate. Dacă au fost planificate, seturile sunt re-aduse planificatorului de date şi sunt marcate ca „eşuate”, în aşa mod ele vor putea fi re-planificate următorului client disponibil.
-
Modulul ales pentru echilibrarea încărcării este instructat că clientul „mort” nu a putut evalua acel număr de seturi. E de datoria acelui modul să-şi însemne acest lucru şi să evite un timp selecţia clientului pentru a minimiza pierderile de timp.
-
Firul de execuţie utilizat de către obiectul HttpGateClient îşi va micşora automat prioritatea. Acest lucru va favoriza ceilalţi clienţi permiţându-le o mai rapidă execuţie.
Trebuie de precizat că deşi un nod poate fi considerat „mort”, defapt e posibil că legătura între calculatorul pe care rulează clientul şi cel pe care este pornit serverul sa fie distrusă pe un timp limitat. Natura protocolului HTTP utilizat de către framework nu impune o conexiune continuă şi deci va permite ambelor părţi să aştepte un timp ca acel efect de reţea să dispară.
O deosebită atenţie trebuie acordată modulelor de echilibrare al încărcării. Aceste module expun un set de metode ce sunt apelate atunci când se doreşte selecţia următorului client ce va primi un set de date, sau când acel client va întoarce setul de date rezultat. Este important deci ca modulul de echilibrare să ia în considerare şi numărul de ori când un client nu a putut evalua setul de date de intrare. Dacă acel factor nu intră în decizia selecţiei următorului obiect e posibilă o scădere dramatică al vitezei de calcul pentru că datele vor fi într-o continuă mişcare spre clientul mort şi de la el.
În acest scop, modulul PredictiveLoadBalancer este cel mai bine adaptat. Acesta foloseşte un sistem bazat pe puncte de penalizare:
-
La selecţia următorului nod, acestuia i se atribuie un punct de penalizare şi un punct de încărcare. Celorlalte noduri li se va scădea câte un punct de penalizare dacă numărul acestora este mai mare ca 0.
-
Daca nodul raportează succesul operaţiunii de evaluare, lui i se va scădea un punct de încărcare. În cazul în care acesta are puncte de penalizare, i se vor scădea 2 puncte.
-
Daca nodul raportează eşecul operaţiunii de evaluare, i se va scădea un punct de încărcare şi se vor adaugă 3 puncte de penalizare.
-
Metoda de selecţie al următorului nod este simplă, şi anume: se va selecta acela care are suma „puncte de încărcare” + „puncte de penalizare” cea mai mică.
Această metodă nu va permite unui nod „mort” să fie selectat prea des. În aşa mod viteza de calcul globală nu va fi influenţată grav de moartea unui nod.
4.5. Descrierea aplicaţiei Client
Aplicaţia client este formată din 4 formulare – formularul principal, 2 utilizate pentru configurare şi unul pentru a adăuga / edita o conecţiune la server.
Fig. 4.6
Formularul de adăugare / editare al unei înregistrări server permite specificarea următoarelor date:
-
Numele înregistrării. Acesta depinde în totalitate de nevoile utilizatorului şi nu are relevanţă funcţională.
-
Adresa efectivă al server-ului. Aceasta poate fi atât nume DNS cât şi o adresă IP. DNS-ul va fi rezolvat automat în timpul rulării.
-
Portul pe care ascultă serverul adăugat. Acesta trebuie să fie egal cu portul specificat în aplicaţia server.
-
Cheia de securitate. Acesta iarăşi depinde de server. Dacă acesta este protejat printr-o cheie, aceasta trebuie introdusă în formular curent sau clientul nu va putea accesa serverul.
Fig. 4.7
Formularul de configurare a parametrilor sistemului permite setări de nivel jos în ce priveşte planificarea şi execuţia algoritmilor:
-
Setarea modului de echilibrare al încărcării local. Acesta va fi utilizat de către cozile de execuţie locale pentru a planifica evaluarea seturilor de date pe toate firele de execuţie ce au fost pornite.
-
Setarea modului de echilibrare al încărcării general. Acesta va fi utilizat de către sistem atunci când va fi nevoie de a afla care unitate de execuţie poate prelua setul de date în cauză.
-
Dimensiunea cozii. Sistemul are o coadă pe care încearcă să o menţină încărcată întotdeauna. Această setare permite specificarea dimensiunii acestei cozi, lucru ce poate influenţa pozitiv sau negativ execuţia unui algoritm.
-
Specificare numărului de fire de execuţie locale deschise. Se recomanda utilizarea opţiunii „Numărul de fire egal cu numărul de procesoare”. În aşa mod se va putea obţine maxim de performanţe pe calculatorul local.
Fig. 4.8
Formularul de selecţie şi configurare al algoritmului permite selecţia unui algoritm din lista celor detectaţi şi configurarea ulterioară al acestuia:
-
Sunt afişate numele programatorului ce a dezvoltat acel algoritm, versiunea acestuia şi o descriere relevanţă despre funcţionalitatea lui.
-
Poate fi invocat formularul de configurare specific acelui algoritm. Setările pot influenţa evaluarea acestui algoritm dacă este conceput în aşa fel.
Fig. 4.9
Formularul principal suportă următoarele funcţionalităţi:
-
Adăugarea, editarea şi ştergerea serverelor ce vor fi utilizate în timpul evaluării unui algoritm. De asemenea, serverul poate fi pornit sau oprit.
-
Accesul la formularul de configurare al aplicaţiei cât şi a celui de selecţie al algoritmului.
-
Vizualizarea progresului efectuat în timpul rulării programului prin intermediul unui grafic ce ilustrează viteză de execuţie globală.
-
Pornirea şi oprirea evaluării algoritmului selectat.
-
Salvarea şi deschiderea fişierelor „masă de lucru” care conţin toate configurările făcute de către utilizator oferind o mai simplă metodă de lucru.
Fig. 4.10
Fig. 4.11
Un bonus deosebit oferit de către aplicaţia DCalc îl constituie conceptul de „masă de lucru”. Acest concept înglobează totalitatea configurărilor pe care le-a efectuat utilizatorul în cadrul sesiunii curente al clientului. DCalc. Aceste setări pot fi salvate şi ulterior re-deschise, uşurând în aşa fel automatizarea numitor calcule.
Fig. 4.12
Acesta figură ilustrează implementarea folosită în DCalc. „Masă de lucru” este stocată în format Xml.
Algoritmii pe care aplicaţia îi poate utiliza sunt expuşi în formă de module „plug-in” ce sunt plasate în directorul „AddIns”. Aceste module trebuie să expună o clasă ce implemetează interfaţă IAlgorithmProvider. Programul va încerca să încarce toate aceste module şi să găsească aceste clase definite în acele module. Odată găsite ele sunt înregistrate pentru ca utilizatorul să le poată selecta.
Fig. 4.13
Mai jos este expus codul utilizat în scopul de a găsi toate clasele expuse de un plug-in care implementează interfaţa IAlgorithmProvider:
Type[] interfaces = type.GetInterfaces();
Boolean isRight = false;
foreach (Type intf in interfaces)
{
if (intf == typeof(IAlgorithmProvider))
{
isRight = true;
break;
}
}
if (isRight)
allProviders.Add(
(IAlgorithmProvider)Activator.CreateInstance(type));
|
4.6. Descrierea aplicaţiei Server
Aplicaţia Server nu conţine decât un singur formular care include atât setările ce pot fi aduse acesteia cât şi metodele de pornire / oprire acestuia. Este inclus şi un mod grafic de vizualizare al încărcării totale.
Următoarea listă descrie pe scurt cele mai importante elemente vizuale remarcate în aplicaţie:
-
Portul ce va fi utilizat pentru a accepta clienţii. Trebuie selectat un port ce nu este ocupat de o altă aplicaţie. În caz contrar va fi generată o eroare.
-
Timpul de viată alocat unui client. Datorită naturii protocolului HTTP, este necesară o restricţie asupra timpului cât clientul va fi considerat conectat.
-
Modulul de echilibrare al încărcării ce va fi folosit între firele de execuţie locale.
-
Cheie de securitate. Dacă este selectată acesta opţiune, trebuie adusă o cheie unică ce va servi drept parolă pentru clienţi. Pentru simplitate, este adus un buton ce poate genera chei unice.
-
Numărul de fire de execuţie locale ce vor fi pornite. Se recomandă utilizarea opţiunii implicit selectate.
-
Panelul de vizualizare stare va furniza informaţii precum numărul de clienţi conectaţi, numărul algoritmilor încărcaţi si starea curentă a cozii locale.
-
Posibilitatea de a porni şi opri serverul.
Fig. 4.14
Serverul nu este limitat la doar un client sau doar un algoritm. Orice număr de clienţi şi algoritmi sunt admişi. Există însă o limitare: procesarea cererilor de la clienţi se fac pe acelaşi fir de execuţie. În practică asta înseamnă că serverul poate satisface următorul client doar după ce termină cu actualul. Acest lucru nu are un impact mare pentru că în timpul comunicării cu clientul de obicei se preiau date de intrare şi se dau date de ieşire ceea ce nu durează mult.
5. Calcularea produsului a două matrici
5.1. Matricea
Matricea este unul dintre conceptele fundamentale în matematică şi este utilizate pe scară largă în calculatoare. Definiţia matematică al acesteia:
O matrice de dimensiuni este in esenţa o simplă funcţie unde este o mulţime nevidă. este un produs cartezian al mulţimilor si . Spunem că matricea este o matrice definită asupra mulţimii .
O matrice ce are n linii si m coloane se numeşte matrice , unde n si m sunt numite dimensiuni. După cum se observă in figura de mai sus, elementele reprezintă valoarea fiecărei celule definite in matrice unde 0 ≤ i ≤ m-1 si 0 ≤ j ≤ n-1.
Matricea reprezintă un important concept matematic întâlnit in majoritatea ştiinţelor exacte. Un număr impresionant de metode numerice au fost dezvoltate pentru a uşura calculele si transformările ce pot fi aduse acestora.
Studiul matricelor a început cu mult timp in urma. „Pătratul magic” ce avea dimensiunea apare in literatura chineza datând in jurul anilor 650 î.e.n.
Matricole au fost aplicate de mult în rezolvarea ecuaţiilor lineare. Un text chinez important datând anilor 300 î.c şi 200 d.c. “Nouă capitole al artei matematice” (Jiu Zhang Suan Shu), este primul exemplu de utilizare al metodelor matriceale pentru a rezolva simultan mai multe ecuaţii. În capitolul 7, “Prea mult dar nu destul” conceptul de “determinant” apare pentru prima dată aproape cu 2000 de ani înaintea oficialei publicări ai acestuia de către matematicianul japonez Seki Kowa în 1683 şi de Gottfied Leibniz în 1693.
“Pătratele magice” erau cunoscute matematicienilor arabi chiar începând cu secolul 7, când arabii cuceriseră partea de nord-vest al subconştinentului indian şi au învăţat matematică indiană. Se presupune că ideile ce au intra la baza acesteia au apărut în China. Primele „Pătrate magice” de ordin 5 şi 6 apar în enciclopedia din Bagdad circa 983 d.c.
După dezvoltarea teoriei determinantului de către Seki Kowa şi Leibniz la sfârşitul secolului 17, Cramer a continuă evoluţia acesteia în secolul 18, concepând bine-cunoscuta regulă lui Cramer în 1750. Carl Friedrich Gauss şi Wilhelm Jordan au conceput eliminarea Gauss-Jordan la începutul secolului 19.
Termenul „matrice” a fost conceput în 1848 de către J.J.Sylvester. Cayle, Hamilton, Grassmann, Frobenius şi von Neumann sunt doar câţiva matematicieni ce au continuat dezvoltarea teoriei matricilor.
5.2. Produsul matricial
Se consideră matricea A cu m linii si n coloane si matricea B cu n linii si p coloane. Rezultatul înmulţirii acestora este matricea AB (notată şi A · B). Dacă C = AB, si este elementul matricei C de la poziţia (i,j), atunci:
pentru fiecare pereche i si j cu 1 ≤ i ≤ m si 1 ≤ j ≤ p.
-
Dostları ilə paylaş: |