我们有一个多GPU框架(在Windows上),其中可以指定“作业”(也指定将在哪些GPU上完成),然后在特定的GPU上执行。 目前,我们采用的方法是在框架的 startup 上为每个GPU创建一个“Worker-Thread”,然后等待处理作业。具体来说,我们使用https://devtalk.nvidia.com/search/more/sitecommentsearch/GPUworker/
中的“GPUWorker”类到目前为止它运作良好,但有一些与性能相关的严重缺点:
在我们的frameowrk中,特定GPU在“作业”的整个时间内都被锁定,即使GPU实际上仅在作业的50%时间内使用。注意,作业具有非常粗糙的粒度,例如'进行光流计算',可以采用例如50 - 100毫秒。
一个不能锁定GPU的特定“异步”作业(例如,一个不稳定的主机设备副本)
所以我现在正在考虑解决这个问题的“更好”策略。 我的想法如下:对于每个“已启动”的新作业,我创建一个新的“临时”CPU线程。然后,CPU线程设置要在其上完成工作的GPU的设备编号(通过'cudaSetDevice')。我想此时也是(对我来说是透明的)创建了Cuda上下文。在发布正确的设备之后,作业的'doWork'功能由CPU线程执行。取决于作业是同步还是异步, “加入”是否完成(等待CPU线程完成)。
我现在有几个问题:
这是一个“好”策略,还是有人知道如何处理这个问题的更好方法?当然,它必须是一个线程安全的策略。
在我提出的策略中,创建新CPU线程和Cuda上下文(隐藏)创建的典型开销(以毫秒为单位)是多少?此外,如果是Cuda上下文的创建是显而易见的,有没有办法(例如使用cuda设备api和某种'上下文迁移')来减少这种开销?
答案 0 :(得分:1)
你的第一种方法听起来比你正在考虑的方案更有希望。
创建CPU线程并初始化CUDA上下文非常昂贵,而且很难让您更快地完成该操作。 NVIDIA故意将大量操作预先加载到上下文创建过程中,因此您不会因资源分配失败而出现意外延迟或失败。
你最好的选择是投资异步。如果没有CPU / GPU并发性,您肯定会在表上保留性能,因为您没有隐藏CUDA驱动程序内置的CPU开销。