cudaMalloc和cudaMemcpy无法处理内核调用

时间:2016-02-22 11:15:01

标签: memory cuda

我已经初始化了一个数组,我试图在内核调用的每个线程中使用(每个线程使用数组的不同部分,因此没有依赖项)。我使用cudaMalloc创建阵列并在设备上保存内存,并使用cudaMemcpy将阵列从主机复制到设备。

我将cudaMalloc返回的指针传递给每个线程使用的内核调用。

int SIZE = 100;

int* data = new int[SIZE];
int* d_data = 0;

cutilSafeCall( cudaMalloc(&d_data, SIZE * sizeof(int)) );
for (int i = 0; i < SIZE; i++)
    data[i] = i;

cutilSafeCall( cudaMemcpy(d_data, data, SIZE * sizeof(int), cudaMemcpyHostToDevice) );

此代码取自here。 对于内核调用。

kernel<<<blocks, threads>>> (results, d_data);

我使用结构Result跟踪每个线程的结果。下一个代码可以正常运行。

__global__ void mainKernel(Result res[], int* data){
   int x = data[0];
}

但是当我将该值分配给res

__global__ void mainKernel(Result res[], int* data){
   int threadId = (blockIdx.x * blockDim.x) + threadIdx.x;

   int x = data[0];

   res[threadId].x = x;
}

引发错误:

  

cudaSafeCall()文件中的运行时API错误,第355行:遇到非法内存访问。

任何涉及使用该指针的操作都会出现相同的错误

__global__ void mainKernel(Result res[], int* data){
   int threadId = (blockIdx.x * blockDim.x) + threadIdx.x;

   int x = data[0];

   if (x > 10)
      res[threadId].x = 5;
}

res的定义没有问题。将任何其他值分配给res[threadId].x并不会给我任何错误。

这是运行cuda-memcheck的输出:

  

=========无效__global__读取大小4
  =========在mainKernel的0x00000150处(Result *,int *)
  =========通过块(49,0,0)中的线程(86,0,0)
  =========地址0x13024c0000超出范围
  =========保存的主机回溯到内核启动时的驱动程序入口点
  =========主机帧:/usr/lib/x86_64-linux-gnu/libcuda.so.1(cuLaunchKernel + 0x2cd)[0x150d6d]
  =========主机帧:./ out [0x2cc4b]
  =========主机帧:./ out [0x46c23]
  =========主机帧:./ out [0x3e37]
  =========主机帧:./ out [0x3ca1]
  =========主机帧:./ out [0x3cd6]
  =========主机帧:./ out [0x39e9]
  =========主机帧:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main + 0xf5)[0x21ec5]
  =========主机帧:./ out [0x31b9]

编辑:

这是完整代码的示例:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <assert.h>

typedef struct    
{
   int x,y,z;
} Result;

__global__ void mainKernel(Result  pResults[], int* dataimage)
{

   int threadId = (blockIdx.x * blockDim.x) + threadIdx.x;

   int xVal = dataimage[0];
   if (xVal > 10)
       pResults[threadId].x = 5;

}

int main (int argc, char** argv)
{

   int NUM_THREADS = 5*5;

   int SIZE = 100;

   int* data = new int[SIZE];
   int* d_data = 0;

   cutilSafeCall( cudaMalloc(&d_data, SIZE * sizeof(int)) );
   for (int i = 0; i < SIZE; i++)
       data[i] = i;

   cutilSafeCall( cudaMemcpy(d_data, data, SIZE * sizeof(int), cudaMemcpyHostToDevice) );

   unsigned int GPU_ID = 1;  // not actually :-)
   // unsigned int GPU_ID =  cutGetMaxGflopsDeviceId() ;
   cudaSetDevice(GPU_ID); 

   Result * results_GPU = 0;
   cutilSafeCall( cudaMalloc( &results_GPU,  NUM_THREADS * sizeof(Result)) );

   Result * results_CPU = 0;
   cutilSafeCall( cudaMallocHost( &results_CPU, NUM_THREADS * sizeof(Result)) );

   mainKernel<<<5,5>>> ( results_GPU, d_data );

   cudaThreadSynchronize(); 

   cutilSafeCall( cudaMemcpy(results_CPU, results_GPU, NUM_THREADS * sizeof(Result),cudaMemcpyDeviceToHost) );

   cutilSafeCall(cudaFree(results_GPU));
   cutilSafeCall(cudaFreeHost(results_CPU));
   cudaThreadExit();

 } // ()

1 个答案:

答案 0 :(得分:0)

你的问题在于这一系列的呼叫:

  cutilSafeCall( cudaMalloc(&d_data, SIZE * sizeof(int)) );
   for (int i = 0; i < SIZE; i++)
       data[i] = i;

   cutilSafeCall( cudaMemcpy(d_data, data, SIZE * sizeof(int), cudaMemcpyHostToDevice) );

   unsigned int GPU_ID = 1;
   cudaSetDevice(GPU_ID); 

   Result * results_GPU = 0;
   cutilSafeCall( cudaMalloc( &results_GPU,  NUM_THREADS * sizeof(Result)) );

   Result * results_CPU = 0;
   cutilSafeCall( cudaMallocHost( &results_CPU, NUM_THREADS * sizeof(Result)) );

   mainKernel<<<5,5>>> ( results_GPU, d_data );

实际发生的是您正在分配d_data并运行内核不同的GPU,并且d_data在您启动内核的GPU上无效。

详细信息,因为您在cudaMalloc之前为d_data致电cudaSetDevice,因此您要在默认设备上分配d_data,然后明确分配results_GPU并在设备1上运行内核。显然,设备1和默认设备不是相同的GPU(设备的枚举通常在运行时从0开始)。

如果你改变这样的代码:

   unsigned int GPU_ID = 1;
   cutilSafeCall(cudaSetDevice(GPU_ID)); 

   cutilSafeCall( cudaMalloc(&d_data, SIZE * sizeof(int)) );
   for (int i = 0; i < SIZE; i++)
       data[i] = i;

   cutilSafeCall( cudaMemcpy(d_data, data, SIZE * sizeof(int), cudaMemcpyHostToDevice) );

   Result * results_GPU = 0;
   cutilSafeCall( cudaMalloc( &results_GPU,  NUM_THREADS * sizeof(Result)) );

   Result * results_CPU = 0;
   cutilSafeCall( cudaMallocHost( &results_CPU, NUM_THREADS * sizeof(Result)) );

   mainKernel<<<5,5>>> ( results_GPU, d_data );

即。在进行任何分配之前选择非默认设备,问题应该消失。你的非常简单的内核不会发生这种情况的原因:

__global__ void mainKernel(Result res[], int* data){
   int x = data[0];
}

只是CUDA编译器默认执行非常积极的优化,并且由于data[0]的读取结果实际上并未使用,所以可以优化整个读取并留下一个空的存根内核,它没有做任何事情。只有当在存储器写入中使用来自存储器的加载结果时,才会在编译期间优化掉代码。如果您好奇,可以通过反汇编编译器发出的代码来自行确认。

请注意,有些方法可以通过点对点访问在支持它的多GPU系统上运行,但必须在代码中明确配置才能使用该工具。