2014-01-14 37 views
1

我最近遇到了Uncorrectable ECC error所示的问题。简而言之,我不时收到一个不可纠正的ECC错误,我的动态并行代码会产生不正确的结果。不可纠正的ECC错误的最可能的假设是驱动程序堆栈已损坏,这也已被另一用户的经验间接证实(参见上面的帖子)。现在我想面对第二个问题,即算法问题。为此,我正在处理下面报告的重现器,由于生成不正确结果的原始代码使用动态并行性,因此也使用此CUDA特性。错误的CUDA动态并行代码结果

我没有看到这个代码的任何明显的问题。我认为关于子内核启动的同步应该是正确的:第一个__syncthreads()不应该是必需的,并且cudaDeviceSynchronize()应该确保子内核的所有内存写入在printf之前完成。

我的问题是:这段代码是错误的还是错误的结果是由于非编程问题?

我的配置:CUDA 5.0,Windows 7,4 GPU系统,配备Kepler K20c,驱动程序327.23。

#include <stdio.h> 
#include <conio.h> 

#define K 6 
#define BLOCK_SIZE 256 

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); } 
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true) 
{ 
    if (code != cudaSuccess) 
    { 
     fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); 
     if (abort) { getch(); exit(code); } 
    } 
} 

int iDivUp(int a, int b) { return ((a % b) != 0) ? (a/b + 1) : (a/b); } 

__global__ void child_kernel(double* P1) 
{ 
    int m = threadIdx.x; 

    P1[m] = (double)m; 
} 

__global__ void parent_kernel(double* __restrict__ x, int M) 
{ 
    int i = threadIdx.x + blockDim.x * blockIdx.x; 

    if(i<M) { 

     double* P1 = new double[13]; 

     dim3 dimBlock(2*K+1,1); dim3 dimGrid(1,1); 

     __syncthreads(); 
     child_kernel<<<dimGrid,dimBlock>>>(P1); 
     cudaDeviceSynchronize(); 

     for(int m=0; m<2*K+1; m++) printf("%f %f\n",P1[m],(double)m); 

    } 
} 

int main() { 

    const int M = 19000; 

//gpuErrchk(cudaSetDevice(0)); 

    double* x = (double*)malloc(M*sizeof(double)); 
    for (int i=0; i<M; i++) x[i] = (double)i; 

    double* d_x; gpuErrchk(cudaMalloc((void**)&d_x,M*sizeof(double))); 

    gpuErrchk(cudaMemcpy(d_x,x,M*sizeof(double),cudaMemcpyHostToDevice)); 

    dim3 dimBlock(BLOCK_SIZE,1); dim3 dimGrid(iDivUp(M,BLOCK_SIZE)); 
    parent_kernel<<<dimGrid,dimBlock>>>(d_x,M); 
    gpuErrchk(cudaPeekAtLastError()); 
    gpuErrchk(cudaDeviceSynchronize()); 

    getch(); 

    return 0; 
} 
+0

当你说你有不正确的结果时,它们是什么?你确定这不像'printf'缓冲区溢出那么简单吗? – talonmies

+0

@talonmies通常,第一次启动代码时,我会收到“或多或少”正确的结果,因为同一行中的两个数字大部分是重合的。随后我启动​​它,越来越多的'P1 [m]'与'(double)m'不同,具有不合理的'double'值。不,我不确定这不是'printf'缓冲区溢出。要检查它,我应该分流'printf'并使用调试器或将'P1'传递给'main',这是我在接下来几个小时内无法做的测试,因为机器暂时处于忙碌状态。 – JackOLantern

+0

@talonmies但是,你能不能指出一些已知的方向来避免'printf'缓冲区溢出,这无论如何都很好知道? – JackOLantern

回答

2

我敢肯定,你超出launch pending limit。几乎不可能用你的代码原样告诉,但是我修改了它并在子内核启动时添加了错误检查。

当我这样做,我得到启动错误,打印输出!。跳绳推出错误的情况下,我的所有的P1[m]m通行证(我没有得到任何*打印在所有。)

#include <stdio.h> 

#define K 6 
#define BLOCK_SIZE 256 

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); } 
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true) 
{ 
    if (code != cudaSuccess) 
    { 
     fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); 
     if (abort) { exit(code); } 
    } 
} 

int iDivUp(int a, int b) { return ((a % b) != 0) ? (a/b + 1) : (a/b); } 

__global__ void child_kernel(unsigned long long* P1) 
{ 
    int m = threadIdx.x; 

    P1[m] = (unsigned long long)m; 
} 

__global__ void parent_kernel(double* __restrict__ x, int M) 
{ 
    int i = threadIdx.x + blockDim.x * blockIdx.x; 

    if(i<M) { 

     unsigned long long* P1 = new unsigned long long[13]; 

     dim3 dimBlock(2*K+1,1); dim3 dimGrid(1,1); 

     __syncthreads(); 
     child_kernel<<<dimGrid,dimBlock>>>(P1); 
     cudaDeviceSynchronize(); 
     cudaError_t err = cudaGetLastError(); 
     if (err != cudaSuccess) printf("!"); 
     else for(unsigned long long m=0; m<dimBlock.x; m++) if (P1[m] != m) printf("*"); 

    } 
} 

int main() { 

    const int M = 19000; 

//gpuErrchk(cudaSetDevice(0)); 

    double* x = (double*)malloc(M*sizeof(double)); 
    for (int i=0; i<M; i++) x[i] = (double)i; 

    double* d_x; gpuErrchk(cudaMalloc((void**)&d_x,M*sizeof(double))); 

    gpuErrchk(cudaMemcpy(d_x,x,M*sizeof(double),cudaMemcpyHostToDevice)); 

    dim3 dimBlock(BLOCK_SIZE,1); dim3 dimGrid(iDivUp(M,BLOCK_SIZE)); 
    parent_kernel<<<dimGrid,dimBlock>>>(d_x,M); 
    gpuErrchk(cudaPeekAtLastError()); 
    gpuErrchk(cudaDeviceSynchronize()); 

    return 0; 
} 

随意添加err变量的进一步解码父内核内核检查的说服自己,你超出了启动待定限制。作为另一种测试,您可以在您的主机代码中将M设置为2048而不是19000,并且所有!打印输出都将消失。 (启动挂起限制默认== 2048)

正如我在评论中所述,我认为不可纠正的ECC错误是一个单独的问题,我建议尝试我在评论中链接的驱动程序321.01。

+0

非常感谢您的回答。在这一刻,我无法测试你的代码,因为机器被一个有截止日期的同事模拟完成,我将在星期一恢复这个问题。与此同时,我已经研究了你对这个[post]的回答(http://stackoverflow.com/questions/17902314/some-child-grids-not-being-executed-with-cuda-dynamic-parallelism)这是照亮为了我。我错误地认为检查父内核的错误也会捕获错误,这些错误也是针对子内核启动的,我现在也会为设备内核启动添加错误检查。 – JackOLantern

+0

我终于测试了你的代码,并且实际上我超出了启动等待限制。具体来说,如果我将'err'变量的解码更改为'if(err == cudaErrorLaunchPendingCountExceeded)printf(“!”);',那么我会收到'!'。另外,如果我设置了'gpuErrchk(cudaDeviceSetLimit(cudaLimitDevRuntimePendingLaunchCount,20000));'在代码的开头,我不再收到'!'了。最后,我的原始代码现在可以通过将挂起启动计数设置为'20000'来正确工作。谢谢。我会接受你的回答。 – JackOLantern