了解每个线程的cuda堆内存限制

时间:2013-07-29 16:18:06

标签: cuda heap-memory

这个问题是关于cuda的堆大小限制。 访问过有关此主题的一些问题,包括以下问题: new operator in kernel .. strange behaviour 我做了一些测试。鉴于内核如下:

#include <cuda.h>
#include <cuda_runtime.h>
#define CUDA_CHECK( err ) __cudaSafeCall( err, __FILE__, __LINE__ )
#define CUDA_CHECK_ERROR()    __cudaCheckError( __FILE__, __LINE__ )
inline void __cudaSafeCall( cudaError err, const char *file, const int line )
{
    if ( cudaSuccess != err )
    {
        fprintf( stderr, "cudaSafeCall() failed at %s:%i : %s\n",
                file, line, cudaGetErrorString( err ) );
        exit( -1 );
    }
    return;
}

inline void __cudaCheckError( const char *file, const int line )
{
    cudaError err = cudaGetLastError();
    if ( cudaSuccess != err )
    {
        fprintf( stderr, "cudaCheckError() failed at %s:%i : %s\n",
                file, line, cudaGetErrorString( err ) );
        exit( -1 );
    }
    return;
}
#include <stdio>
#define NP 900000
__device__ double *temp;
__device__ double *temp2;
__global__
void test(){
int i = blockDim.x*blockIdx.x + threadIdx.x;
if(i==0){
    temp = new double[NP];
    //temp2 = new double[NP];
}

if(i==0){
    for(int k=0;k<NP;k++){
        temp[i] = 1.;
        if(k%1000 == 0){
            printf("%d : %g\n", k, temp[i]);
        }
    }
}
if(i==0){
    delete(temp);
    //delete(temp2);
}
}
int main(){
    //cudaDeviceSetLimit(cudaLimitMallocHeapSize, 32*1024*1024);
    //for(int k=0;k<2;k++){
        test<<<ceil((float)NP/512), 512>>>();
        CUDA_CHECK_ERROR();
    //}
    return 0;
}

我想测试堆大小限制。

  1. 使用一个大小为的线程动态分配一个数组(temp) 大约超过960,000 * sizeof(双倍)(接近8MB,这是 堆大小的默认限制)给出错误:ok。 900,000件作品。 (有人知道如何计算真实限制吗?)
  2. 增加堆大小限制允许分配更多内存:normal,ok。
  3. 回到8MB堆大小,每个线程用两个线程分配一个数组(所以,用if(i == 0 || i == 1)替换if(i == 0),每一个900,000 * sizeof(双倍)失败。但每个450,000 * sizeof(双),仍然可以。还好。
  4. 这就是我的问题:用一个线程分配两个数组(因此,线程0的temp和temp2),每个900,000 * sizeof(double)也可以,但它不应该?实际上,当我尝试在两个数组中编写时,它都会失败。但是,当使用两个带有一个线程的数组而不是两个带有两个线程的数组时,任何人都知道为什么这种不同的分配行为?
  5. 编辑:另一个测试,我发现那些像我一样学习堆的用法的人会感兴趣:  5.执行内核两次,其中一个大小为900,000 * sizeof(double)的数组由单个线程0分配,如果有删除则有效。如果省略delete,它将第二次失败,但第一次调用将被执行。

    编辑2:如何分配设备范围的变量一次但是所有线程都可写(不是来自主机,使用设备代码中的动态分配)?

1 个答案:

答案 0 :(得分:2)

可能您没有在new操作上测试返回的空指针,这是一个valid method in C++ for the operator to report a failure

当我按如下方式修改你的代码时,我收到消息“第二次新的失败”:

#include <stdio.h>

 #define NP 900000
__device__ double *temp;
__device__ double *temp2;
__global__
void test(){
int i = blockDim.x*blockIdx.x + threadIdx.x;
if(i==0){
    temp = new double[NP];
    if (temp == 0) {printf("first new failed\n"); return;}
    temp2 = new double[NP];
    if (temp2 == 0) {printf("second new failed\n"); return;}
}

if(i==0){
    for(int k=0;k<NP;k++){
        temp[i] = 1.;
        if(k%1000 == 0){
            printf("%d : %g\n", k, temp[i]);
        }
    }
}
if(i==0){
    delete(temp);
    delete(temp2);
}
}

int main() {

  test<<<1,1>>>();
  cudaDeviceSynchronize();
  return 0;
}

如果您提供完整的,可编译的代码,供其他人使用,就像我一样,这很方便。

对于您的第一个EDIT问题,如果删除第一个新内容,第二个新内容将起作用就不足为奇了。第一个几乎分配了所有可用的8MB。如果删除该分配,则第二个将成功。参考documentation,我们看到以这种方式动态分配的内存在cuda上下文的整个生命周期中存在,或直到执行相应的删除操作(即不仅仅是单个内核调用。内核的完成)不一定免费分配。)

对于您的第二个EDIT问题,您已经使用__device__ double *temp;指针演示了一个方法,一个线程可以通过该指针分配所有线程都可以访问的存储。但是,跨块会出现问题,因为无法保证块之间的同步顺序或块之间的执行顺序,因此如果从块0中的线程0分配,则仅在块0在其他块之前执行时才有用。您可以想出一个复杂的方案来检查变量分配是否已经完成(可能通过测试指针为NULL,也可能使用原子),但它会创建脆弱的代码。最好提前规划全局分配并从主机进行相应分配。