Explicații · doi
O descărcare care merge perfect este cazul ușor, și orice client îl gestionează competent. Diferențele interesante apar când lipsește o parte din postare, ceea ce se întâmplă mult mai des decât ar sugera cazul curat. Iată ce se petrece de fapt, și de ce costă atât de mult timp.
Forma problemei
O postare Usenet nu este un singur fișier. Sunt mii de articole mici, fiecare stocat independent pe serverele fiecărui furnizor, iar NZB-ul pe care îl descarci este lista numelor lor. O lansare de 6.5 GB înseamnă aproximativ 9,000 dintre ele.
Articolele dispar din motive obișnuite. Furnizorii le păstrează o perioadă de retenție fixă și apoi le șterg. Unii furnizori nu au primit deloc un anumit articol de la bun început, fiindcă propagarea între servere este imperfectă. Uneori o încărcare era incompletă când a fost publicată, iar o parte din ea nu a existat nicăieri vreodată. Oricare ar fi cauza, efectul este același: clientul tău cere un articol, iar răspunsul este un refuz, nu date.
Este destul de obișnuit încât cei care publică pe Usenet să țină cont. Aproape fiecare lansare vine însoțită de date de paritate, de obicei fișiere PAR2, adică informație redundantă suplimentară calculată din original. Dacă lipsește o parte din conținut, paritatea o poate reconstrui, cu condiția să ai destulă. O postare tipică poartă în jur de 10% paritate, deci o postare poate pierde o bună parte din sine și rămâne perfect recuperabilă.
Așadar o postare deteriorată nu este de obicei o descărcare stricată. Este o descărcare care cere puțină aritmetică înainte de a fi completă. Întrebarea este doar cât îi ia unui client să își dea seama, și tocmai acolo diferă printr-un factor de doi sau mai mult.
Partea costisitoare
Când un server nu are un articol, întoarce un refuz, iar clientul încearcă atunci următorul server din lista ta. Este lucrul corect de făcut, fiindcă al doilea furnizor îl are foarte des. Costul apare când nu îl are nimeni.
În acel caz clientul parcurge întreaga ta listă de furnizori, câte un server pe rând, și este refuzat de fiecare dintre ei înainte de a putea conchide că articolul chiar a dispărut. Parcurgerea este secvențială, iar refuzurile sunt mai lente decât presupune lumea. Le-am măsurat direct, pe o linie în repaus, fără niciun conținut în mișcare:
| furnizor | timp de răspuns la o cerere normală | timp de refuz al unui articol lipsă |
|---|---|---|
| Furnizorul A | 77.5 ms | 78.8 ms |
| Furnizorul B | 10.5 ms | 454 ms |
| Furnizorul C | 10.8 ms | 871 ms |
| Furnizorul D | 9.9 ms | 1,227 ms |
| Furnizorul E | 10.6 ms | 2,239 ms |
Două lucruri sar în ochi. Întâi, împrăștierea este uriașă: un refuz costă undeva între 79 ms și 2.2 secunde, în funcție de furnizorul care răspunde, o diferență de aproape treizeci de ori. Apoi, un furnizor refuză practic în timpul necesar întrebării, în vreme ce altul are nevoie de peste două sute de ori propriul timp normal de răspuns ca să spună nu. Nu este latență de rețea, este muncă ce se petrece la ei, iar întrebatul în mai puține dus-întors abia dacă ajută.
Adună: o parcurgere completă a cinci furnizori costă, pentru un singur articol care nu mai există, cam 5 secunde. Gândește-te acum că o postare deteriorată are multe astfel de articole, și că un client care le tratează în ordine plătește acel cost în mod repetat în timp ce conexiunea ta stă degeaba. Descărcarea nu este lentă fiindcă datele sosesc încet. Au încetat cu totul să mai sosească, iar clientul așteaptă să i se spună ceea ce are deja destule informații ca să deducă.
Soluția
Ideea nu este să facem refuzurile mai rapide, fiindcă nu controlăm furnizorii. Este să observăm momentul în care răspunsul nu mai contează.
În orice clipă a unei descărcări clientul știe două lucruri: câte bucăți lipsesc încă și câte date de paritate deține deja. Dacă paritatea din mână ajunge să reconstruiască tot ce a rămas în suspensie, atunci răspunsul la «are cineva articolul ăsta?» nu mai poate schimba rezultatul. Fie că este un da lent, fie un nu lent, fișierul se reconstruiește oricum. Continuarea parcurgerii nu cumpără nimic în afară de întârziere.
Așa că versiunea actuală încetează să mai întrebe în acel punct și trece direct la reparare. Reconstrucția în sine este rapidă și a fost mereu: aproximativ 2.3 secunde pe o postare de 6.5 GB, iar cifra aceea este aceeași în toate versiunile pe care le-am măsurat. Timpul economisit este în întregime așteptarea care nu mai are loc.
| postare de 6.5 GB, 60 de articole lipsă | timpul până la un fișier verificat |
|---|---|
| așteptarea refuzului de la fiecare furnizor | 23 s |
| oprirea de îndată ce paritatea acoperă golul | 13 s |
| din care, aritmetica de reparare propriu-zisă | 2.3 s |
Aceeași schimbare nu face absolut nimic pe o postare nedeteriorată, ceea ce este dovada cea mai limpede că face ce credem noi: fără articole lipsă nu există parcurgere de scurtat, iar cele două versiuni termină o sarcină curată de 6.5 GB în identice 7 secunde.
Este un compromis, nu un câștig gratuit. A repara devreme înseamnă a ține datele de paritate în memorie în loc să le arunci și să aduci altele mai târziu, deci memoria de vârf (RSS) pe o postare grav deteriorată urcă de la aproximativ 0.8 GB la aproximativ 1.3 GB, și tragem cam 400 MB în plus de la furnizori. Pe o postare nedeteriorată memoria rămâne neschimbată la 0.24 GB. Credem că este compromisul potrivit pentru cei mai mulți, întrucât memoria se cheltuiește doar în cazul în care îți câștigă zece secunde, dar este un cost real și există o setare pentru el.
Numele furnizorilor sunt trecute sub tăcere în tabelul refuzurilor în mod deliberat. Cifrele sunt proprietatea unei singure măsurători, într-o seară, dintr-un singur loc, iar un furnizor care indexează altfel, sau pe care s-a întâmplat să îl prindem într-o seară proastă, nu ar trebui etichetat drept lent pe baza unei singure runde. Ce contează pentru argument este forma: refuzurile variază enorm, iar totalul este destul de mare cât să domine o descărcare deteriorată. Cifrele pe furnizor și ieșirea brută a sondelor sunt în evidența noastră internă, iar numerele de la un capăt la altul pe care le explică se află pe pagina de benchmark-uri.