我已经初始化了一个数组,我试图在内核调用的每个线程中使用(每个线程使用数组的不同部分,因此没有依赖项)。我使用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();
} // ()
答案 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系统上运行,但必须在代码中明确配置才能使用该工具。