假设您有一个相当典型的异步计算序列:
现在,如果您在循环中运行,主机将返回到步骤1,因为2和3对于主机是非阻塞的。
问题是:“如果主机再次执行第2步,而系统还没有完成向设备传输数据,那么主机会发生什么情况?”
如果是后者,假设如果主机运行得太快,这个缓冲区可能会耗尽空间,wrt设备操作吗?
我知道现代设备有多个复制引擎,但我不确定这些引擎对同一流上的多个副本是否有用,对同一个地方是否有用。
我明白,一个遇到这种情况的系统不会是一个设计良好的系统--把询问作为一个知识点。
这不是我在代码中遇到的东西--寻找关于这种行为应该如何工作的文档的任何指针。已经查看了复制函数和异步行为的API页面,没有看到任何我能够识别的相关内容。
发布于 2022-07-07 23:08:49
主机MemCpyAsync在上一份副本完成之前会阻塞吗?
不,通常不是,假设“块”是指阻塞CPU线程。发出给GPU的异步工作项进入队列,控制在工作开始之前立即返回到CPU线程。队列可以容纳“许多”项目。工作的发布可以一直进行,直到队列已满,而没有任何其他障碍或依赖。
记住流语义的两个规则之一是很重要的:
因此,假设有这样的情况(假设h_ibuff
和h_obuff
指向固定的主机内存):
cudaStream_t stream;
cudaStreamCreate(&stream);
for (int i = 0; i < frames; i++){
cudaMemcpyAsync(d_ibuff, h_ibuff, cudaMemcpyHostToDevice, stream);
kernel<<<...,stream>>>(...);
cudaMemcpyAsync(h_obuff, d_obuff, cudaMemcpyDeviceToHost, stream);
}
在循环的第二次传递中,cudaMemcpyAsync
操作将被插入到队列中,但在流语义表明它们可以开始之前,不会开始执行(或执行任何事情)。这个循环发出的每一个op都是这样的。
一个合理的问题可能是“如果在循环的每一次传递中,我都希望h_ibuff
中有不同的内容呢?”(相当明智)。那么你就需要具体地解决这个问题。例如,插入一个简单的memcpy
操作来“重新加载”h_ibuff
是行不通的。你需要某种同步。例如,当内核和后续的h_ibuff
D->H操作发生时,您可能会决定要“重新填充”cudaMemcpyAsync
。你可以这样做:
cudaStream_t stream;
cudaEvent_t event;
cudaEventCreat(&event);
cudaStreamCreate(&stream);
for (int i = 0; i < frames; i++){
cudaMemcpyAsync(d_ibuff, h_ibuff, cudaMemcpyHostToDevice, stream);
cudaEventRecord(event, stream);
kernel<<<...,stream>>>(...);
cudaMemcpyAsync(h_obuff, d_obuff, cudaMemcpyDeviceToHost, stream);
cudaEventSynchronize(event);
memcpy(h_ibuff, databuff+i*chunksize, chunksize); // "refill"
}
这种重构将允许发出异步工作,以保持GPU的忙碌,并将副本“重叠”为“填充”h_ibuff
。它还将防止“填充”操作从开始到以前的缓冲区内容安全地传输到设备,还将防止下一个缓冲区副本从开始一直到新内容被“重新加载”。
这不是唯一的方法;这是一种可能的方法。
对于上面问/回答的最后一个问题,您可能会问一个类似的问题:“如何处理输出缓冲区端?”这一机制可能非常相似,留给读者。
关于这个主题的结构化学习,您可能希望学习本讲座系列的CUDA并发部分。
https://stackoverflow.com/questions/72904943
复制相似问题