遇到CUDA非法内存访问

问题描述

我在装有Nvidia GTX 960M的笔记本电脑上编写了CUDA程序。该代码可以正常工作。我还实现了可以在此线程中找到的错误检查: What is the canonical way to check for errors using the CUDA runtime API?

并使用cuda-memcheck测试了代码,该错误报告了0个错误。

我想在具有Nvidia Titan X的服务器上测试我的代码。但是cudaPeekAtLastError()会引发错误:

遇到非法的内存访问

对于我的笔记本电脑和服务器,我正在使用以下堆分配

cudaDeviceSetLimit(cudaLimitMallocHeapSize,1024 * 1024 * 1024);

并运行以下线程和块:

int blockSize = 128;
int numBlocks = (nPossibilities + blockSize - 1) / blockSize;

GTX 960M的计算能力为5,而Titan X为6.1,但是根据计算能力表,每个多处理器最多具有32个活动块和最多2048个线程

https://en.wikipedia.org/wiki/CUDA

我在服务器上运行了cuda-memcheck,非法内存访问的问题是由于空指针造成的。

为了解决该问题,我使用以下几行将堆内存大小分配从1GB增加到2GB,问题得到解决:

const size_t malloc_limit = size_t(2048) * size_t(2048) * size_t(2048);
cudaDeviceSetLimit(cudaLimitMallocHeapSize,malloc_limit); 

我的问题是,为什么在Titan X上会出现此问题,而在960M上却不会发生?为什么我需要增加分配给Titan X而不是960M的堆内存大小?

如果需要,我可以发布我的代码,但这是一个很大的代码,内核中有多个函数调用。

cuda-memcheck之后的错误如下:

GPUassert:未指定的启动失败all.cu 779 ========= CUDA-MEMCHECK =========大小为8的无效的全局写入 =========在/home/osa/cuda/all.cu中的0x00001130:186:fun(double *,double *,double *,double *, double *,double *,int,int,int) ==========通过线程(125,0)在块(193,0)中 =========地址0x00000000超出范围 =========在内核启动时保存的主机回溯到驱动程序入口点 =========主机框架:/usr/lib/i386-linux-gnu/libcuda.so.1(cuLaunchKernel + 0x2fe)[0x282a4e] =========主机框架:./全部[0x1dac1] =========主机框架:./全部[0x382d3] =========主机框架:./全部[0x9508] =========主机框架:./全部[0x93c0] =========主机框架:./全部[0x942d] =========主机框架:./全部[0x8d7a] =========主机框架:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main + 0xf0)[0x20840] =========主机框架:./全部[0x2999] ========= =========大小为8的无效的全局写入 =========在/home/osa/cuda/all.cu中的0x00001130:186:fun(double *,double *,double *,double *, double *,double *,int,int,int) ==========通过线程(124,0)中 =========地址0x00000000超出范围 =========在内核启动时保存的主机回溯到驱动程序入口点 =========主机框架:/usr/lib/i386-linux-gnu/libcuda.so.1(cuLaunchKernel + 0x2fe)[0x282a4e] =========主机框架:./全部[0x1dac1] =========主机框架:./全部[0x382d3] =========主机框架:./全部[0x9508] =========主机框架:./全部[0x93c0] =========主机框架:./全部[0x942d] =========主机框架:./全部[0x8d7a] =========主机框架:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main + 0xf0)[0x20840] =========主机框架:./全部[0x2999] ========= ========= CUDA API调用“未指定启动失败”导致程序命中cudaErrorLaunchFailure(错误4) cudaDeviceSynchronize。 =========保存的主机回溯到错误时的驱动程序入口点 =========主机框架:/usr/lib/i386-linux-gnu/libcuda.so.1 [0x391b13] =========主机框架:./全部[0x3c2c6] =========主机框架:./全部[0x8d83] =========主机框架:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main + 0xf0)[0x20840] =========主机框架:./全部[0x2999] ========= =========由于对cudaPeekAtLastError的CUDA API调用上的“未指定的启动失败”,程序命中了cudaErrorLaunchFailure(错误4)。 =========保存的主机回溯到错误时的驱动程序入口点 =========主机框架:/usr/lib/i386-linux-gnu/libcuda.so.1 [0x391b13] =========主机框架:./全部[0x39b93] =========主机框架:./全部[0x8d88] =========主机框架:/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main + 0xf0)[0x20840] =========主机框架:./全部[0x2999] ========= =========错误摘要:4个错误

在我的代码中,最多计算19位数字组合的可能性总数。此数字确定线程总数。可能性由(2^n)-1计算,因此,如果我选择9位数字,则为511,因此该过程将总共执行511个线程。

尽管对于内核配置,我选择的块大小为128,但我也给出了可能性的数量(nPossibilities)作为参数,并且在内核中,我执行以下操作:

if (idx > 0 && idx < nPossibilities)
{
 //Do something
}

在服务器上,代码最多处理15位数字,相当于32,767。 16及以上会导致问题中出现错误。对于16它将是65,536。这是否意味着对于正在运行的Titan Xp〜32,000个线程,需要1GB的堆,而在此之上我需要分配更多的线程?但是对于19位数字,我总共需要524,287个线程!哪个好多!那么1GB足以容纳约32,000个线程,而2GB足以容纳约524,000个线程?

我在内核中使用new分配的变量的大小也取决于位数。我粗略计算了分配变量的大小,对于15位数字,它是0.032MB,对于16位0.034MB,对于19位0.0415MB

解决方法

因为Titan Xp比960M支持更多的“运行中”线程。

大概在您的CUDA设备代码中,您正在执行类似mallocnew的操作(希望也执行freedelete)。它们从设备堆中分配出来,设备堆的大小由您为此使用的CUDA运行时API调用控制:cudaDeviceSetLimit(cudaLimitMallocHeapSize,1024 * 1024 * 1024);

这两个GPU均可在给定时间运行的最大线程数由2048 * SM数给出。即使您的特定代码的占用率小于每个SM 2048,该数目(每个SM的最大占用线程数)也可能在960M或Titan Xp上相同。

因此,正在运行的线程总数由SM的数量确定。 960M具有5个SM,因此它在飞行中(即在执行的某个阶段)最多可以有2048x5 =〜10,000个线程。 Titan Xp有30个SM,因此在飞行中可以有2048x30 =〜60,000个线程。这意味着,如果每个线程都执行特定大小的malloc,然后再执行free,则在960M上的任何时间点上可能有10,000个未完成的分配,但是其中有60,000个未完成在Titan Xp上的任何时间。更多未完成的分配=对(设备堆)内存的更多需求。

在Titan Xp和960M上,您很有可能需要更多的可用空间来堆放设备。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...