关于CUDA的BFS实施问题

时间:2015-12-16 18:12:36

标签: c cuda

我使用CUDA 7.5运行并行BFS的以下算法。该函数接受一个边数组和一个顶点数组。边缘定义为

typedef struct Edge
{
    int first;
    int second;

}Edge;

对于除起始顶点之外的所有顶点,顶点数组初始化为-1。因此我们有类似

的内容
 1 2 3 4 5 6.....1024
-1-1-1-1 0-1.....-1

在这种情况下,起始顶点为4 假设一个稀疏图形,边缘列表将以下列方式获得数据(数据将在边缘内容中,但我提供了整数表示)

1 2  3  4 5 .......2048
3 17 12 1 3........2010
9 34 20 9 17.......196

BFS应该与2048个线程平行运行,任何线程都有0作为第一个/第二个索引写入顶点数组1中的相关索引,并将bool修改为1.这是BFS代码。

__global__ void bfs(Edge* edges, int* vertices, int current_depth, int* modified){

    int e = blockDim.x * blockIdx.x + threadIdx.x;
    int vfirst = edges[e].first;
    if (vfirst > 1023) {printf("oops %d:%d\n", e, vfirst); return;}
    int dfirst = vertices[vfirst];
    int vsecond = edges[e].second;
    if (vsecond > 1023) {printf("oops %d:%d\n", e, vsecond); return;}
    int dsecond = vertices[vsecond];

    if((dfirst == current_depth) && (dsecond == -1)){
        vertices[vsecond] = current_depth;
        printf("e:%d  depth:%d\n", e, current_depth);
        __syncthreads();
        *modified = 1;
        printf("%d\n", *modified);
    }else if((dsecond == current_depth) && (dfirst == -1)){
        vertices[vfirst] = current_depth;
        printf("e:%d depth:%d\n", e, current_depth);
        __syncthreads();
        *modified = 1;
        printf("%d\n", *modified);
    }
}

每次调用时,主代码会重复调用此BFS内核,以增加当前深度的值。这是调用代码的相关部分。

begin = clock();

    do{

        h_modified = 0;
        //printf("Entered while loop\n");
        err = cudaMemcpy(d_modified, &h_modified, sizeof(int), cudaMemcpyHostToDevice);
        if (err != cudaSuccess)
        {
            fprintf(stderr, "Failed to copy h_done to device(error code %s)!\n", cudaGetErrorString(err));
            exit(EXIT_FAILURE);
        }

        printf("CUDA kernel launching with %d blocks of %d threads\n", edgeBlocks, threadsPerBlock);

        bfs<<<edgeBlocks, threadsPerBlock>>>(d_edges, d_vertices, current_depth, d_modified);
        cudaThreadSynchronize();

        err = cudaGetLastError();
        if (err != cudaSuccess)
        {
            fprintf(stderr, "Failed to launch bfs kernel (error code %s)!\n", cudaGetErrorString(err));
            exit(EXIT_FAILURE);
        }
        //printf("Second kernel launch finished\n");

        err = cudaMemcpy(&h_modified, d_modified, sizeof(int), cudaMemcpyDeviceToHost);
        printf("%d\n", h_modified);
        if (err != cudaSuccess)
        {
            fprintf(stderr, "Failed to copy d_done to host(error code %s)!\n", cudaGetErrorString(err));
            exit(EXIT_FAILURE);
        }

        printf("BFS run for level %d\n", current_depth);
        current_depth++;


    }while(h_modified != 0);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("Time taken: %f\n", time_spent);

memcpy和mallocs都已正确检查。我的问题是,在第一次运行BFS时,修改后的变量永远不会被修改为1,因此函数永远不会被第二次调用。我已经彻底检查了我的逻辑,但似乎无法解决问题。这是我在CUDA的第一个完整项目,因此任何帮助将不胜感激。如果您想要一个完整的可验证示例,请使用链接

https://github.com/soumasish/ParallelBreadthFirstSearch

1 个答案:

答案 0 :(得分:2)

  

我的问题是,在第一次运行BFS时,修改后的变量永远不会被修改为1,因此函数永远不会被第二次调用。

  1. 遇到的第一个问题是cuda-memcheck所指示的越界访问。这是因为edges数组错误地包含了1024的索引,这是无效的,因为vertices数组的大小是1024.修复是修改edges数组,以便不存在大于1023的值。

  2. 发现的另一个主要问题是current_depth变量最初设置为1(在main.cu中)。这样可以防止if内核中的bfs条件得到满足。 if条件:

        if((dfirst == current_depth) &&...
    

    取决于从匹配dfirst值的vertices数组中检索的值(current_depth)。但由于vertices数组的初始总体全部为0或-1(如问题描述中所述),因此在第一次内核启动时不可能满足if条件。因此,modified变量永远不会更改为1,因此不会发生其他内核启动。修复是在main.cu中将current_depth初始设置为零。

  3. 此外,在main.cu中观察到以下代码行:

    err = cudaMemcpy(&h_vertices, d_vertices, VERTEX_BYTES, cudaMemcpyDeviceToHost);
    

    由于h_vertices已经是一个指针,因此在这里获取它的地址是不正确的,因此符号在这里是不合适的。按原样使用该代码将是程序中堆栈损坏的一个方法。解决方法是删除&符号。