在CUDA内核启动后,线程块调度对特定SM的行为是什么?

时间:2015-05-20 22:28:17

标签: cuda

我的问题是在内核执行已经开始后,在CUDA(特别是kepler或更新的nvidia架构)中调度线程块。

根据我对kepler体系结构(可能不正确)的理解,可以在任何时刻调度到单个SM的活动块数量有限(如果我正确记住,则为16个块)。同样根据我的理解,一旦计划在特定SM上运行,块就无法移动。

我很好奇的是在初始选择块并且已经开始在设备上执行之后的块调度和执行行为(假设内核具有比在所有SM中可以活动的更多的线程块)。

当一个当前运行的活动块在SM中完成时,是否会执行新块?或者,只有在SM完成所有当前活动的块之后才执行下一组块?或者只有在所有SM完成所有当前活动的块执行后才启动它们?

此外,我听说块调度已经修复了#34;单个SM。我假设它只在块变为活动状态后固定到单个SM。是这样的吗?

1 个答案:

答案 0 :(得分:4)

只要SM有足够的未使用资源来支持新块,就可以安排新块。在安排新的块之前,SM不必完全耗尽块。

正如评论中所指出的,如果您现在要求公开文档来支持这一断言,我不确定我是否可以指出它。但是,可以创建一个测试用例并向自己证明这一点。

简而言之,您将创建一个可以启动多个块的块专用内核。每个SM上的第一个块将使用原子发现并声明自己。这些块将“持续”直到所有其他块完成,使用块完成的计数器(再次,使用原子,类似于threadfence减少示例代码)。所有其他不是第一个在给定SM上启动的块都会退出。完成这样的代码,而不是挂起,将证明即使某些块仍然驻留,也可以安排其他块。

这是一个完整的例子:

$ cat t743.cu
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define NB 1000
// increase array length here if your GPU has more than 32 SMs
#define MAX_SM 32
// set HANG_TEST to 1 to demonstrate a hang for test purposes
#define HANG_TEST 0

#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

static __device__ __inline__ uint32_t __smid(){
    uint32_t smid;
    asm volatile("mov.u32 %0, %%smid;" : "=r"(smid));
    return smid;}

__device__ volatile int blocks_completed = 0;
// increase array length here if your GPU has more than 32 SMs
__device__ int first_SM[MAX_SM];

// launch with one thread per block only
__global__ void tkernel(int num_blocks, int num_SMs){

  int my_SM = __smid();
  int im_not_first = atomicCAS(first_SM+my_SM, 0, 1);
  if (!im_not_first){
    while (blocks_completed < (num_blocks-num_SMs+HANG_TEST));
  }
  atomicAdd((int *)&blocks_completed, 1);
}

int main(int argc, char *argv[]){
  unsigned my_dev = 0;
  if (argc > 1) my_dev = atoi(argv[1]);
  cudaSetDevice(my_dev);
  cudaCheckErrors("invalid CUDA device");
  int tot_SM = 0;
  cudaDeviceGetAttribute(&tot_SM, cudaDevAttrMultiProcessorCount, my_dev);
  cudaCheckErrors("CUDA error");
  if (tot_SM > MAX_SM) {printf("program configuration error\n"); return 1;}
  printf("running on device %d, with %d SMs\n", my_dev, tot_SM);
  int temp[MAX_SM];
  for (int i = 0; i < MAX_SM; i++) temp[i] = 0;
  cudaMemcpyToSymbol(first_SM, temp, MAX_SM*sizeof(int));
  cudaCheckErrors("cudaMemcpyToSymbol fail");
  tkernel<<<NB, 1>>>(NB, tot_SM);
  cudaDeviceSynchronize();
  cudaCheckErrors("kernel error");
}

$ nvcc -o t743 t743.cu
$ ./t743 0
running on device 0, with 15 SMs
$ ./t743 1
running on device 1, with 1 SMs
$ ./t743 2

我已经使用CUDA 7在K40c,C2075和Quadro NVS 310 GPU上测试了上述代码。它没有挂起。

要回答您的第二个问题,首先在其上安排SM的块上 {{em> remains。一个可能exception就是CUDA动态并行性。