GPU和CPU并发:生产者消费者有界缓冲区

3
考虑以下问题:
您拥有一个只有一个GPU和一个CPU的计算环境。在GPU上,您运行一个程序,对1e6浮点数数组进行计算。这个计算步骤重复n次(进程1)。每次计算步骤完成后,我将数组从设备内存传输到主机内存。传输完成后,使用串行算法在CPU上分析数据(进程2)。
该程序以串行方式工作。我想知道如何并行化进程1和进程2,以减少总程序运行时间。必须让进程1等待进程2完成,反之亦然。
我知道CUDA内核是异步调用的,也知道有带固定主机内存的异步复制操作。但是,在这种情况下,我需要等待GPU完成后,CPU才能开始处理输出。如何传递这些信息?
我尝试修改了多线程CPU生产者/消费者代码,但没有成功。最终,我将管理GPU和CPU工作负载的两个CPU线程串行化了。然而,在这里,我的GPU需要等待CPU完成才能继续进行......
#include <mutex>
#include <condition_variable>

#include "ProducerConsumerBuffer.hpp"

ProducerConsumerBuffer::ProducerConsumerBuffer(int capacity_in, int n): capacity(capacity_in), count(0) {
    c_bridge = new float[n];
    c_CPU = new float[n];
}

ProducerConsumerBuffer::~ProducerConsumerBuffer(){
    delete[] c_bridge;
    delete[] c_CPU;
}

void ProducerConsumerBuffer::upload(device_pointers *d, params &p, streams *s){
    std::unique_lock<std::mutex> l(lock);

    not_full.wait(l, [this](){return count != 1; });

    copy_GPU_to_CPU(d,c_bridge,p,s);
    count++;

    not_empty.notify_one();
}



void ProducerConsumerBuffer::fetch(){
    std::unique_lock<std::mutex> l(lock);

    not_empty.wait(l, [this](){return count != 0; });

    std::swap(c_bridge,c_CPU);
    count--;

    not_full.notify_one();

}

我希望通过 cudastreams 来实现同步。但我认为它们只适用于设备函数调用。我需要使用 MPI 吗,还是有其他选项来在异构计算平台上同步进程?我读到 OpenCL 支持此操作,因为所有计算设备都组织在一个“上下文”中。是否不可能使用 CUDA 实现相同的操作?
假如我的序列化 CPU 操作比 GPU 操作长 4 倍,我打算创建 4 个 CPU 消费者。
非常感谢您的任何见解!
编辑:CPU 函数包含无法并行化的串行代码。

只是一个小提示:为什么你需要在主机上进行“数据分析”?如果你可以在设备上执行它,根据这个分析步骤的输出,你可以节省内存带宽... - m.s.
2
要获取CPU和GPU之间的设备并发性,通常的惯用语是双缓冲:让CPU和GPU在两个不同的缓冲区上运行,然后在两个设备完成时切换缓冲区的状态。你所描述的工作量似乎需要4个缓冲区和4个CPU线程来处理CPU。目标是使每个设备(CPU和GPU)花费相等的时间进行处理,否则一个设备会浪费等待时间。这里的可分页memcpy样例应该有所帮助。https://github.com/ArchaeaSoftware/cudahandbook/tree/master/concurrency - ArchaeaSoftware
@oscillon,请确保标记一个答案并点赞,以便我们可以关闭它。 - Jason Newton
1个回答

0

如果没有使用多个线程或进程,或者显着增加CPU算法以实现可接受的调度延迟,那么无法做到您想要的。这是因为您必须能够以低延迟的正确频率命令GPU处理您拥有的GPU工作负载数据,但CPU工作负载并不小,必须考虑在循环运行时间中。

因此,为确保CPU和GPU都持续处理并实现最高吞吐量和最低延迟,您必须将GPU命令部分和昂贵的CPU计算部分分成不同的线程 - 在两者之间是某种IPC - 最好是共享内存。如果专用CPU处理线程与CUDA类似地进行工作,并在跨线程使用其cudaEvent_t,则可以简化某些任务,同时使GPU命令线程也能命令CPU线程 - 即1个命令线程和2个处理从属(GPU,CPU)。


听起来MPI支持CUDA是正确的选择,对吗? - Oscillon
我并不经常使用MPI,因此无法对该特定策略发表评论,除了数据块的消息传递不适用于大块数据,而且从我对它们的天真认识来看,大多数MPI都是基于套接字的。我会自己编写线程/服务器,但如果MPI/cuda适合您并且您习惯使用它,请继续使用 - 只有在出现新瓶颈时才会出现问题。 - Jason Newton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接