Cuda 在主机上完美运行的代码,放在内核中,由于神秘的原因而失败

Cuda 在主机上完美运行的代码,放在内核中,由于神秘的原因而失败,cuda,neural-network,backpropagation,Cuda,Neural Network,Backpropagation,我必须将预先存在的“仅主机”反向传播实现移植到CUDA。我认为算法的性质在这里并不重要,所以我不会对它的工作方式做太多解释。但我认为重要的是,它使用三维数组,所有三维数组都是动态分配的。 我使用VS2010和CUDA 5.0。我的设备是2.1。原始的纯主机代码可以在这里下载 → 守则的要点: 使用“pattern.h”中的数据结构,将成人数据中的模式加载到内存中 分配了几个多维数组 该算法使用之前分配的数组在模式上运行 如果要尝试运行代码,请不要忘记修改kernel.cu开头的PATH常量。我

我必须将预先存在的“仅主机”反向传播实现移植到CUDA。我认为算法的性质在这里并不重要,所以我不会对它的工作方式做太多解释。但我认为重要的是,它使用三维数组,所有三维数组都是动态分配的。 我使用VS2010和CUDA 5.0。我的设备是2.1。原始的纯主机代码可以在这里下载 →

守则的要点:

  • 使用“pattern.h”中的数据结构,将成人数据中的模式加载到内存中
  • 分配了几个多维数组
  • 该算法使用之前分配的数组在模式上运行
  • 如果要尝试运行代码,请不要忘记修改kernel.cu开头的PATH常量。我还建议你使用“2”层,“5”神经元,学习率为“0.00001”。正如你所看到的,这是完美的。“MSE”正在改善。对于那些对这种算法的作用一无所知的人,让我们简单地说,它学习如何根据模式中存在的14个变量预测目标值。“MSE”减小,这意味着算法在每个“历元”之后犯的错误更少

    我花了很长时间试图在设备上运行这段代码。我还是不成功。最后一次尝试是简单地复制初始化数组的代码并将算法运行到一个大内核中。又失败了。这个代码可以在那里下载 →

    准确地说,以下是与原始纯主机代码的区别:

    • 算法使用的f()和fder()将成为设备 功能
    • 参数是硬编码的:2层,5个神经元,学习率为 0.00001
    • “w”数组是使用固定值(0.5)而不是rand()初始化的 再
    • 在设备的内存中分配一个数据结构,并且数据 从成人数据加载后发送到设备内存中 在主人的记忆中
    我认为我做了使代码在内核中运行所需的最少修改。“kernel\u check\u learningData”内核显示了一些关于加载到设备内存中的模式的信息,证明了以下代码,将模式从主机发送到设备,确实有效:

    Data data;
    Data* dev_data;
    int* dev_t;
    double* dev_x;
    ...
    input_adult(PathFile, &data);
    ...
    cudaMalloc((void**)&dev_data, sizeof(Data));
    cudaMalloc((void**)&dev_t, data.N * sizeof(int));
    cudaMalloc((void**)&dev_x, data.N * data.n * sizeof(double));
    // Filling the device with t and x's data.
    cudaMemcpy(dev_t, data.t, data.N * sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_x, data.x, data.N * data.n * sizeof(double), cudaMemcpyHostToDevice);
    // Updating t and x pointers into devices Data structure.
    cudaMemcpy(&dev_data->t, &dev_t, sizeof(int*), cudaMemcpyHostToDevice);
    cudaMemcpy(&dev_data->x, &dev_x, sizeof(double*), cudaMemcpyHostToDevice);
    // Copying N and n.
    cudaMemcpy(&dev_data->N, &data.N, sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(&dev_data->n, &data.n, sizeof(int), cudaMemcpyHostToDevice);
    
    当读取“w”数组时,它显然在正向阶段开始时失败。我找不到任何解释

    我认为有两种可能性:

  • 将模式发送到设备内存中的代码被窃听,尽管它似乎工作正常,并且在开始前向阶段时会进一步引发错误
  • CUDA API的行为与它应该的不一样 很长一段时间以来,我一直在拼命寻找我的错误。所以我想知道社区是否能为我提供一些帮助


    谢谢。

    这是您代码中的问题,以及为什么它在64位机器模式下工作,而不是在32位机器模式下工作

    在反向传播内核中的正向路径中,有一系列代码如下:

    /*
    * for layer = 0
    */
    for (i = 0; i < N[0]; i++) {    // for all neurons i of layer 0
    a[0][i] = x[ data->n * pat + i];    // a[0][i] = input i
    }
    
    a[layer] = (double *)malloc( N[layer] * sizeof(double) );  
    
    假设
    test\u bp.exe
    是可执行文件的名称。cuda memcheck可以方便地识别是否发生了越界写入,甚至可以识别发生写入的源代码行

    那为什么这是不允许的呢?让我们先看一下内核代码中的代码:<代码> A[0 ][]/COD>被分配:

    a[0] = (double *)malloc( N[0] * sizeof(double *) );
                                                  ^ oops!!
    
    a[0][]
    用于保存
    double
    数据,但您正在分配指针存储。 事实证明,在64位机器中,这两种类型的存储大小相同,因此它最终可以正常工作。但是在32位机器中,
    double
    指针是4字节,而
    double
    数据是8字节。因此,在32位机器中,当我们以8字节的数据步长对这个数组进行索引时,我们最终会从数组的末尾运行

    在内核代码的其他地方,您正在为
    a
    的其他“层”分配存储,如下所示:

    /*
    * for layer = 0
    */
    for (i = 0; i < N[0]; i++) {    // for all neurons i of layer 0
    a[0][i] = x[ data->n * pat + i];    // a[0][i] = input i
    }
    
    a[layer] = (double *)malloc( N[layer] * sizeof(double) );  
    
    这是正确的。我看到原始的“仅主机”代码似乎也包含此错误。代码中也可能存在潜在的缺陷


    如果要在windows wddm设备上运行,您仍然需要解决内核运行时间问题,以某种方式避免windows TDR事件。正如我已经指出的,这段代码没有试图使用机器的并行功能。

    这是代码中的问题,以及为什么它在64位机器模式下工作,而不是在32位机器模式下工作

    在反向传播内核中的正向路径中,有一系列代码如下:

    /*
    * for layer = 0
    */
    for (i = 0; i < N[0]; i++) {    // for all neurons i of layer 0
    a[0][i] = x[ data->n * pat + i];    // a[0][i] = input i
    }
    
    a[layer] = (double *)malloc( N[layer] * sizeof(double) );  
    
    假设
    test\u bp.exe
    是可执行文件的名称。cuda memcheck可以方便地识别是否发生了越界写入,甚至可以识别发生写入的源代码行

    那为什么这是不允许的呢?让我们先看一下内核代码中的代码:<代码> A[0 ][]/COD>被分配:

    a[0] = (double *)malloc( N[0] * sizeof(double *) );
                                                  ^ oops!!
    
    a[0][]
    用于保存
    double
    数据,但您正在分配指针存储。 事实证明,在64位机器中,这两种类型的存储大小相同,因此它最终可以正常工作。但是在32位机器中,
    double
    指针是4字节,而
    double
    数据是8字节。因此,在32位机器中,当我们以8字节的数据步长对这个数组进行索引时,我们最终会从数组的末尾运行

    在内核代码的其他地方,您正在为
    a
    的其他“层”分配存储,如下所示:

    /*
    * for layer = 0
    */
    for (i = 0; i < N[0]; i++) {    // for all neurons i of layer 0
    a[0][i] = x[ data->n * pat + i];    // a[0][i] = input i
    }
    
    a[layer] = (double *)malloc( N[layer] * sizeof(double) );  
    
    这是正确的。我看到原始的“仅主机”代码似乎也包含此错误。代码中也可能存在潜在的缺陷

    如果希望在窗口上运行,您仍然需要解决内核运行时间问题,以某种方式避免windows TDR事件