【问题标题】:Cuda not copying large arrays to deviceCuda没有将大型阵列复制到设备
【发布时间】:2014-10-18 06:14:32
【问题描述】:

我是 CUDA 的新手,所以如果我犯了任何愚蠢的错误,我很抱歉,但这让我感到困惑。以下代码非常适用于最多 620 个元素的数组。当我们将 NV def(涡旋数)从 621 更改为更高时,内核中的所有数组都变为 NAN。我希望有人能解释一下。

#include <stdio.h>
#include <time.h>
#define NP 20000
#define DT 0.01 
#define NV 620  // Fails if 621 or larger
#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

__device__ float d_x0[NV];
__device__ float d_y0[NV];
__global__ static void  calc(float *d_x, float *d_y, float Lx, float Ly ){
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    float fx, fy, t0, t1, t2, t3, t4, dx, dy, pi = acos(-1.0);
    int j, n;
    if (i<NV) {
        // For array error detection
        if (isnan(d_x0[i])) printf(" dx(%d)!",i);
        if (isnan(d_y0[i])) printf(" dy(%d)!",i);
        if (isnan(d_x[i])) printf(" x(%d)!",i);
        if (isnan(d_y[i])) printf(" y(%d)!",i);
        fx = 0.0;   fy = 0.0;
        for (j = 0 ; j < NV ; j++){ 
            dx = d_x0[i] - d_x0[j];
            dy = d_y0[i] - d_y0[j];
            t0 = 2.0 * dy / Ly;
            t1 = sin(2.0 * pi * dx / Lx);
            t3 = cos(2.0 * pi * dx / Lx);
                for (n = -10 ; n <= 10 ; n++){
                    if (n == 0){
                        if (j != i){
                            t2 = cosh(2.0 * pi * Ly / Lx * (dy / Ly + n));
                            t4 = sinh(2.0 * pi * Ly/Lx * (dy / Ly + n));
                            fx = fx + t1 / (t2 - t3);
                            fy = fy + t4 / (t2 - t3);
                        }
                    }   
                    else{
                        t2 = cosh(2.0 * pi * Ly / Lx * (dy / Ly + n));
                        t4 = sinh(2.0 * pi * Ly/Lx * (dy / Ly + n));
                        fx = fx + t1 / (t2 - t3);
                        fy = fy + t4 / (t2 - t3);                           
                    }
                }
                fy = fy - t0;
        }
        fx = fx * pi / Lx;
        fy = fy * pi / Lx;
        d_x[i] = d_x0[i] + fx * DT;
        d_y[i] = d_y0[i] + fy * DT;
        // Clip box
        if(d_x[i] > Lx)   d_x[i] = d_x[i] - (abs(d_x[i] / Lx) * Lx);
        if(d_x[i] < 0.0)  d_x[i] = d_x[i] + ((abs(d_x[i] / Lx) + 1.0) * Lx);
        if(d_y[i] > Ly)   d_y[i] = d_y[i] - (abs(d_y[i] / Ly) * Ly);
        if(d_y[i] < 0.0)  d_y[i] = d_y[i] + ((abs(d_y[i] / Ly) + 1.0) * Ly);
    }
}
__global__ static void  update(float *d_x, float *d_y ){
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    if (i<NV) {
        d_x0[i] = d_x[i];
        d_y0[i] = d_y[i];
    }
}
int main(int argc,char **argv) {
    float Lx, Ly, dv;
    int i, k;
    int size = (NV) * sizeof(float);
    float* x = (float*)malloc(size);
    float* y = (float*)malloc(size);
    float* x0 = (float*)malloc(size);
    float* y0 = (float*)malloc(size);
    dv = 0.12 * 16.0;
    Lx = sqrt(2.0 / 3.0 * sqrt(3.0) * NV / dv); 
    Ly = Lx * sqrt(3.0) / 2.0;
    for(i=0 ; i < NV ; i++){
        x0[i] = Lx * (rand() % 1000)/1000;  
        y0[i] = Ly * (rand() % 1000)/1000;
    }
    // GPU mem management
    float *d_x = NULL, *d_y = NULL;
    cudaMalloc((void**)&d_x, size);
    cudaCheckErrors("cudaMalloc fail 1");
    cudaMalloc((void**)&d_y, size);
    cudaCheckErrors("cudaMalloc fail 2");
    cudaMemcpyToSymbol(d_x0, x0, size);
    cudaCheckErrors("cudaMemcpyToSymbol fail 1");
    cudaMemcpyToSymbol(d_y0, y0, size);
    cudaCheckErrors("cudaMemcpyToSymbol fail 2");
    int threadsPerBlock = 512;
    int blocksPerGrid = (NV + threadsPerBlock - 1) / threadsPerBlock;
    for(k = 0; k < NP ; k++){
        calc<<<blocksPerGrid, threadsPerBlock>>>( d_x, d_y, Lx, Ly);
        cudaCheckErrors("kernel 1 call fail");
        cudaDeviceSynchronize();
        update<<<blocksPerGrid, threadsPerBlock>>>( d_x, d_y);
        cudaCheckErrors("kernel 2 call fail");
        if (k%((NP)/200)==0) {
            cudaMemcpy(x, d_x, size, cudaMemcpyDeviceToHost);
            cudaCheckErrors("cudaMemCopy fail 1");
            cudaMemcpy(y, d_y, size, cudaMemcpyDeviceToHost);
            cudaCheckErrors("cudaMemCopy fail 2");
            printf("(%d%%) ",100*k/NP);
            for(i = 1 ; i <= 5 ; i++) printf(",%5.2f,%5.2f ", x[i], y[i]);
            printf("\n\n");
        }
    }
    cudaMemcpy(x, d_x, size, cudaMemcpyDeviceToHost);
    cudaCheckErrors("cudaMemcpy fail 1");
    cudaMemcpy(y, d_y, size, cudaMemcpyDeviceToHost);
    cudaCheckErrors("cudaMemcpy fail 2");
    cudaMemcpyFromSymbol(x0, d_x0, size);
    cudaCheckErrors("cudaMemcpyFromSymbol fail 1");
    cudaMemcpyFromSymbol(y0, d_y0, size);
    cudaCheckErrors("cudaMemcpyFromSymbol fail 2");
    cudaFree(d_x);
    cudaFree(d_y);
    return 0;
}

我尝试更改块和网格结构,使用 -arch=sm_35 -arch=sm_30--cudart=shared 选项进行编译,甚至将数组从 float 更改为 double,但没有任何效果。

【问题讨论】:

    标签: arrays cuda nvcc


    【解决方案1】:

    您的代码从不初始化 d_xd_y 数组。

    您在设备上为它们分配空间:

    float *d_x = NULL, *d_y = NULL;
    cudaMalloc((void**)&d_x, size);
    cudaCheckErrors("cudaMalloc fail 1");
    cudaMalloc((void**)&d_y, size);
    cudaCheckErrors("cudaMalloc fail 2");
    

    但是你永远不会初始化它们或复制任何东西给它们。这意味着他们有垃圾。因此,当您调用 calc 内核时,第一行:

        if (isnan(d_x[i])) printf(" x(%d)!",i);
        if (isnan(d_y[i])) printf(" y(%d)!",i);
    

    总是为我打印出来。

    解决此问题后,您的一些个人计算在您的主循环的每次迭代中都会崩溃,包括第一个 calc 内核调用。只要单次迭代产生一个 d_xnan,我希望您能看到这将在下一次迭代中传播到您的所有其余值。

    为了解决这个问题,我建议进一步使用printf 检测您的代码。我发现以下修改很有用:

    #include <stdio.h>
    #include <time.h>
    #include <assert.h>
    #define NP 20000
    #define DT 0.01
    #define NV 621  // Fails if 621 or larger
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    __device__ float d_x0[NV];
    __device__ float d_y0[NV];
    __global__ static void  calc(float *d_x, float *d_y, float Lx, float Ly ){
        int i = blockDim.x * blockIdx.x + threadIdx.x;
        float fx, fy, t0, t1, t2, t3, t4, dx, dy, pi = acos(-1.0);
        int j, n;
        if (i<NV) {
            // For array error detection
            if (isnan(d_x0[i])) printf(" dx(%d)!",i);
            if (isnan(d_y0[i])) printf(" dy(%d)!",i);
            if (isnan(d_x[i])) printf(" x(%d)!",i);
            if (isnan(d_y[i])) printf(" y(%d)!",i);
            fx = 0.0;   fy = 0.0;
            for (j = 0 ; j < NV ; j++){
                dx = d_x0[i] - d_x0[j];
                dy = d_y0[i] - d_y0[j];
                t0 = 2.0 * dy / Ly;
                t1 = sin(2.0 * pi * dx / Lx);
                t3 = cos(2.0 * pi * dx / Lx);
                    for (n = -10 ; n <= 10 ; n++){
                        if (n == 0){
                            if (j != i){
                                t2 = cosh(2.0 * pi * Ly / Lx * (dy / Ly + n));
                                t4 = sinh(2.0 * pi * Ly/Lx * (dy / Ly + n));
                                fx = fx + t1 / (t2 - t3);
                if(isnan(fx)) {printf("!8 %d, %d, %d, %f, %f, %f\n",i, j, n, fx, t2, t3); return;}
                                fy = fy + t4 / (t2 - t3);
                            }
                        }
                        else{
                            t2 = cosh(2.0 * pi * Ly / Lx * (dy / Ly + n));
                            t4 = sinh(2.0 * pi * Ly/Lx * (dy / Ly + n));
                            fx = fx + t1 / (t2 - t3);
                            fy = fy + t4 / (t2 - t3);
                        }
                    }
                    fy = fy - t0;
            }
            fx = fx * pi / Lx;
            fy = fy * pi / Lx;
            d_x[i] = d_x0[i] + fx * DT;
            d_y[i] = d_y0[i] + fy * DT;
            // Clip box
            if(d_x[i] > Lx)   d_x[i] = d_x[i] - (abs(d_x[i] / Lx) * Lx);
            if(d_x[i] < 0.0)  d_x[i] = d_x[i] + ((abs(d_x[i] / Lx) + 1.0) * Lx);
            if(d_y[i] > Ly)   d_y[i] = d_y[i] - (abs(d_y[i] / Ly) * Ly);
            if(d_y[i] < 0.0)  d_y[i] = d_y[i] + ((abs(d_y[i] / Ly) + 1.0) * Ly);
        }
    }
    __global__ static void  update(float *d_x, float *d_y ){
        int i = blockDim.x * blockIdx.x + threadIdx.x;
        if (i<NV) {
            if (isnan(d_x[i])) assert(0);
            if (isnan(d_y[i])) assert(0);
            d_x0[i] = d_x[i];
            d_y0[i] = d_y[i];
        }
    }
    int main(int argc,char **argv) {
        float Lx, Ly, dv;
        int i, k;
        int size = (NV) * sizeof(float);
        float* x = (float*)malloc(size);
        float* y = (float*)malloc(size);
        float* x0 = (float*)malloc(size);
        float* y0 = (float*)malloc(size);
        dv = 0.12 * 16.0;
        Lx = sqrt(2.0 / 3.0 * sqrt(3.0) * NV / dv);
        Ly = Lx * sqrt(3.0) / 2.0;
        printf("Lx = %f, Ly = %f\n", Lx, Ly);
        for(i=0 ; i < NV ; i++){
            x0[i] = Lx * (rand() % 1000)/1000;
            y0[i] = Ly * (rand() % 1000)/1000;
            x[i]  = 1.0f;
            y[i]  = 1.0f;
        }
        printf("x0[0] = %f, y0[0] = %f\n", x0[0], y0[0]);
        // GPU mem management
        float *d_x = NULL, *d_y = NULL;
        cudaMalloc((void**)&d_x, size);
        cudaCheckErrors("cudaMalloc fail 1");
        cudaMalloc((void**)&d_y, size);
        cudaCheckErrors("cudaMalloc fail 2");
        cudaMemcpyToSymbol(d_x0, x0, size);
        cudaCheckErrors("cudaMemcpyToSymbol fail 1");
        cudaMemcpyToSymbol(d_y0, y0, size);
        cudaCheckErrors("cudaMemcpyToSymbol fail 2");
        cudaMemcpy(d_x, x, size, cudaMemcpyHostToDevice);
        cudaCheckErrors("cudaMemcpy fail 1");
        cudaMemcpy(d_y, y, size, cudaMemcpyHostToDevice);
        cudaCheckErrors("cudaMemcpy fail 2");
        int threadsPerBlock = 512;
        int blocksPerGrid = (NV + threadsPerBlock - 1) / threadsPerBlock;
        for(k = 0; k < NP ; k++){
            printf("iter %d\n", k);
            calc<<<blocksPerGrid, threadsPerBlock>>>( d_x, d_y, Lx, Ly);
            cudaCheckErrors("kernel 1 call fail");
            cudaDeviceSynchronize();
            update<<<blocksPerGrid, threadsPerBlock>>>( d_x, d_y);
            cudaCheckErrors("kernel 2 call fail");
            if (k%((NP)/200)==0) {
                cudaMemcpy(x, d_x, size, cudaMemcpyDeviceToHost);
                cudaCheckErrors("cudaMemCopy fail 1");
                cudaMemcpy(y, d_y, size, cudaMemcpyDeviceToHost);
                cudaCheckErrors("cudaMemCopy fail 2");
                printf("(%d%%) ",100*k/NP);
                for(i = 1 ; i <= 5 ; i++) printf(",%5.2f,%5.2f ", x[i], y[i]);
                printf("\n\n");
            }
        }
        cudaMemcpy(x, d_x, size, cudaMemcpyDeviceToHost);
        cudaCheckErrors("cudaMemcpy fail 1");
        cudaMemcpy(y, d_y, size, cudaMemcpyDeviceToHost);
        cudaCheckErrors("cudaMemcpy fail 2");
        cudaMemcpyFromSymbol(x0, d_x0, size);
        cudaCheckErrors("cudaMemcpyFromSymbol fail 1");
        cudaMemcpyFromSymbol(y0, d_y0, size);
        cudaCheckErrors("cudaMemcpyFromSymbol fail 2");
        cudaFree(d_x);
        cudaFree(d_y);
        return 0;
    }
    

    这些向我表明,对于元素 86 和 518,下面的计算是错误的,因为 t2 = t3 = 1.0:

                                fx = fx + t1 / (t2 - t3);
    

    希望您可以从那里向后工作。我发现您的随机化方案为x0y0 产生了许多重复值:

    for(i=0 ; i < NV ; i++){
        x0[i] = Lx * (rand() % 1000)/1000;  
        y0[i] = Ly * (rand() % 1000)/1000;
    }
    

    这些重复值导致此处的值为 0:

            dx = d_x0[i] - d_x0[j];
    

    这里 cos(0) = 1.0:

            t3 = cos(2.0 * pi * dx / Lx);
    

    对于 ij 的一些值,您也在这里得到 1:

                            t2 = cosh(2.0 * pi * Ly / Lx * (dy / Ly + n));
    

    这导致 t2-t3 = 0,然后事情就爆炸了。

    我认为这些都不是 CUDA 特有的。我相信这段代码也应该在使用嵌套循环的普通主机代码中爆炸。我相信增加NV 会加剧问题,因为d_x0d_y0 中有更多重复项。

    【讨论】:

    • d_xd_y 未初始化的原因是因为数据是在函数calc 中在设备上创建的。然后将数据复制回主机。注意d_x[i] = d_x0[i] + fx * DT;d_y[i] = d_y0[i] + fy * DT; 行。谢谢。
    • 当然可以,但是请注意,calc 内核中的第一行正在检查d_xd_y 的有效性,然后您提到的任何这些行都会被执行。因此,它们最初可能是nan,并且可能会触发这些打印输出。该问题不是您代码中的关键问题。
    • 尽管有这些 cmets,但两种代码(原始代码和修订代码)仅适用于 621 个元素以下大小的数组。您的代码对原始问题没有任何更改。我有一个 CUDA 6.5 的 GTX Titan。
    • 如果您想编辑您的问题并发布您当前正在运行的修改后的代码,我会再看一下。或者,作为一个简单的测试,如果它检测到 t2-t3 = 0(在计算除法之前),您可能希望在内核中放置一个 printf 或其他指标。
    • 发布的代码没有重大变化。该算法在物理学中是众所周知的并且已经被广泛使用。关键是,如果所讨论的除法是数组中所有元素成为 NaN 的决定因素,那么对于 621 个元素以下的数组大小也会发生这种情况,但事实并非如此。问题边界正好是 621 个元素,这可能表明一些与 CUDA/内存/硬件限制相关的问题。
    【解决方案2】:

    正如 Robert Crovella 所指出的,随机数生成器总是生成相同的序列,其中第一个重复坐标出现在数组位置 621(在 windows 上),这导致观察到的无限爆炸。问题已解决,重新生成生成器并将以下代码添加到原始程序以检查叠加:

    for(i=1 ; i <= NV ; i++){
        do {
            test=false; 
            x0[i] = Lx * (rand() % 1000)/1000;  
            y0[i] = Ly * (rand() % 1000)/1000;
            x[i]=x0[i]; y[0]=y0[i];
            for(j=1 ; j < i ; j++){
                if (i!=j&&x0[i]==x0[j]&&y0[i]==y0[j]) {
                    test=true; 
                    printf("(%d)superposto.\n",i);
                }
            }
        } while (test);
        printf("%f, %f\n", x0[i], y0[i]);
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-25
      • 2013-04-10
      • 2012-04-04
      • 2013-12-06
      • 1970-01-01
      • 2019-10-15
      • 2017-03-23
      • 2019-01-02
      • 2015-04-22
      相关资源
      最近更新 更多