我有一个CUDA内核,其中每个线程遍历树。因此,我有一个while循环,直到线程到达一个叶子。在树的每一步中,它都会检查它应该选择跟随哪个子节点。
代码如下:
__global__ void search(float* centroids, float* features, int featureCount, int *votes)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
if(tid < featureCount)
{
int index = 0;
while (index < N)
{
votes[tid] = index;
int childIndex = index * CHILDREN + 1;
float minValue = FLT_MAX;
if(childIndex >= (N-CHILDREN)) break;
for(int i = 0; i < CHILDREN; i++)
{
int centroidIndex = childIndex + i;
float value = distance(centroids, features, centroidIndex, tid);
if(value < minValue)
{
minValue = value;
index = childIndex + i;
}
}
}
tid += blockDim.x * gridDim.x;
}
}
__device__ float distance(float* a, float* b, int aIndex, int bIndex)
{
float sum = 0.0f;
for(int i = 0; i < FEATURESIZE; i++)
{
float val = a[aIndex + i] - b[bIndex + i];
sum += val * val;
}
return sum;
}
这段代码进入一个无限循环。这就是我觉得奇怪的地方。如果我改变距离方法返回一个常数,它的工作(即。
我错过了一些循环在CUDA或只是有一些隐藏的bug我看不见?因为我不明白代码怎么能进入无限循环
CUDA c++中的循环与c++中的循环具有相同的语义,因此您的代码中一定存在某个错误。调试它的一种策略是在主机上进行调试。
首先,因为你的代码是标量的(例如,它不包含对__syncthreads
的调用),你可以将它重构成__host__ __device__
函数。
distance
不包含cuda特定的标识符或函数,因此您可以简单地在__host__
前面添加:
__host__ __device__ float distance(float* a, float* b, int aIndex, int bIndex);
要重构search
函数,将tid
(取决于cuda特定的标识符threadIndex
等)提升到它外面的一个参数中,并使其成为__host__ __device__
函数:
__host__ __device__ void search(int tid, float* centroids, float* features, int featureCount, int *votes)
{
if(tid < featureCount)
{
int index = 0;
while (index < N)
{
votes[tid] = index;
int childIndex = index * CHILDREN + 1;
float minValue = FLT_MAX;
if(childIndex >= (N-CHILDREN)) break;
for(int i = 0; i < CHILDREN; i++)
{
int centroidIndex = childIndex + i;
float value = distance(centroids, features, centroidIndex, tid);
if(value < minValue)
{
minValue = value;
index = childIndex + i;
}
}
}
}
}
现在写一个__global__
函数,它除了计算tid
和调用search
之外什么都不做:
__global__ void search_kernel(float *centroids, float features, int featureCount, int *votes)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
search(tid, centroids, features, featureCount, votes);
}
由于search
现在是__host__ __device__
,您可以通过从CPU调用它来调试它,模拟内核启动的操作:
for(int tid = 0; tid < featureCount; ++tid)
{
search(tid, centroids, features, featureCount, votes);
}
它应该像挂在设备上一样挂在主机上。在里面放一个printf
看看是哪里。当然,您需要确保在主机端复制您的数组,例如centroids
,因为主机不能解引用指向设备内存的指针。
尽管printf
可以在较新的硬件上从__device__
函数中使用,但您可能更喜欢这种方法的原因是,从内核调用printf
直到内核退役之后的才会提交。如果内核永远不会退出(就像你的情况一样),那么你的调试输出将永远不会出现在屏幕上。