În timp ce făceam câteva exemple de bază de CUDA realizate de NVIDIA am copiat un cod pentru a testa creșterea de viteză de la calculul pe CPU la cel pe GPU pentru înmulțirea matricelor.
După 30 de minute în care m-am uitat la rezultate și am văzut cum CPU-ul meu (da, CPU) efectua calcule de 1000 de ori mai rapide decât GPU-ul meu, mi-am dat seama că sincronizarea nu funcționa corect. Un fragment din cod arată astfel (acesta este codul de la NVIDIA):
//Create timers
cudaEvent_t start;
cudaEvent_t stop;
float simpleKernelTime;
float optimisedKernelTime;
//start timer
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
matrixMultKernel<<<grid, block >>>(a_d, b_d, c_d, N);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
// Print time and do other things
cudaEventRecord(start, 0);
matrixMultCPU(a_h, b_h, d_, N);
cudaEventRecord(stop, 0)
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
// Print time
Acest cod funcționează bine pe o mașină Linux (am copiat același cod ca și persoana de lângă mine și el obținea o sincronizare bună), dar pe o mașină Windows 8 cu Visual Studio 2013, sincronizarea pe partea de CPU (a doua jumătate a fragmentului) nu funcționa (dădea întotdeauna ~0,003ms).
De ce se întâmplă acest lucru? Am rezolvat problema folosind <time.h>
(eliminând cudaEventRecord()
apelurilor și folosind abordări standard de sincronizare a codului C), deci nu vreau să știu cum se repară, ci mai mult de ce se întâmplă acest lucru.
Din câte am înțeles, evenimentele CUDA nu sunt concepute pentru a măsura timpul dedicat numai CPU (numai gazdă) în sine, ci mai degrabă execuția kernelului și apelurile API CUDA. De la Ghid de programare CUDA C 3.2.5.6.
Evenimente (sublinierea îmi aparține):
Timpul de execuție oferă, de asemenea, o modalitate de a monitoriza îndeaproape progresul dispozitivului, , precum și de a efectua o cronometrare precisă, permițând aplicației să înregistreze în mod asincron evenimente în orice punct al programului și să interogheze când aceste evenimente sunt finalizate.
Sunt, de asemenea, surprins că obțineți vreun timp (lansările de kernel sunt asincrone), deoarece codul dvs. lipsește cudaEventSynchronize()
:
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
A se vedea, de asemenea Cum să implementați măsurători de performanță în CUDA C/C++.
Pentru măsurarea timpului doar pentru CPU, consultați acest fir de discuție.
EDIT:
Pentru a obține timpul corect pentru matrixMultCPU()
trebuie să adăugați sincronizarea pentru start
eveniment:
cudaEventRecord(start, 0);
cudaEventSynchronize(start);
- Opps, greșeala mea! Eu folosesc
cudaEventSynchronize(stop);
în codul meu original. Cu toate acestea, nu este necesar să se utilizezecudaEventRecord()
funcționează pe CPU în alte sisteme/compilatoare (nu știu sigur de ce). Adică, codul este scris de NVIDIA, nu de mine, și l-am văzut corect timp în alte sisteme de operare/compilatoare, doar că nu funcționează pe sistemul meu. – > . - Poate că întrebarea mea ar fi trebuit să fie formulată invers atunci: De ce
cudaEventRecord()
funcționează pentru cronometrarea codului non-GPU pe Linux cunvcc
? – > . - @AnderBiguri: Mi-am editat răspunsul. Vezi dacă funcționează pentru tine. Probabil că implementarea GNU/Linux sincronizează
start
în mod implicit. – > . - Înțeleg, mulțumesc. Voi lăsa această problemă deschisă pentru o vreme, deoarece mă interesează de ce se întâmplă acest lucru… > .
cudaEventRecord
apelurile și am folosit sincronizarea standard a codului C. – > Por Ander Biguri.