Ich lerne gerade CUDA Streams durch die Berechnung eines Skalarprodukts zwischen zwei Vektoren. Die Bestandteile sind eine Kernfunktion, die Vektoren x und y aufnimmt und einen Vektor Ergebnis der Größe gleich der Anzahl der Blöcke zurückgibt, wo jeder Block seine eigene reduzierte Summe beiträgt.CUDA Streams Performance
Ich habe auch eine Host-Funktion dot_gpu, die den Kernel aufruft und reduziert den Vektor Ergebnis zum Endprodukt Wert Punkt.
Die synchrone Version genau dies tut:
// copy to device
copy_to_device<double>(x_h, x_d, n);
copy_to_device<double>(y_h, y_d, n);
// kernel
double result = dot_gpu(x_d, y_d, n, blockNum, blockSize);
während die Asynchron geht man wie:
double result[numChunks];
for (int i = 0; i < numChunks; i++) {
int offset = i * chunkSize;
// copy to device
copy_to_device_async<double>(x_h+offset, x_d+offset, chunkSize, stream[i]);
copy_to_device_async<double>(y_h+offset, y_d+offset, chunkSize, stream[i]);
// kernel
result[i] = dot_gpu(x_d+offset, y_d+offset, chunkSize, blockNum, blockSize, stream[i]);
}
for (int i = 0; i < numChunks; i++) {
finalResult += result[i];
cudaStreamDestroy(stream[i]);
}
Ich schlechtere Leistung bekommen, wenn Streams mit und versuchte, die Gründe zu untersuchen. Ich habe versucht, die Downloads, Kernel-Aufrufe und Uploads zu pumpen, aber ohne Ergebnisse.
// accumulate the result of each block into a single value
double dot_gpu(const double *x, const double* y, int n, int blockNum, int blockSize, cudaStream_t stream=NULL)
{
double* result = malloc_device<double>(blockNum);
dot_gpu_kernel<<<blockNum, blockSize, blockSize * sizeof(double), stream>>>(x, y, result, n);
#if ASYNC
double* r = malloc_host_pinned<double>(blockNum);
copy_to_host_async<double>(result, r, blockNum, stream);
CudaEvent copyResult;
copyResult.record(stream);
copyResult.wait();
#else
double* r = malloc_host<double>(blockNum);
copy_to_host<double>(result, r, blockNum);
#endif
double dotProduct = 0.0;
for (int i = 0; i < blockNum; i ++) {
dotProduct += r[i];
}
cudaFree(result);
#if ASYNC
cudaFreeHost(r);
#else
free(r);
#endif
return dotProduct;
}
Meine Vermutung ist, dass das Problem innerhalb der ist dot_gpu() Funktionen, die nicht nur den Kernel nicht nennen. Sagen Sie mir, wenn ich das richtig die folgenden Stream Ausführungen verstehen
foreach stream {
cudaMemcpyAsync(device[stream], host[stream], ... stream);
LaunchKernel<<<...stream>>>(...);
cudaMemcpyAsync(host[stream], device[stream], ... stream);
}
Der Host führt alle drei Befehle ohne blockiert zu werden, da cudaMemcpyAsync und Kernel-Rückkehr sofort (aber auf der GPU diese ausgeführt werden sequentiell als sie derselben zugeordnet sind, Strom). Der Host geht also zum nächsten Stream (auch wenn stream1 weiß, in welchem Stadium es ist, aber wen interessiert das. Er macht seinen Job auf der GPU, oder?) Und führt die drei Befehle erneut aus, ohne blockiert zu werden. Und so weiter und so weiter. Allerdings blockiert mein Code den Host, bevor er den nächsten Stream irgendwo innerhalb der dot_gpu() Funktion verarbeiten kann. Liegt es daran, dass ich & Sachen freigebe, sowie das vom Kernel zurückgegebene Array auf einen einzigen Wert reduziere?