【问题标题】:cuda -- out of memory (threads and blocks issue) --Address is out of boundscuda——内存不足(线程和块问题)——地址越界
【发布时间】:2012-08-28 12:55:16
【问题描述】:

我正在使用 63 个寄存器/线程,所以(最多 32768 个)我可以使用大约 520 个线程。在这个例子中我现在使用 512 个线程。

(并行性在全局 computeEHfields 函数函数内的函数“computeEvec”中。) 问题是:

1) 下面的内存检查错误。

2) 当我使用 numPointsRp>2000 时,它显示“内存不足”,但是(如果我没有做错)我计算了全局内存,没关系。

-------------------已更新------------ ---------------

我用 cuda-memcheck 运行程序,它给了我(仅当 numPointsRs>numPointsRp 时):

========= 大小为 4 的 全局 读取无效

========= 在 computeEH 字段中的 0x00000428 处

========= 块 (0,0,0) 中的线程 (2,0,0)

========= 地址 0x4001076e0 超出范围

========= ========= 无效的 global 读取大小为 4

========= 在 computeEH 字段中的 0x00000428 处

========= 块 (0,0,0) 中的线程 (1,0,0)

========= 地址 0x4001076e0 超出范围

========= ========= 无效的 global 读取大小为 4

========= 在 computeEH 字段中的 0x00000428 处

========= 块 (0,0,0) 中的线程 (0,0,0)

========= 地址 0x4001076e0 超出范围

错误摘要:160 个错误

------------编辑----------------------------

另外,有时(如果我只使用线程而不是块(我没有测试块))如果我有 numPointsRs=1000 和 numPointsRp=100 然后更改 numPointsRp=200 然后再次更改numPointsRp=100 我没有拿到第一个结果!

import pycuda.gpuarray as gpuarray
import pycuda.autoinit
from pycuda.compiler import SourceModule
import numpy as np
import cmath
import pycuda.driver as drv


Rs=np.zeros((numPointsRs,3)).astype(np.float32)
for k in range (numPointsRs): 
    Rs[k]=[0,k,0]

Rp=np.zeros((numPointsRp,3)).astype(np.float32)
for k in range (numPointsRp): 
    Rp[k]=[1+k,0,0]


#---- Initialization and passing(allocate memory and transfer data) to GPU -------------------------
Rs_gpu=gpuarray.to_gpu(Rs)
Rp_gpu=gpuarray.to_gpu(Rp)


J_gpu=gpuarray.to_gpu(np.ones((numPointsRs,3)).astype(np.complex64))
M_gpu=gpuarray.to_gpu(np.ones((numPointsRs,3)).astype(np.complex64))

Evec_gpu=gpuarray.to_gpu(np.zeros((numPointsRp,3)).astype(np.complex64))
Hvec_gpu=gpuarray.to_gpu(np.zeros((numPointsRp,3)).astype(np.complex64))
All_gpu=gpuarray.to_gpu(np.ones(numPointsRp).astype(np.complex64))


mod =SourceModule("""
#include <pycuda-complex.hpp>
#include <cmath>
#include <vector>
#define RowRsSize %(numrs)d
#define RowRpSize %(numrp)d


typedef  pycuda::complex<float> cmplx;
extern "C"{


    __device__ void computeEvec(float Rs_mat[][3], int numPointsRs,   
         cmplx J[][3],
         cmplx M[][3],
         float *Rp,
         cmplx kp, 
         cmplx eta,
         cmplx *Evec,
         cmplx *Hvec, cmplx *All)

{

    while (c<numPointsRs){
        ...         
                c++;

                }     
        }


__global__  void computeEHfields(float *Rs_mat_, int numPointsRs,   
        float *Rp_mat_, int numPointsRp,    
    cmplx *J_,
    cmplx *M_,
    cmplx  kp, 
    cmplx  eta,
    cmplx E[][3],
    cmplx H[][3], cmplx *All )
    {
        float Rs_mat[RowRsSize][3];
        float Rp_mat[RowRpSize][3];

        cmplx J[RowRsSize][3];
        cmplx M[RowRsSize][3];


    int k=threadIdx.x+blockIdx.x*blockDim.x;

      while (k<numPointsRp)  
     {

        computeEvec( Rs_mat, numPointsRs,  J, M, Rp_mat[k], kp, eta, E[k], H[k], All );
        k+=blockDim.x*gridDim.x;


    }

}
}

"""% { "numrs":numPointsRs, "numrp":numPointsRp},no_extern_c=1)


func = mod.get_function("computeEHfields")


func(Rs_gpu,np.int32(numPointsRs),Rp_gpu,np.int32(numPointsRp),J_gpu, M_gpu, np.complex64(kp), np.complex64(eta),Evec_gpu,Hvec_gpu, All_gpu, block=(128,1,1),grid=(200,1))

print(" \n")


#----- get data back from GPU-----
Rs=Rs_gpu.get()
Rp=Rp_gpu.get()
J=J_gpu.get()
M=M_gpu.get()
Evec=Evec_gpu.get()
Hvec=Hvec_gpu.get()
All=All_gpu.get()

--------GPU型号------------- ----------------------

Device 0: "GeForce GTX 560"
  CUDA Driver Version / Runtime Version          4.20 / 4.10
  CUDA Capability Major/Minor version number:    2.1
  Total amount of global memory:                 1024 MBytes (1073283072 bytes)
  ( 0) Multiprocessors x (48) CUDA Cores/MP:     0 CUDA Cores   //CUDA Cores    336 => 7 MP and 48 Cores/MP

【问题讨论】:

  • 您是否一次将所有点复制到 GPU 内存中?一个点的大小是多少?
  • 大小是 int。我在全局函数内部调用另一个(设备)函数,并在那里进行并行处理。
  • 您的问题没有提供足够的详细信息,您的块/网格大小是有效的,因此您的内核或主机代码中一定有某些东西导致了错误(您甚至没有说错误来自哪里)。
  • 但是你在做报复性的投票,这在很多情况下只是把不好的问题推到了列表的顶部。这可能有助于提升发帖者的自尊心,但它使支持开发者社区的任务(我的任务)变得更加困难。
  • @George:我不想看到数百行代码。我想看一个重现问题的简洁案例。如果你做不到,那是你考虑得不够多。最后一个错误意味着您的函数调用中的块尺寸无效。

标签: cuda pycuda


【解决方案1】:

当我使用 numPointsRp>2000 时,它显示“内存不足”

现在我们有了一些真正的代码可以使用,让我们编译它,看看会发生什么。使用 RowRsSize=2000RowRpSize=200 并使用 CUDA 4.2 工具链进行编译,我得到:

nvcc -arch=sm_21 -Xcompiler="-D RowRsSize=2000 -D RowRpSize=200" -Xptxas="-v" -c -I./ kivekset.cu 
ptxas info    : Compiling entry function '_Z15computeEHfieldsPfiS_iPN6pycuda7complexIfEES3_S2_S2_PA3_S2_S5_S3_' for 'sm_21'
ptxas info    : Function properties for _Z15computeEHfieldsPfiS_iPN6pycuda7complexIfEES3_S2_S2_PA3_S2_S5_S3_
    122432 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 57 registers, 84 bytes cmem[0], 168 bytes cmem[2], 76 bytes cmem[16]

关键数字是每个线程 57 个寄存器和 122432 字节堆栈帧。占用计算器建议,一个 512 个线程的块每个 SM 最多有 1 个块,而你的 GPU 有 7 个 SM。在您使用 pyCUDA 为输入和输出分配一个字节的内存之前,这总共提供了 122432 * 512 * 7 = 438796288 字节的堆栈帧(本地内存)来运行您的内核。在具有 1Gb 内存的 GPU 上,不难想象内存不足。您的内核有一个巨大的本地内存占用。开始考虑减少它的方法。


正如我在 cmets 中指出的,完全不清楚为什么每个线程 都需要此内核代码中输入数据的完整副本。它会导致巨大的本地内存占用,并且似乎完全没有理由以这种方式编写代码。我怀疑您可以将内核修改为以下内容:

typedef  pycuda::complex<float> cmplx;
typedef float fp3[3];
typedef cmplx cp3[3];

__global__  
void computeEHfields2(
        float *Rs_mat_, int numPointsRs,
        float *Rp_mat_, int numPointsRp,
        cmplx *J_,
        cmplx *M_,
        cmplx  kp, 
        cmplx  eta,
        cmplx E[][3],
        cmplx H[][3], 
        cmplx *All )
{

    fp3 * Rs_mat = (fp3 *)Rs_mat_;
    cp3 * J = (cp3 *)J_;
    cp3 * M = (cp3 *)M_;

    int k=threadIdx.x+blockIdx.x*blockDim.x;
    while (k<numPointsRp)  
    {
        fp3 * Rp_mat = (fp3 *)(Rp_mat_+k);
        computeEvec2( Rs_mat, numPointsRs, J, M, *Rp_mat, kp, eta, E[k], H[k], All );
        k+=blockDim.x*gridDim.x;
    }
}

它调用的主要 __device__ 函数如下所示:

__device__ void computeEvec2(
        fp3 Rs_mat[], int numPointsRs,   
        cp3 J[],
        cp3 M[],
        fp3   Rp,
        cmplx kp, 
        cmplx eta,
        cmplx *Evec,
        cmplx *Hvec, 
        cmplx *All)
{
 ....
}

并消除线程本地内存的每个字节,而完全不改变计算代码的功能。

【讨论】:

  • :所以,这是一个糟糕的设计代码,对吗?我需要修改它才能使用共享内存?你能给我一些提示(在我的代码中)吗?我的意思是根据你的经验,不花时间。另外,我有 63 个寄存器,为什么会有这样的差异?
  • :最后,您说 438796288 甚至在程序运行之前就已分配。所以,当我运行它时,所需的内存(我作为输入的矩阵)正在添加到 438796288 字节?
  • 关于内存问题,是的,它就是这样工作的。但是这个代码是一个完整的火车残骸。为什么每个线程都加载完整输入数据的本地内存副本?这太疯狂了,尤其是当任何给定线程加载的Rp_mat 的大部分内容从未使用时。为什么要有本地内存副本?为什么不直接从数组中读取?如此多的代码毫无意义,以至于我什至无法开始建议如何“修复”它。
  • :问题是我没有开始设计这段代码来使用 cuda,但是使用 c++,这就是为什么它没有使用好的并行实践。所以,我必须设计再次。非常感谢你的帮助。(你能给我一个例子来说明你的意思吗?为什么不直接从数组中读取呢?)
  • @George:我真的不在乎你的赏金。这可能是因为您对问题进行了太多编辑,以至于将其转换为社区 wiki 问题,因此我的回答也被编入了 wiki 条目,不符合声誉。看看我的编辑,然后我建议离开并再考虑一下。似乎您出于不明显的原因使事情变得过于复杂。
【解决方案2】:

使用 R=1000 然后

block=R/2,1,1 和 grid=1,1 一切正常

如果我尝试 R=10000 并且

block=R/20,1,1 和 grid=20,1 ,然后它显示“内存不足”

我不熟悉 pycuda,也没有读过你的代码 深。但是你有更多的块和更多的线程,所以它 会

  • 本地内存(可能是内核的堆栈,每个线程分配),

  • 共享内存(按块分配),或

  • 根据gridgridDim 分配的全局内存。

你可以减少堆栈大小调用

cudeDeviceSetLimit(cudaLimitStackSize, N));

(代码适用于 C 运行时 API,但 pycuda 等效项应该不难找到)。

【讨论】:

  • :你好,感谢您的帮助。我可以看到(来自 ptxas 的信息)直到大约 82000 字节堆栈帧程序运行正常。但更多,它没有。另外,减少堆栈大小给了我相同的结果。
  • :我也无法理解:我对 numPointsRp 进行并行处理,但是如果我将 numPointsRs 增加到 10000,例如它会显示“cuLaunchKernel failed: invalid value”。
  • 1x1x1 网格没有意义 - 您尝试在单个 SM 上运行所有线程。为什么必须将输入冗余复制到本地内存?代码不会以这种方式扩展,所以不要浪费时间试图用锤子和撬棍让它运行。而是尝试真正了解您的目标设备、它的内存类型、C/C++ ABI 及其限制并相应地重写您的程序(请参阅 CUDA 文档或我在 [这篇文章] 中的回答 [stackoverflow.com/questions/12172279/… 可以帮助让你开始)。
猜你喜欢
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-24
  • 1970-01-01
  • 2016-11-14
  • 2012-01-24
  • 1970-01-01
相关资源
最近更新 更多