2014-02-12 62 views
3

我对线程的形成和执行方式有很多疑问。CUDA线程如何工作

首先,文档将GPU线程描述为轻量级线程。 假设我希望乘以两个100*100矩阵。如果每个元素都由不同的线程计算,这将需要100*100个线程。但是,我的GPU(NVIDIA GT 640M LE)规格显示两个SM,每个SM只能支持2048个线程。如果我的GPU不支持这么多的线程,那么如何计算其余元素并行呢?

另外考虑基本向量添加代码。假设我调用与1块和64个线程内核添加100个元件的两个阵列,每个阵列如下:

__global__ void add(int* a,int* b,int* c) 
    { 
     int i = threadIdx.x; 
     for(i<100) 
     { 
      c[i] = a[i] + b[i]; 
     {  
    } 

由于只有64个线程进行初始化我假定64个元件被并行加入。

  • 如何添加其余元素?
  • warp调度程序如何决定分配哪些线程来添加最后的36个元素?

我的主要问题是:

我不明白一个线程是如何知道哪些元素进行操作。

回答

6

您的卡具有计算能力3.0,请参阅here

从CUDA C编程指南的表12中可以看出,您为计算能力提及的2048线程的数量是指每个多处理器的最大驻留线程数。这并不意味着您无法在整体上启动超过2048的线程。例如,从该表的上方几行可以看出,线程块网格的最大最大值为2^31-1。这意味着,例如,启动8192线程的1d线程网格是完全合法的。原因是该卡将执行线程扭曲之间的上下文切换,如本文所示:What is the context switching mechanism in GPU?

关于你的问题的第二部分,你实现add函数在概念上是错误的。您正在使用索引i作为线索索引和作为for循环索引。更正确的实现是以下

__global__ void add(int* a,int* b,int* c) 
{ 
    int i = threadIdx.x; 
    c[i] = a[i] + b[i]; 
} 

上述写入装置执行以下操作:每个线程将执行两个任务,即现在

int i = threadIdx.x; 
    c[i] = a[i] + b[i]; 

,例如,用于螺纹#3threadIdx.x的值变量将是3。因此,线程#3将处理一个局部变量i,它的内存空间是私有的,其值将被分配给3。此外,它将从全局内存中加载a[3]b[3],将它们加起来,将结果分配给c[3],然后将最终结果存储到全局内存。因此,当您启动网格时,您当然不能仅用64线程填充整个100元素的数组,并且您将需要100线程。

请注意,以上解释过于简单。我建议你阅读一些基本的教科书作为着名的CUDA示例。

+0

感谢您的答案。我了解有关启动大量线程的部分。 – mastercheif141

+0

即将到来的第二部分我还没有得到如何添加其余的元素。因为只有64个线程被启动,所以在该块上仅存在两个warp。在添加了64个元素之后,将添加剩余的元素?如果是,那么通过哪些线程? – mastercheif141

+0

@ mastercheif141我试图更好地解释你的第二个问题。见编辑的答案。 – JackOLantern

1

会给你CUDA中4 * 4矩阵加法程序的插图。它可能会让你知道线程是如何启动和运行的。

int main() 
    { 
    int *a, *b, *c;   //To store your matrix A & B in RAM. Result will be stored in matrix C 
    int *ad, *bd, *cd;   // To store matrices into GPU's RAM. 
    int N =16; 

      //No of rows and columns. 

size_t size=sizeof(float)* N * N; 

a=(float*)malloc(size);  //Allocate space of RAM for matrix A 
b=(float*)malloc(size);  //Allocate space of RAM for matrix B 

//allocate memory on device 
    cudaMalloc(&ad,size); 
    cudaMalloc(&bd,size); 
    cudaMalloc(&cd,size); 

//initialize host memory with its own indices 
    for(i=0;i<N;i++) 
     { 
    for(j=0;j<N;j++) 
     { 
      a[i * N + j]=(float)(i * N + j); 
      b[i * N + j]= -(float)(i * N + j); 
     } 
     } 

//copy data from host memory to device memory 
    cudaMemcpy(ad, a, size, cudaMemcpyHostToDevice); 
    cudaMemcpy(bd, b, size, cudaMemcpyHostToDevice); 

//calculate execution configuration 
    dim3 grid (1, 1, 1); 
    dim3 block (16, 1, 1); 

//each block contains N * N threads, each thread calculates 1 data element 

    add_matrices<<<grid, block>>>(ad, bd, cd, N); 

    cudaMemcpy(c,cd,size,cudaMemcpyDeviceToHost); 
    printf("Matrix A was---\n"); 
    for(i=0;i<N;i++) 
    { 
     for(j=0;j<N;j++) 
      printf("%f ",a[i*N+j]); 
     printf("\n"); 
    } 

    printf("\nMatrix B was---\n"); 
    for(i=0;i<N;i++) 
    { 
     for(j=0;j<N;j++) 
      printf("%f ",b[i*N+j]); 
     printf("\n"); 
    } 

    printf("\nAddition of A and B gives C----\n"); 
    for(i=0;i<N;i++) 
    { 
     for(j=0;j<N;j++) 
      printf("%f ",c[i*N+j]); //if correctly evaluated, all values will be 0 
     printf("\n"); 
    } 



    //deallocate host and device memories 
    cudaFree(ad); 
    cudaFree(bd); 
    cudaFree (cd); 

    free(a); 
    free(b); 
    free(c); 

    getch(); 
    return 1; 
} 

/////Kernel Part 

__global__ void add_matrices(float *ad,float *bd,float *cd,int N) 
{ 
    int index; 
    index = blockIDx.x * blockDim.x + threadIDx.x    

    cd[index] = ad[index] + bd[index]; 
} 

让我们有两个矩阵A和B添加16×16点的矩阵.. 的一个例子,其尺寸16 * 16 ..

所有你必须首先确定你的线程配置。 假设您将启动一个内核函数,它将执行矩阵加法的并行计算,这将在您的GPU设备上执行。

现在,一个网格启动时只有一个内核函数。 一个网格最多可以有65,535个块,它们可以以三维方式排列。 (65535 * 65535 * 65535)。

在网格中的每个块可以有最高1024不threads.Those线程也可以安排在3点维的方式(1024 * 1024 * 64)

现在我们的问题是,除了16×16点的矩阵..

A | 1 2 3 4 |  B | 1 2 3 4 |  C| 1 2 3 4 | 
    | 5 6 7 8 | +  | 5 6 7 8 | = | 5 6 7 8 | 
    | 9 10 11 12 |   | 9 10 11 12 |  | 9 10 11 12 | 
    | 13 14 15 16|   | 13 14 15 16|  | 13 14 15 16| 

我们需要16个线程来执行计算。

i.e. A(1,1) + B (1,1) = C(1,1) 
    A(1,2) + B (1,2) = C(1,2) 
    .  .   . 
    .  .   . 
    A(4,4) + B (4,4) = C(4,4) 

所有这些线程将同时执行。 所以我们需要一个有16个线程的块。 为了方便起见,我们将在一个块中安排线程(16 * 1 * 1) 由于线程数不是16,所以我们只需要一个块来存储这16个线程。

这样,网格配置将是dim3 Grid(1,1,1)即网格将仅具有一个块 和嵌段构型将是dim3 block(16,1,1)即块将具有布置逐列16个线程。

以下程序会给你清晰的执行概念。 了解索引部分(即threadIDs,blockDim,blockID)是重要的部分。您需要阅读CUDA文献。一旦你对索引有了清晰的认识,你就会赢得一半的战斗。所以花一些时间与cuda书籍:-)

0

因为是非常错误在这里 - 一些线程与螺纹编号< 100将运行forewer。 对于新手来说,可以这样解释:threadid是由系统值预先定义的,显示当前线程号码。当前线程需要它从一个值,从B,用C写它,所以它会

int i = threadIdx.x; 
c[i] = a[i] + b[i]; 

如果你有数组大小魔神100什么不是为了某个线程匹配的64倍, 块大小不读/写超出范围,请执行以下操作:

int i = threadIdx.x; 
    if(i < 100){ 

     c[i] = a[i] + b[i]; 
    } 

只有在最后一个块上才有分歧。 也许你想