[英]cudaMemcpyAsync with pageable memory blocks host thread?
我用可分页内存写了一个关于 cudaMemcpyAsync 的简单代码,并做了一些分析。
Kernel
__global__ void
vectorScale(float *C, float scal, size_t numElements)
{
int i = blockDim.x * blockIdx.x + threadIdx.x;
clock_t start_clock = clock();
clock_t clock_offset = 0;
while (clock_offset < 14100){
clock_offset = clock() - start_clock;
}
if (i < numElements) C[i] *= scal;
}
主要的
int main(int argc, char* argv[])
{
cudaStream_t stream;
// Print the vector length to be used, and compute its size
size_t numElements = 1024*1024*256;
size_t size = numElements * sizeof(float);
printf("[Vector scale of %zu elements]\n", numElements);
float *h_C;
if(argc==1) h_C = (float *)malloc(size);
else cudaHostAlloc((void**)&h_C, size, cudaHostAllocPortable);
// Initialize the host input vectors
for (size_t i = 0; i < numElements; ++i)
h_C[i] = i*0.01;
// Allocate the device vector C
float *d_C = NULL;
cudaMalloc((void **)&d_C, size);
cudaStreamCreate(&stream);
cudaMemcpyAsync(d_C, h_C, size, cudaMemcpyHostToDevice, stream);
int threadsPerBlock = 256;
int blocksPerGrid =(numElements + threadsPerBlock - 1) / threadsPerBlock;
vectorScale<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(d_C, 2.0, numElements);
cudaMemcpyAsync(h_C, d_C, size, cudaMemcpyDeviceToHost, stream);
cudaStreamSynchronize(stream);
// Free device global memory
cudaFree(d_C);
// Free host memory
if(argc==1) free(h_C);
else cudaFreeHost(h_C);
cudaStreamDestroy(stream);
return 0;
}
如快照所示,在“CUDA API”行中,cudaMemcpyAsync 的时间成本很大,这意味着主机 thead 被阻塞。 它看起来与文档指南不一致。
顺便说一句,如果我使用固定的 memory,cudaMemcpyAsync 的行为与预期一样,主机线程会在 cudaStreamSynchronize() 处阻塞。
似乎很明显(您的测试用例是一个证明点)在某些情况下cudaMemcpyAsync
可能成为主机线程的阻塞操作,不再是相对于其他主机 CPU 线程活动的“异步”。
我没有发现任何证据表明这与文档相矛盾,该文档没有任何声明,例如“在任何情况下cudaMemcpyAsync
都不会阻塞主机线程”。 如果您知道任何此类声明,请指出。 实际上, 文档表明您正在观察的内容是预期的:
如果异步 memory 副本涉及未页面锁定的主机 memory,则它们也将是同步的。
cudaMemcpyAsync
的异步特性取决于将数据传输到固定分配或从固定分配传输(对于 D->H 或 H->D 的传输方向)。 固定分配是必需的,以便编程到 DMA 引擎中的将“异步”进行传输的底层操作具有主机数据的固定映射。 这是必需的。
可分页分配意味着底层逻辑->物理映射可以随时更改,这会阻止cudaMemcpyAsync
的正常操作。 由于cudaMemcpyAsync
的预期用例是将数据复制到固定分配/从固定分配复制数据,我推测 CUDA 运行时 API 开发人员认为在这种情况下可接受的行为是阻塞主机线程并“立即”完成传输(即同步。我不希望任何违反 CUDA stream 语义的传输,无论是同步还是异步)。 同样,您的测试用例是一个证明点。
继续提供“异步”行为的唯一其他选择 w.r.t。 我可以想象的主机线程将启动另一个 CPU 线程来异步处理操作。 同样,我推测 CUDA 运行时 API 开发人员认为,对于此 API 的非首选用例,开发和维护此类机器是没有必要的。
然而,这里的猜想离题了。 我不想争论它。 主要思想是您所观察的是预期的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.