无数据传输延迟的GPU(CUDA)非线性优化

无数据传输延迟的GPU(CUDA)非线性优化,cuda,mathematical-optimization,Cuda,Mathematical Optimization,我正在尝试完全在GPU上执行非线性优化问题。目标函数的计算和从GPU到CPU的数据传输是瓶颈。为了解决这个问题,我想 将目标和目标的计算高度并行化 在GPU上执行整个优化 更具体地说,伪代码中的问题如下: x = x0 // initial guess of the vector of unknowns, typically of size ~10,000 for iteration = 1 : max_iter D = compute_search_direction(x)

我正在尝试完全在GPU上执行非线性优化问题。目标函数的计算和从GPU到CPU的数据传输是瓶颈。为了解决这个问题,我想

  • 将目标和目标的计算高度并行化
  • 在GPU上执行整个优化
  • 更具体地说,伪代码中的问题如下:

    x = x0  // initial guess of the vector of unknowns, typically of size ~10,000
    for iteration = 1 : max_iter
          D = compute_search_direction(x)
          alpha = compute_step_along_direction(x)
          x = x   +   D * alpha  // update
    end for loop
    
    函数
    compute\u search\u direction(x)
    compute\u step\u from\u direction(x)
    每次迭代都调用目标函数
    f0(x)
    几十次。目标函数是一个复杂的CUDA核,基本上是一个正向布洛赫模拟(=描述磁场中核自旋动力学的方程组)。
    f0(x)
    的输出是F(目标函数的值,标量)和DF(雅可比矩阵,或一阶导数向量,与x大小相同,即~10000)。在GPU上,
    f0(x)
    速度非常快,但将x从CPU传输到GPU,然后将F和DF从GPU传输回CPU需要一段时间(总共约1秒)。因为每次迭代都要调用函数几十次,这会导致非常缓慢的整体优化

    理想情况下,我希望上面的整个伪代码都在GPU上。我现在能想到的唯一解决方案是递归内核。上面的伪代码是“外部内核”,启动时线程数=1,块数=1(即,此内核不是真正的并行内核…)。然后,每当需要计算目标函数和一阶导数向量时,该核就会调用目标函数(即“内部核”,这是一个大规模并行的核)。由于内核启动是异步的,我可以强制GPU等待,
    f0
    内部内核被完全评估,以移动到外部内核的下一条指令(使用同步点)

    从某种意义上说,这实际上与常规CUDA编程相同,CPU控制内核启动以评估目标函数
    f0
    ,但CPU被一个不并行的外部内核(1个线程,1个块)替换。但是,由于所有东西都在GPU上,因此不再有数据传输延迟

    我现在用一个简单的例子来测试这个想法的可行性。然而,这似乎相当麻烦。。。我的问题是:

  • 这对其他人有意义吗
  • 有没有更直接的方法可以在不增加嵌套内核复杂性的情况下实现相同的结果

  • 看起来你把“减少GPU和CPU之间的内存传输”和“让整个代码在设备上运行(又名GPU上)”混为一谈了

    为了减少内存传输,您不需要在GPU上运行整个代码

    您可以将数据复制到GPU一次,然后在GPU代码和CPU代码之间来回切换。只要您不尝试从CPU代码访问任何GPU内存(反之亦然),您就可以了

    下面是一个正确方法的伪代码,用于您想要做的事情

    // CPU code
    cudaMalloc(&x,...) //allocate memory for x on GPU
    cudaMemCpy(x, x0, size, cudaMemCpyHostToDevice); //Copy x0 to the freshly allocated array 
    cudaMalloc(&D, ....)    //allocate D and alpha before the loop
    cudaMalloc(&alpha, ....)
    for iteration = 1 : max_iter
          compute_search_direction<<<...>>>(x, D) //Call a kernel that does the computation and stores the result in D
          compute_step_along_direction<<<....>>>(x, alpha)
          combine_result<<<...>>>(x, D, alpha)  // x   +   D * alpha
    end for loop
    //Eventually copy x on CPU, if need be
    
    //CPU代码
    cudamaloc(&x,…)//在GPU上为x分配内存
    cudaMemCpy(x,x0,size,cudaMemCpyHostToDevice)//将x0复制到新分配的阵列
    cudamaloc(&D,…)//在循环之前分配D和alpha
    Cudamaloc(&alpha,…)
    对于迭代=1:max\u iter
    compute\u search\u direction(x,D)//调用执行计算并将结果存储在D中的内核
    沿_方向(x,alpha)计算_阶跃_
    合并结果(x,D,alpha)//x+D*alpha
    循环结束
    //如果需要,最终在CPU上复制x
    

    希望有帮助

    我想到的另一个选择是与网格范围同步的合作发布。我的答案是Q1。不可以。您可能可以用python编写外部循环,它是串行的,每个计算都在GPU上完成,并获得相同的性能。内核有机会并发运行的唯一方法是将它们放在单独的流中。如果它们都发布到同一个流中(或者全部发布到默认流中),那么它们就不可能并发运行。对!我不知道。我想我在代码中放了太多的同步器。谢谢,我将编辑我的回答是的,这基本上就是我在帖子中提到的:嵌套内核。。。由于内核
    compute\u search\u direction
    compute\u step\u沿\u direction
    多次调用目标函数kernel
    f0
    。这很好(动态并行),我只是想知道是否还有其他解决方案…@Bastien我将尝试以不同的方式解释。我应该提到的第一件事是:Cuda中不允许嵌套内核,因此您描述的实现不可能编译。我不知道“嵌套内核是不允许的”是什么意思。但在不深入这个答案的实质的情况下,我认为这与Talonmes的评论是一样的。不必使用动态并行来获得您在这里可能看到的几乎所有好处。最大的好处是在GPU上获取所有数据,然后根据需要随时调用内核。你在问题中展示的循环类似于共轭梯度下降中使用的循环,这里有一个CUDA示例代码。