CUDA线程是如何工作的

CUDA线程是如何工作的,cuda,Cuda,我对线程的形成和执行方式有很多疑问 首先,文档将GPU线程描述为轻量级线程。 假设我想将两个100*100矩阵相乘。如果每个元素由不同的线程计算,则需要100*100线程。但是,我的GPU(NVIDIA GT 640M LE)规范显示了两个SM,每个SM只能支持2048个线程。既然我的GPU不能支持这么多线程,那么如何计算其余的元素并行y呢 还考虑基本向量添加代码。假设我调用一个包含1个块和64个线程的内核来添加两个数组,每个数组包含100个元素,如下所示: __global__ voi

我对线程的形成和执行方式有很多疑问

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

还考虑基本向量添加代码。假设我调用一个包含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];
        {    
     }
\uuuu全局\uuuuu无效添加(int*a、int*b、int*c)
{
int i=threadIdx.x;

有关(i您的卡具有计算能力
3.0
,请参阅

从CUDA C编程指南的表12中,您提到的计算能力的
2048
线程数指的是每个多处理器的最大驻留线程数。这并不意味着您总共只能启动
2048
个线程。例如,从该表上面的几行可以ead指出,线程块网格的最大
x
-尺寸为
2^31-1
。这意味着启动
1d
线程网格(例如
8192
线程)是完全合法的。原因是该卡将在线程扭曲之间执行上下文切换,如本文所述:

关于问题的第二部分,您对
add
函数的实现在概念上是错误的。您将索引
i
同时用作线程索引和
循环索引。更正确的实现如下

__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];
现在,例如,对于thread
#3
来说,
threadIdx.x
变量的值将是
3
。因此,thread
#3
将处理一个局部变量
i
,它的值将分配给
3
。此外,它将加载
a[3]
b[3]
从全局内存中,将它们相加,将结果分配给
c[3]
,然后将最终结果存储到全局内存。因此,当启动网格时,当然不能仅通过
64
线程填充整个
100
元素数组,您将需要
100
线程


请注意,上面的解释过于简单。我建议您通过示例阅读一些基础教科书,如著名的CUDA。

将为您演示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];
}
我们需要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网格(1,1,1)
,即网格只有一个块 块配置将为
dim3块(16,1,1)
,即块将有16条螺纹按列排列

以下程序将为您提供有关其执行的清晰信息。。
理解索引部分(即threadid、blockDim、blockID)是重要的一部分。你需要阅读CUDA文献。一旦你对索引有了明确的认识,你将赢得一半的胜利!所以花些时间阅读CUDA书籍…:-)

因为这里是非常错误的-一些threadid<100的线程会提前运行。 对于新手来说,它可以这样解释:threadid是由系统值预定义的,显示当前线程号的内容。当前线程从a、b中获取其值,并将其写入c中,因此

int i = threadIdx.x;
c[i] = a[i] + b[i];
如果数组大小为100,则与64x的块大小不匹配, 为了使某些线程不会读/写越界,请执行以下操作:

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

        c[i] = a[i] + b[i];
    }
inti=threadIdx.x;
如果(i<100){
c[i]=a[i]+b[i];
}
只有在最后一个块上才有散度。
可能你想要这个

谢谢你的回答。我理解了关于启动大量线程的部分。接下来的第二部分,我仍然不知道如何添加剩余的元素。由于只启动了64个线程,所以块上只存在两个扭曲。添加完64个元素后,是否会添加剩余的元素?如果是,那么哪些线索?@mastercheif141我试图更好地解释你的第二个问题。请参阅编辑后的答案。
int i = threadIdx.x;
    if(i < 100){

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