我对线程的形成和执行方式有很多怀疑。
首先,文档将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个元素。
我的主要问题是:
我不明白线程如何知道要操作哪些元素。
答案 0 :(得分:6)
您的卡具有计算功能3.0
,请参阅here。
从“CUDA C编程指南”的表12中,您提到的计算功能的2048
个线程数是指每个多处理器的最大驻留线程数。这并不意味着您无法在整体上启动超过2048
个线程。例如,从该表的上面几行中,您可以读到线程块网格的最大最大x
维度为2^31-1
。这意味着启动它是完全合法的,例如,1d
线程网格,例如8192
个线程。原因是该卡将执行线程扭曲之间的上下文切换,如本文所示: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];
现在,例如,对于线程#3
,threadIdx.x
变量的值将为3
。因此,线程#3
将处理对其内存空间私有的局部变量i
,其值将分配给3
。此外,它将从全局内存加载a[3]
和b[3]
,将它们相加,将结果分配给c[3]
,然后将最终结果存储到全局内存中。因此,当您启动网格时,您当然不能仅通过100
个线程填充整个64
元素数组,并且您将需要100
个线程。
请注意,上述说明过于简单。我建议你阅读一些基本的教科书作为着名的CUDA示例。
答案 1 :(得分: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];
}
让我们举一个16 * 16矩阵的例子。 你有两个矩阵A和B,尺寸为16 * 16 ..
首先,你必须决定你的线程配置。 您可以启动一个内核函数,它将执行矩阵加法的并行计算,这将在您的GPU设备上执行。
现在,使用一个内核函数启动一个网格。 网格最多可以有65,535个块,这些块可以以三维方式排列。 (65535 * 65535 * 65535)。
网格中的每个块最多可以有1024个线程。这些线程也可以以三维方式排列(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书籍......: - )
答案 2 :(得分:0)
for在这里是非常错误的 - 一些线程与threadid&lt; 100将运行forewer。 对于新手来说,它可以用这种方式解释: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];
}
你只会在最后一个街区出现分歧。 可能你想要那个