CUDA内核中的无限循环

时间:2011-11-21 00:20:46

标签: parallel-processing cuda

我有一个CUDA内核,每个线程遍历一棵树。因此,我有一个循环,循环直到线程到达叶子。在树的每一步中,它都会检查应该选择哪个孩子。

代码如下:

__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中的循环或者是否有一些我看不到的隐藏错误?因为我没有看到代码如何进入无限循环。

1 个答案:

答案 0 :(得分:4)

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等)提升到参数之外,并将其设为{{ 1}}功能:

__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,你可以通过从CPU调用来调试它,模仿内核启动会做什么:

__host__ __device__

它应该完全像在设备上一样挂在主机上。坚持for(int tid = 0; tid < featureCount; ++tid) { search(tid, centroids, features, featureCount, votes); } 在里面找出原因。当然,您需要确保制作数组的主机端副本,例如printf,因为主机无法取消引用指向设备内存的指针。

即使centroids可用于具有较新硬件的printf函数,您可能更喜欢这种方法的原因是从内核调用__device__直到 之后内核退休。如果内核永远不会退休(因为在你的情况下显然没有),那么你的调试输出将永远不会出现在屏幕上。