Cum se obține o performanță bună de citire concurentă de pe disc

Aș dori să pun o întrebare, apoi să o urmez cu propriul meu răspuns, dar și să văd ce răspunsuri au alte persoane.

Avem două fișiere mari pe care le-am dori să le citim de la două fire separate în același timp. Un fir va citi secvențial fișierul A, în timp ce celălalt fir va citi secvențial fișierul B. Nu există nici o blocare sau comunicare între fire, ambele se citesc secvențial cât de repede pot și ambele elimină imediat datele pe care le citesc.

Experiența noastră cu această configurare pe Windows este foarte slabă. Procesul combinat al celor două fire este de ordinul a 2-3 MiB/sec. Unitatea pare să fie petrecându-și majoritatea timpului căutând înapoi și înainte între cele două fișiere, probabil lectură foarte puțin după fiecare caută.

Dacă dezactivam una din fire și analizăm temporar performanța unui singur fir, atunci obținem o lățime de bandă mult mai bună (~ 45 MiB/sec pentru această mașină). Deci, în mod clar, performanța negativă cu două fire este un artefact al planificatorului de discuri OS.

Is there anything we can do to improve the concurrent thread read performance? Perhaps by using different APIs or by tweaking the OS disk scheduler parameters in some way.

Unele detalii:

Fișierele sunt de ordinul a 2 GiB fiecare pe o mașină cu 2GiB de RAM. În scopul acestei întrebări, considerăm că nu sunt cache și defragmentează perfect. Am folosit instrumente de defragmentare și am rebootat pentru a ne asigura că este cazul.

Nu folosim API-uri speciale pentru a citi aceste fișiere. Comportamentul este repetabil pe diferite API bogate standard, cum ar fi CreateFile Win32, C fopen, C ++'s std :: ifstream, FileInputStream Java etc.

Fiecare fir se rotește într-o buclă care face apeluri la funcția de citire. Am variat numărul de octeți solicitați de API fiecare iterație de la valori între 1KiB și 128MiB. Variajul acestui lucru nu a avut niciun efect, atât de clar că suma pe care sistemul de operare le citește fizic după ce fiecare disc este căutat nu este dictat de acest număr. Acesta este exact ceea ce trebuie așteptat.

Diferența dramatică între performanțele cu un fir și cele cu două fire este repetabilă între Windows 2000, Windows XP (32-bit și 64-bit), Windows Server 2003, dar și cu și fără hardware RAID5.

0
fr hi bn

6 răspunsuri

Utilizați IOCompletionPorts în Windows? Windows via C ++ are un capitol aprofundat pe această temă și ca noroc ar fi, este disponibil și pe MSDN .

0
adăugat

Aș dori să adaug câteva alte note în răspunsul meu. Toate celelalte sisteme de operare non-Microsoft pe care le-am testat nu suferă de această problemă. Linux, FreeBSD și Mac OS X (această versiune finală pe hardware diferit) toate se degradează mult mai grațios în termeni de lățime de bandă agregată atunci când se deplasează de la un fir la două. De exemplu, Linux a degradat de la ~ 45 MiB/sec la ~ 42 MiB/sec. Aceste sisteme de operare trebuie să citească bucăți mai mari ale fișierului dintre fiecare căutător și, prin urmare, să nu-și petreacă aproape tot timpul de așteptare pe disc să caute.

Soluția noastră pentru Windows este de a trece FILE_FLAG_NO_BUFFERING pavilion la CreateFile și de a folosi mari (~ 16MiB) citește în fiecare apel la readfile . Acest lucru este suboptimal din mai multe motive:

  • Fișierele nu sunt stocate în memoria cache atunci când se citesc astfel, astfel încât nu există niciunul dintre avantajele pe care caching-ul le dă în mod normal.
  • Constrângerile atunci când lucrați cu acest steguleț sunt mult mai complicate decât lectura normală (alinierea tampoanelor de citire la limitele paginii etc.).

(De exemplu, Windows este incapabil să facă OI la mai multe fișiere simultan cu orice eficiență, astfel încât în ​​timp ce schimbarea tuturor celorlalte operațiuni IO este forțată să fie disproporționat de lentă.)


Editați pentru a adăuga câteva detalii suplimentare pentru Will Dean:

Desigur, în cadrul acestor diferite configurații hardware, cifrele brute s-au schimbat (uneori substanțial). Problema însă este degradarea consistentă a performanței pe care doar Windows o suferă atunci când se deplasează de la un fir la două. Iată un rezumat al mașinilor testate:

  • Mai multe stații de lucru Dell (Intel Xeon) de diferite vârste care rulează Windows 2000, Windows XP (32 biți) și Windows XP (64 biți) cu o singură unitate.
  • Un server Dell 1U (Intel Xeon) care rulează Windows Server 2003 (64-bit) cu RAID 1 + 0.
  • O stație de lucru HP (AMD Opteron) cu Windows XP (64-bit) și Windows Server 2003 și hardware RAID 5.
  • PC-ul meu de familie fără fir (AMD Athlon64) care rulează Windows XP (32-bit), FreeBSD (64-bit) și Linux (64-bit)
  • MacBook-ul meu acasă (Intel Core1) care rulează Mac OS X, unitate SATA unică.
  • PC-ul meu de acasă Koolu care rulează Linux. Foarte slab în comparație cu celelalte sisteme, dar am demonstrat că chiar și această mașină poate depăși un server Windows cu RAID5 atunci când citește mai multe discuri.

Utilizarea CPU pe toate aceste sisteme a fost foarte scăzută în timpul testelor și antivirusul a fost dezactivat.

Am uitat să mai menționez, dar am încercat, de asemenea, API-ul normal Win32 CreateFile cu setul de coduri FILE_FLAG_SEQUENTIAL_SCAN . Acest steag nu a rezolvat problema.

0
adăugat
Bine ați venit la Microsoft.
adăugat autor v.oddou, sursa

Se pare un pic ciudat că te văd nici o diferență peste o gamă destul de largă de versiuni de Windows și nimic între o singură unitate și hardware RAID-5.

Doar simt intelegere, dar asta ma face sa ma indoiesc ca aceasta este o problema simpla. Altele decât OS X și Raid5, au încercat toate acestea pe aceeași mașină - ai încercat o altă mașină? Este folosirea CPU practic zero în timpul acestui test?

Care este cea mai scurtă aplicație pe care o puteți scrie care demonstrează această problemă? - Aș fi interesat să încerc aici.

0
adăugat
per unitate vs. raid5: dacă citiți date secvențiale din două fișiere suficient de mari, nu puteți evita toate capetele de disc care caută înainte și înapoi; dimensiunea benzii este de obicei 16-128kB, deci pentru a citi 1MB de date, aveți nevoie de toate (sau de cele mai multe) capete pentru a căuta acolo.
adăugat autor tzot, sursa

Paul - a văzut actualizarea. Foarte interesant.

Ar fi interesant să-l încercați pe Vista sau Win2008, deoarece oamenii par să raporteze unele îmbunătățiri considerabile de I/O pe acestea în anumite circumstanțe.

Singura mea sugestie despre un alt API ar fi sa incercati memoria de cartografiere a fisierelor - ati incercat asta? Din păcate, la 2GB per fișier, nu veți putea să cartografiați mai multe fișiere întregi pe o mașină pe 32 de biți, ceea ce înseamnă că acest lucru nu este la fel de banal cum ar putea fi.

0
adăugat
mergând la astfel de extinde doar pentru a face ceva de lucru pe ferestre, aș fi pur și simplu pleda pentru a muta procesul său la linux. care este costul fiecărei soluții? sincer...
adăugat autor v.oddou, sursa

Aș crea un fel de blocare în siguranță a memoriei. Fiecare fir ar putea aștepta blocarea până când a fost liber. Când blocarea devine liberă, luați blocajul și citiți fișierul pentru o anumită perioadă de timp sau o cantitate definită de date, apoi eliberați blocarea pentru orice alte fire de așteptare.

0
adăugat

Problema pare să fie în politica Windows I/O de planificare. În conformitate cu ceea ce am găsit aici există multe modalități pentru un sistem de operare pentru a programa cererile de disc. În timp ce Linux și alții pot alege între diferite politici, înainte ca Windows Vista să fie blocat într-o singură politică: o coadă FIFO, unde toate cererile sunt împărțite în blocuri de 64 KB. Cred că această politică este cauza problemei pe care o întâmpinați: planificatorul va amesteca cererile din cele două fire, provocând căutarea continuă între diferitele zone ale discului. Vestea bună este că, în conformitate cu aici și aici , Vista a introdus un planificator de discuri mai inteligent, unde puteți seta prioritatea solicitărilor dvs. și alocați, de asemenea, o lățime minimă pentru proces. Vestea proastă este că nu am găsit nicio modalitate de a modifica dimensiunea politicii discului sau tampoanelor în versiunile anterioare de Windows. De asemenea, chiar dacă creșterea priorității discului de intrare/ieșire a procesului dvs. va crește performanța în raport cu celelalte procese, aveți în continuare problemele legate de firele dvs. în competiție. Ceea ce vă pot sugera este să modificați software-ul dvs. prin introducerea unei politici auto accesate de disc De exemplu, ați putea folosi o politică ca aceasta în firul dvs. B (similar pentru Thread A):

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

Ai putea folosi semafoare pentru verificarea stării sau ai putea folosi contoarele perfmon pentru a obține starea coada de disc reală. Valorile lui X și/sau Y ar putea fi de asemenea auto-reglate, prin verificarea ratelor reale de transfer și modificarea lentă a acestora, maximizându-se astfel debitul atunci când aplicația rulează pe diferite mașini și/sau O.S. S-ar putea să găsiți că nivelurile de memorie cache, memorie sau RAID le afectează într-un fel sau altul, dar cu auto-tuning veți obține întotdeauna cea mai bună performanță în fiecare scenariu.

0
adăugat