CUDA是否为您自动实现负载平衡?

CUDA是否为您自动实现负载平衡?,cuda,load-balancing,gpgpu,Cuda,Load Balancing,Gpgpu,我希望得到一些关于CUDA C中负载平衡最佳实践的一般性建议和澄清,特别是: 如果一条经线中的一条线比另一条经线需要更长的时间,它会阻碍另一条经线的完成吗 如果是,是否将备用处理能力分配给另一个warp 为什么我们需要扭曲和阻塞的概念?在我看来,翘曲只是一小块32根线 一般来说,对于给定的内核调用,我需要什么样的负载平衡? 每个经纱中的线 每个块中的线程 所有块上的线程 最后,给出一个示例,您将为以下功能使用哪些负载平衡技术: 我有一个向量x0的N点:[1,2,3,…,N] 我随机选择5

我希望得到一些关于CUDA C中负载平衡最佳实践的一般性建议和澄清,特别是:

  • 如果一条经线中的一条线比另一条经线需要更长的时间,它会阻碍另一条经线的完成吗
  • 如果是,是否将备用处理能力分配给另一个warp
  • 为什么我们需要扭曲和阻塞的概念?在我看来,翘曲只是一小块32根线
  • 一般来说,对于给定的内核调用,我需要什么样的负载平衡?
    • 每个经纱中的线
    • 每个块中的线程
    • 所有块上的线程
最后,给出一个示例,您将为以下功能使用哪些负载平衡技术:

  • 我有一个向量
    x0
    N
    点:
    [1,2,3,…,N]
  • 我随机选择5%的点并记录它们(或一些复杂的函数)
  • 我将结果向量
    x1
    (例如
    [1,log(2),3,4,5,…,N]
    )写入内存
  • 我在
    x1
    上重复上述2个操作以产生
    x2
    (例如
    [1,log(log(2)),3,4,log(5),…,N]
    ),然后再进行8次迭代以产生
    x3
    <代码>x10
  • 我返回
    x10
  • 非常感谢

    如果一条经线中的一条线比另一条经线需要更长的时间,它会阻碍另一条经线的完成吗

    对。一旦扭曲中存在分歧,调度程序就需要获取所有分歧分支,并逐个处理它们。不在当前执行的分支中的线程的计算能力将丢失。您可以查看CUDA编程指南,它很好地解释了到底发生了什么

    如果是,是否将备用处理能力分配给另一个warp

    不,不幸的是,它完全丢失了

    为什么我们需要扭曲和阻塞的概念?在我看来,翘曲只是一小块32根线

    由于扭曲必须是SIMD(单指令多数据)才能实现最佳性能,因此块内的扭曲可以完全分散,但它们共享一些其他资源。(共享内存、寄存器等)

    一般来说,对于给定的内核调用,我需要什么样的负载平衡

    我认为负载平衡这个词在这里不合适。只要确保始终有足够多的线程在执行,并避免扭曲内部的分歧。同样,《CUDA编程指南》也是一本很好的读物

    现在举个例子:

    您可以执行m个m=0..N*0.05的线程,每个线程选取一个随机数,并将“复杂函数”的结果放入x1[m]。
    然而,使用GPU从全局内存中随机读取大面积数据并不是最有效的,因此您还应该考虑是否真的需要完全随机。

    线程分为三个不同的级别。Warps利用SIMD实现更高的计算密度。线程块利用多线程实现延迟容忍。网格为跨SMs的负载平衡提供独立的粗粒度工作单元

    经线 硬件一起执行扭曲的32个线程。它可以使用不同的数据执行单个指令的32个实例。如果线程采用不同的控制流,因此它们并非都在执行同一条指令,那么在指令执行时,这32个执行资源中的一些将处于空闲状态。这在CUDA引用中称为控制分歧

    如果内核表现出大量的控制差异,那么在这个级别重新分配工作可能是值得的。这通过使所有执行资源在一个扭曲中保持忙碌来平衡工作。您可以在线程之间重新分配工作,如下所示

    // Identify which data should be processed
    if (should_do_work(threadIdx.x)) {
      int tmp_index = atomicAdd(&tmp_counter, 1); 
      tmp[tmp_index] = threadIdx.x;
    }
    __syncthreads();
    
    // Assign that work to the first threads in the block
    if (threadIdx.x < tmp_counter) {
      int thread_index = tmp[threadIdx.x];
      do_work(thread_index); // Thread threadIdx.x does work on behalf of thread tmp[threadIdx.x]
    }
    
    //确定应该处理哪些数据
    如果(应该做什么工作(threadIdx.x)){
    int tmp_索引=原子添加(&tmp_计数器,1);
    tmp[tmp_index]=threadIdx.x;
    }
    __同步线程();
    //将该工作分配给块中的第一个线程
    if(螺纹IDX.x
    块中的翘曲 在SM上,硬件将计划扭曲到执行单元上。有些指令需要一段时间才能完成,因此调度程序会交错执行多个扭曲,以使执行单元保持忙碌。如果某些扭曲尚未准备好执行,将跳过它们,而不会造成性能损失

    在这个级别上通常不需要负载平衡。只需确保每个线程块有足够的扭曲可用,以便调度器始终可以找到准备好执行的扭曲

    网格中的块 运行时系统将块调度到SMs上。一个SM上可以同时运行多个块


    在这个级别上通常不需要负载平衡。只需确保有足够的线程块可用于多次填充所有SMs。当一些SMs处于空闲状态且没有更多的线程块准备执行时,过度提供线程块以最小化内核末尾的负载不平衡是很有用的。

    其他人为理论问题提供了很好的答案

    对于您的示例,您可以考虑将问题重组如下:

  • 有一个向量
    x
    N
    点:
    [1,2,3,…,N]
  • x
    的每个元素上计算一些复杂的函数,得到
    y
  • 随机抽取
    y
    的子集,通过
    y10
    生成
    y0
  • 步骤2对每个输入元素只操作一次,而不考虑是否需要该值。如果第3步的采样是在没有替换的情况下完成的,这意味着您将计算实际需要的元素数的2倍,但您将在没有控制div的情况下计算所有元素
    I have a vector x0 of N points: [1, 2, 3, ..., N]
    I randomly pick 50% of the points and log them (or some complicated function) (1)
    I write the resulting vector x1 to memory (2)
    I repeat the above 2 operations on x1 to yield x2, and then do a further 8 iterations to  yield x3 ... x10 (3)
    I return x10 (4)