我有以下内核:
__global__ void myKernel(int k, int inc, int width, int* d_Xco, int* d_Xnum, bool*
Xvalid, float* d_X)
{
int i, k1;
i = threadIdx.x + blockIdx.x * blockDim.x;
//k1 = threadIdx.y + blockIdx.y * blockDim.y;
if( (i < k) ){
for(k1 = 0; k1 < inc; k1++){
int mul = (d_X[i*inc + k1] >= 2e2);
d_X[i*inc + k1] *= (float)(!mul);
d_Xco[i*width + k1] = k*mul;
d_Xnum[i] += mul;
d_Xvalid[i*inc + k1] = (!mul) ;
}
}// of if
}
以这种方式打电话:
int bx = (int)(k/32)+1;
int by = (int)(inc/32)+1;
dim3 b(bDim, 1);
dim3 t(tDim, 1);
cmyKernel<< b, t >>>( k, inc, width, d_Xco, d_Xnum, d_Xvalid, d_X );
cudaThreadSynchronize();
k
约为9000,inc
约为5000,所以我确信我没有超过块数。如果在myKernel
维度中使用1thread / 1block调用y
,则内核似乎工作正常,但是,仅将y
维度中的线程和块的数量更改为10,例如,它提供了错误的输出,即使在内核中我并没有真正使用y
中的线程和块。理想情况下,我想使用for()
k = threadIdx.y + blockIdx.y * blockDim.y
答案 0 :(得分:2)
如果启动y维度= 10的内核,则使用它们。仅仅因为您没有使用线程标识符threadIdx.y
和blockIdx.y
并不意味着线程未启动。当你启动y维度= 10的内核时,你将有10个线程,i = 0,10个线程,i = 1等。
假设你为了简单起见而推出2x2线程。你将有线程(0,0)(0,1)(1,0)(1,1)。在你的代码中,对于两个线程(0,0)和(0,1),i变量为0,但threadIdx.y
是不同的。这意味着两个线程将评估相同i变量的代码并导致竞争条件。
您需要解决迭代之间的依赖关系(d_Xnum [i] + = mul)。一种方法是使用atomicAdd(..)
。取消注释k1
,将循环替换为if(k1 < inc)
并添加atomicAdd
。这应该会给你正确的行为。
答案 1 :(得分:1)
正如评论中已经说明的那样,您当前的解决方案是启动多个线程,每个线程将其工作应用于相同的内存空间。这是由于生成的几个线程都具有相同值的threadIdx.x,而threadIdx.y值不同。这意味着你将有几个线程同时读取和写入同一个内存空间,这有很多潜在的问题,here is a brief description。
为避免这种情况,您可以采取以下几个步骤。例如,您可以使用同步数据访问(这将导致大量减速,因为线程等待其他人完成数据访问)。如果你想让每个线程处理一个单元格元素,你需要删除for循环,而不是像以前一样使用k1,但是你必须仔细考虑内存读写,因为进程的任何部分都可以在任何之前或之后执行其他在另一个线程!
这里的核心理解是你永远不能依赖线程之间的操作顺序!
在数据访问方面,有助于将所有数据结构视为网格,其中每个线程只应访问和修改其自身坐标中的数据,例如(3,2)对于具有threadIdx.x的线程== 3和threadIdx.y == 2.这样,您可以轻松地可视化线程的行为和潜在的竞争条件。最简单的方法是为输出数据的每个元素创建一个网格条目,因此如果你有一个9000x5000元素的矩阵,你可能会产生一定数量的线程,并从那里开始优化。这当然会导致GPU必须多次在所有单元上执行,但这是一个很好的起点。
奥斯陆大学有一个关于这个主题的研究生课程,其中包括。您可能会发现these slides与您的理解密切相关。请参阅有关线程批处理,网格和块的部分。