如何同时在CPU和GPU设备上运行任务?

时间:2013-11-05 22:44:55

标签: c++ c++-amp

我有这段代码,这些代码具有分析,优化和缓存效率,因为我很可能通过我的知识水平获得它。它在概念上在CPU上运行如下:

#pragma omp parallel for schedule(dynamic)
  for (int i = 0; i < numberOfTasks; ++i)
  {
    result[i] = RunTask(i); // result is some array where I store the result of RunTask.
  }

恰好,RunTask()本质上是一组线性代数运算,每次都在相同的非常大的数据集上重复运行,因此适合在GPU上运行。所以我想实现以下目标:

  1. 将部分任务卸载到GPU
  2. 当GPU忙时,处理CPU上的其余任务
  3. 对于CPU级别的操作,请保留我的超级用户RunTask()功能,而不必修改它以符合restrict(amp)。我当然可以为GPU任务设计一个restrict(amp)兼容的lambda。
  4. 最初我想过做以下事情:

    // assume we know exactly how much time the GPU/CPU needs per task, and this is the 
    // most time-efficient combination:
    int numberOfTasks = 1000;
    int ampTasks = 800;
    
    // RunTasksAMP(start,end) sends a restrict(amp) kernel to the GPU, and stores the result in the
    // returned array_view on the GPU
    Concurrency::array_view<ResulType, 1> concurrencyResult = RunTasksAMP(0,ampTasks);
    
    // perform the rest of the tasks on the CPU while we wait
    #pragma omp parallel for schedule(dynamic)
      for (int i = ampTasks; i < numberOfTasks; ++i)
      {
        result[i] = RunTask(i); // this is a thread-safe
      }
    
    // do something to wait for the parallel_for_each in RunTasksAMP to finish.
    concurrencyResult.synchronize();
    //... now load the concurrencyResult array into the first elements of "result"
    

    但我怀疑你可以做这样的事情,因为

      

    对parallel_for_each的调用表现得好像是同步的

    http://msdn.microsoft.com/en-us/library/hh305254.aspx

    那么有可能实现我的1-3个请求,还是我必须放弃3号?即便如此,我将如何实施呢?

1 个答案:

答案 0 :(得分:4)

请参阅我对will array_view.synchronize_asynch wait for parallel_for_each completion?的回答,了解为什么parallel_for_each可以作为排队或调度操作而不是同步操作。这解释了为什么您的代码应满足您的要求1&amp; 2.它也应该满足要求3,尽管你可能想要考虑使用一个restrict(cpu, amp)函数,因为这样可以减少维护代码。

但是,您可能需要考虑您的方法的一些性能影响。

首先,parallel_for_each仅排队工作,来自主机和GPU内存的数据副本使用主机资源(假设您的GPU是离散的和/或不支持直接复制)。如果您在主机上的工作使保持GPU工作所需的所有资源饱和,那么您实际上可能会降低GPU计算速度。

其次,对于许多数据并行且易于在GPU上运行的计算,它们要快得多,以至于尝试在CPU上运行工作的额外开销不会导致整体加速。开销包括第一项(上图)和协调主机工作的额外开销(调度线程,合并结果等)。

最后,您的实现不会考虑在GPU和CPU上运行任务所花费的时间的任何变化。它假设800个AMP任务将花费200个cpu任务。在某些硬件上可能是这样,但在其他硬件上则不然。如果一组任务花费的时间超过预期,那么您的应用程序将阻塞并等待较慢的任务集完成。您可以使用主/工作模式来避免这种情况,从队列中提取任务,直到没有更多可用任务。这种方法意味着在最坏的情况下,您的应用程序必须等待最终任务完成,而不是一块任务。使用主/工作方法也意味着无论相对CPU / GPU性能如何,您的应用程序都将以相同的效率运行。

My book讨论了使用主/工作人员(n-body)和并行队列(cartoonizer)跨多个GPU调度工作的示例。您可以从CodePlex下载源代码。请注意,基于与C ++ AMP产品团队的讨论,出于上述原因故意不包括CPU和GPU上的共享工作。