我创建了一个简单的CUDA应用程序来添加两个矩阵,编译通过。我想知道所有线程将如何启动内核以及CUDA内部的流程是什么?我的意思是每个线程将以什么方式执行矩阵的每个元素。
我知道这是非常基础的概念,但我不了解这个。我对流程感到困惑。
我创建了一个简单的CUDA应用程序来添加两个矩阵,编译通过。我想知道所有线程将如何启动内核以及CUDA内部的流程是什么?我的意思是每个线程将以什么方式执行矩阵的每个元素。
我知道这是非常基础的概念,但我不了解这个。我对流程感到困惑。
您启动了一个块的网格。
块被不可分割地分配给多处理器(在多处理器上的块数确定可用共享内存的数量)。
块进一步分为线程束。对于Fermi GPU,它是32个线程,要么执行相同的指令,要么处于非活动状态(因为它们通过提前退出循环或不执行其他线程束中的邻居执行的if
等方式进行了分支)。在Fermi GPU上,最多有两个线程束在一个多处理器上运行。
每当存在延迟(即执行由于内存访问或数据依赖性需等待完成而导致的停顿)时,就会运行另一个线程束(相同或不同块的线程束数由每个线程使用的寄存器数和一个/多个块使用的共享内存量确定)。
这种调度是透明的。也就是说,您不必过多考虑它。但是,您可能希望使用预定义的整数向量threadIdx
(我的线程在块中的位置?),blockDim
(一个块的大小是多少?),blockIdx
(我的块在网格中的位置?)以及gridDim
(网格的大小是多少?)将工作(读取:输入和输出)分配给线程。您可能还希望了解如何有效地访问不同类型的内存(以便可以在单个事务中服务多个线程)。但这已经偏离了主题。
NSight提供了一个图形化调试器,一旦您克服了术语障碍,就能很好地了解设备上正在发生的情况。对于那些您在调试器中看不到的事情(例如停顿原因或内存压力),其分析器也是如此。
您可以通过另一个内核启动来同步网格中的所有线程(所有线程)。 对于非重叠的连续内核执行,无需进一步同步。
一个网格(或一个内核运行 - 无论您称之为何)中的线程可以通过使用原子操作(用于算术)或适当的内存栅栏(用于加载或存储访问)来使用全局内存进行通信。
您可以使用内置指令__syncthreads()
同步一个block内的所有线程(所有线程之后都将处于活动状态——但是,在Fermi GPU上最多只能运行两个warp)。一个block中的线程可以通过原子操作(用于算术运算)或适当的内存栅栏(用于加载或存储访问)来使用共享或全局内存进行通信。让我们以4 * 4矩阵相加为例。您有两个矩阵A和B,维数均为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 =4; //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 | 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|
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)
dim3 Grid(1,1,1)
,即网格只有一个块,
而块配置将是dim3 block(16,1,1)
,即块将按列排列16个线程。