【问题标题】:Wrong results with CUDA threads writing on private locations in global memoryCUDA 线程在全局内存中的私有位置上写入的错误结果
【发布时间】:2013-11-24 23:39:51
【问题描述】:

编辑 3: 我需要每个线程在全局内存中写入和读取一个私有位置。下面我发布了一个显示我的问题的工作代码。下面,我将列出涉及的主要变量和结构。

变量

  • srcArr_h (host) --> srcArr_d (device) : 在 [0, COLORLEVELS] 范围内的随机浮点数组,尺寸由 ARRDIM 给出
  • auxD (device) : 维度数组ARRDIM * ARRDIM 将最终结果保存在设备中
  • auxH (host) : 维度数组 ARRDIM * ARRDIM 将最终结果保存在主机中
  • c_glob_d (device) : 为每个线程保留COLORLEVELS 的私有位置的数组浮动,大小由num_threads * COLORLEVELS 给出
  • idx (device) : 当前线程的标识号

我的问题:在内核中,我为每个值ic (ic∈ [0, COLORLEVELS]) 更新c_glob[idx],即c_glob[idx][ic]。我使用c_glob[idx][COLORLEVELS] 来计算存储在auxD 中的最终结果g0。我的问题是我的最终结果是错误的。复制到 auxH 的结果显示,我得到的数字至少比预期大一个数量级,甚至奇怪的数字表明我的操作可能会溢出。
帮助:我做错了什么?如何让每个线程写入和读取全局内存中的每个私有位置?现在我正在使用ARRDIM = 512 进行调试,但我的目标是使其适用于ARRDIM~ 10^4,从而为10^4*10^4 线程创建c_glob 数组)。我想我会遇到每次运行允许的线程总数的问题。所以我想知道您是否可以为我的问题提出任何其他解决方案。
谢谢你。

#include <string>
#include <stdint.h>
#include <iostream>
#include <stdio.h>
#include "cuPrintf.cu"
using namespace std;

#define ARRDIM 512
#define COLORLEVELS 4

__global__ void gpuKernel
(
    float *sa, float *aux,
    size_t memPitchAux, int w,
    float *c_glob
)
{
    float sc_loc[COLORLEVELS];

    float g0=0.0f;

    int tidx = blockIdx.x * blockDim.x + threadIdx.x; 
    int tidy = blockIdx.y * blockDim.y + threadIdx.y; 

    int idx  = tidy * memPitchAux/4 + tidx;

    for(int ic=0; ic<COLORLEVELS; ic++)
    {
        sc_loc[ic] = ((float)(ic*ic));
    }

    for(int is=0; is<COLORLEVELS; is++)
    {
        int ic = fabs(sa[tidy*w +tidx]);
        c_glob[tidy * COLORLEVELS + tidx + ic] += 1.0f;
    }

    for(int ic=0; ic<COLORLEVELS; ic++)
    {
        g0 += c_glob[tidy * COLORLEVELS + tidx + ic]*sc_loc[ic];
    }

    aux[idx] = g0;
}

int main(int argc, char* argv[])
{
    /*
     * array src host and device
     */
    int heightSrc = ARRDIM;
    int widthSrc = ARRDIM;
    cudaSetDevice(0);

    float *srcArr_h, *srcArr_d;
    size_t nBytesSrcArr = sizeof(float)*heightSrc * widthSrc;

    srcArr_h = (float *)malloc(nBytesSrcArr); // Allocate array on host
    cudaMalloc((void **) &srcArr_d, nBytesSrcArr); // Allocate array on device
    cudaMemset((void*)srcArr_d,0,nBytesSrcArr); // set to zero

    int totArrElm = heightSrc*widthSrc;

    for(int ic=0; ic<totArrElm; ic++)
    {
        srcArr_h[ic] = (float)(rand() % COLORLEVELS);
    }

    cudaMemcpy( srcArr_d, srcArr_h,nBytesSrcArr,cudaMemcpyHostToDevice);

    /*
     * auxiliary buffer auxD to save final results
     */
    float *auxD;
    size_t auxDPitch;
    cudaMallocPitch((void**)&auxD,&auxDPitch,widthSrc*sizeof(float),heightSrc);
    cudaMemset2D(auxD, auxDPitch, 0, widthSrc*sizeof(float), heightSrc);

    /*
     * auxiliary buffer auxH allocation + initialization on host
     */
    size_t auxHPitch;
    auxHPitch = widthSrc*sizeof(float);
    float *auxH = (float *) malloc(heightSrc*auxHPitch);

    /*
     * kernel launch specs
     */
    int thpb_x = 16;
    int thpb_y = 16;

    int blpg_x = (int) widthSrc/thpb_x;
    int blpg_y = (int) heightSrc/thpb_y;
    int num_threads = blpg_x * thpb_x + blpg_y * thpb_y;

    /* 
     * c_glob: array that reserves a private location of COLORLEVELS floats for each thread
     */
    int cglob_w = COLORLEVELS;
    int cglob_h = num_threads;

    float *c_glob_d;
    size_t c_globDPitch;
    cudaMallocPitch((void**)&c_glob_d,&c_globDPitch,cglob_w*sizeof(float),cglob_h);
    cudaMemset2D(c_glob_d, c_globDPitch, 0, cglob_w*sizeof(float), cglob_h);

    /*
    * kernel launch
    */
    dim3 dimBlock(thpb_x,thpb_y, 1);
    dim3 dimGrid(blpg_x,blpg_y,1);

    gpuKernel<<<dimGrid,dimBlock>>>(srcArr_d,auxD, auxDPitch, widthSrc, c_glob_d);

    cudaThreadSynchronize();

    cudaMemcpy2D(auxH,auxHPitch, 
                 auxD,auxDPitch,  
                 auxHPitch, heightSrc,
                 cudaMemcpyDeviceToHost);
    cudaThreadSynchronize();

    float min = auxH[0];
    float max = auxH[0];
    float f;
    string str;

    for(int i=0; i<widthSrc*heightSrc; i++)
    {

        if(min > auxH[i])
            min = auxH[i];
        if(max < auxH[i])
            max = auxH[i];
    }
    cudaFree(srcArr_d);
    cudaFree(auxD);
    cudaFree(c_glob_d);

}

【问题讨论】:

  • 我的猜测是您没有对内核进行 cuda 错误检查,并且它运行不正确。当您使用 cuda-memcheck 运行代码时会发生什么?
  • 是否可以发布完整的可编译和可执行代码来重现您的错误?
  • 投票结束。 SO 期望:“有关您编写的代码问题的问题必须在问题本身中描述特定问题 - 并包括有效的代码来重现它。请参阅 SSCCE.org 以获得指导。”您尚未提供 SSCCE.org 代码
  • 很抱歉浪费了你的时间,但问题是所有线程都试图更新 gmem 上的同一个数组,这就是为什么我得到疯狂的结果。事实是我原来的内核中有更多的代码,如果我在内核中分配 + 初始化 c[](c[] 存储在寄存器/本地内存中),运行失败,_gpuContrastKernel() 执行失败:(6) 启动超时并被终止_这发生在使用特斯拉M2090 集群,但是如果我在我的工作站(TESLA C1060)上运行代码,我的实现就很好。我读到 Fermi 架构要严格得多,这可能是问题吗?

标签: c++ cuda parallel-processing gpu gpgpu


【解决方案1】:

您决定既不显示整个代码也不显示缩小的大小来重现您的问题。因此,无法对以下可能的解决方案进行测试和验证。

我认为您已经找到了问题的根源:多个线程正在尝试并行写入相同的内存位置。这是导致竞争条件的情况。例如,请参阅演示文稿的第四张幻灯片"CUDA C: race conditions, atomics, locks, mutex, and warps"

竞争条件有一个蛮力解决方案:原子函数。它们在 CUDA C 编程指南的第 B.12 节中进行了描述。因此,您可以尝试通过更改行来解决您的问题

c[ic] += 1.0f;

atomicAdd(&c[ic],1);

您将为此付出代价:原子操作序列化代码以避免竞争条件。

我已经提到原子函数是解决问题的强力解决方案,因为通过适当地重新考虑实现,您可以找到避免它们的方法。但由于您提供的细节很少,目前还不能说。

【讨论】:

  • 好的,谢谢。我发布了一个重现问题的代码。希望现在我的问题更清楚了。不幸的是,原子操作对我的情况没有帮助,现在我分享的代码应该很清楚了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
相关资源
最近更新 更多