Cuda统一了gpu和主机之间的内存

时间:2014-05-02 08:02:33

标签: c++ c cuda

我正在编写一个基于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向前推进它并继续。

如果有更好的方法来完成我想要做的事情,那也会有所帮助。

2 个答案:

答案 0 :(得分:4)

您正在描述生产者 - 消费者模型,其中GPU正在生成一些数据,并且CPU会不时地使用该数据。

实现此目的的最简单方法是让CPU成为主服务器。 CPU在GPU上启动内核,当它准备好准备使用数据时(例如你的while循环),它与GPU同步,从GPU复制数据,再次启动内核生成更多数据,并对其复制的数据执行任何操作。这允许您在CPU处理上一批时让GPU填充固定大小的缓冲区(因为有两个副本,一个在GPU上,一个在CPU上)。

通过对数据进行双缓冲可以改善这一点,这意味着当您将另一个数据复制到CPU时,可以通过缓冲区之间的乒乓来保持GPU在100%的时间内忙于生成数据。这假设复制比生产更快,但如果没有,那么你将使拷贝带宽饱和,这也很好。

这些都不是你实际描述的。您要求的目的是让GPU掌握数据。我要小心谨慎,因为您需要仔细管理缓冲区大小,并且需要仔细考虑时间和通信问题。当然可以做类似的事情,但在探索这个方向之前,你应该阅读有关内存栅栏,原子操作和volatile的信息。

答案 1 :(得分:2)

我尝试添加

__threadfence_system();

*h_i = 2*i + 1;

有关详细信息,请参阅here。没有它,修改完全有可能永远保留在GPU缓存中。不管怎么说,你最好听其他答案:为多线程/块改进它你必须处理其他问题&#34;让类似的计划可靠地运作。

正如汤姆建议的那样(+1),最好使用双缓冲。 Streams帮助了很多这样的方案,你可以找到描述here