我正在编写一个基于cuda的程序,需要定期将一组项目从GPU传输到主机内存。为了使进程保持异步,我希望使用cuda的UMA在主机内存中有一个内存缓冲区和标志(因此GPU和CPU都可以访问它)。 GPU将确保标志清除,将其项添加到缓冲区,并设置标志。 CPU等待设置标志,从缓冲区中复制内容,并清除标志。据我所知,这并没有产生任何竞争条件,因为它迫使GPU和CPU轮流,总是读取并写入彼此相对的旗帜。
到目前为止,我还没有能够让它发挥作用,因为似乎确实存在某种竞争条件。我想出了一个类似问题的简单示例:
#include <stdio.h>
__global__
void uva_counting_test(int n, int *h_i);
int main() {
int *h_i;
int n;
cudaMallocHost(&h_i, sizeof(int));
*h_i = 0;
n = 2;
uva_counting_test<<<1, 1>>>(n, h_i);
//even numbers
for(int i = 1; i <= n; ++i) {
//wait for a change to odd from gpu
while(*h_i == (2*(i - 1)));
printf("host h_i: %d\n", *h_i);
*h_i = 2*i;
}
return 0;
}
__global__
void uva_counting_test(int n, int *h_i) {
//odd numbers
for(int i = 0; i < n; ++i) {
//wait for a change to even from host
while(*h_i == (2*(i - 1) + 1));
*h_i = 2*i + 1;
}
}
对我来说,这种情况总是在CPU(host h_i: 1
)的第一个打印语句后挂起。非常不寻常的事情(这可能是一个线索)是我可以让它在cuda-gdb中工作。如果我在cuda-gdb中运行它,它将像以前一样挂起。如果按ctrl + C,它会将我带到内核中的while()循环行。从那里,令人惊讶的是,我可以告诉它继续,它将完成。对于n> 2,它会在每个内核之后再次冻结内核中的while()循环,但是我可以继续用ctrl + C向前推进它并继续。
如果有更好的方法来完成我想要做的事情,那也会有所帮助。
答案 0 :(得分:4)
您正在描述生产者 - 消费者模型,其中GPU正在生成一些数据,并且CPU会不时地使用该数据。
实现此目的的最简单方法是让CPU成为主服务器。 CPU在GPU上启动内核,当它准备好准备使用数据时(例如你的while
循环),它与GPU同步,从GPU复制数据,再次启动内核生成更多数据,并对其复制的数据执行任何操作。这允许您在CPU处理上一批时让GPU填充固定大小的缓冲区(因为有两个副本,一个在GPU上,一个在CPU上)。
通过对数据进行双缓冲可以改善这一点,这意味着当您将另一个数据复制到CPU时,可以通过缓冲区之间的乒乓来保持GPU在100%的时间内忙于生成数据。这假设复制比生产更快,但如果没有,那么你将使拷贝带宽饱和,这也很好。
这些都不是你实际描述的。您要求的目的是让GPU掌握数据。我要小心谨慎,因为您需要仔细管理缓冲区大小,并且需要仔细考虑时间和通信问题。当然可以做类似的事情,但在探索这个方向之前,你应该阅读有关内存栅栏,原子操作和volatile
的信息。
答案 1 :(得分:2)