如何计算内核中动态内存分配所需的堆大小?

时间:2017-08-21 09:29:25

标签: cuda

我遇到的问题是,如果我将CUDA堆大小设置为我需要在内核中分配的内存总量,那么堆仍然不足以为所有分配提供服务。

这是一个代表我的用例的最小例子:

#include <stdio.h>

#define NARR 8

__global__
void heaptest(int N){
    double* arr[NARR];

    __shared__ double* arrS[NARR];

    if(threadIdx.x == 0){
        for(int i = 0; i < NARR; i++){
            arrS[i] = (double*) malloc(sizeof(double) * N);

            if(arrS[i] == NULL)
                printf("block %d, array %d is NULL\n", blockIdx.x, i);
        }            
    }

    __syncthreads();

    for(int i = 0; i < NARR; i++){
        arr[i] = arrS[i];
    }
}

size_t getHeapSizePerBlock(int N){
    return sizeof(double) * N * NARR;
}

int main(){

    int N = 4000 * 18;

    int nBlocks = 1;

    size_t myheapsize = getHeapSizePerBlock(N) * nBlocks;

    printf("set heap size to %lu\n", myheapsize);
    cudaDeviceSetLimit(cudaLimitMallocHeapSize, myheapsize);

    size_t a;

    cudaDeviceGetLimit(&a, cudaLimitMallocHeapSize);
    printf("heap size is now %lu\n", a);

    heaptest<<<nBlocks, 128>>>(N);

    cudaDeviceSynchronize();

    cudaDeviceReset();

    return 0;
}

我用nvcc V8.0.61编译。

nvcc -arch = sm_60 heaptest.cu -o heaptest

程序输出

set heap size to 4608000
heap size is now 4653056
block 0, array 7 is NULL

因此,即使堆大小大于所需的大小,它也不够大。 在这种情况下,如何正确计算所需的尺寸?

1 个答案:

答案 0 :(得分:2)

您可能无法计算应用程序堆所需的精确大小,因为您无法控制CUDA的内存管理器。就像在你拥有操作系统的地方分配CPU内存一样内存管理器,CUDA拥有自己的内存管理器。当您在堆中分配多个数组时,您无法保证它们完全符合堆的大小,可能存在一些开销。

为了举例说明,我对您的代码进行了一些小修改,以便打印malloc返回的内存地址:

printf("block %d, array %d is %p\n", blockIdx.x, i, arrS[i]);

这是我在GTX 1070上得到的东西:

block 0, array 0 is 0x102059a8d20
block 0, array 1 is 0x10205600120
block 0, array 2 is 0x1020568f280
block 0, array 3 is 0x10205738520
block 0, array 4 is 0x102057c7680
block 0, array 5 is 0x10205870920
block 0, array 6 is 0x102058ffa80
block 0, array 7 is (nil)

首先要注意的是内存位置不是(总是)连续/增加(例如,数组0&gt;数组6&gt; ...&gt;数组1),但这对我们来说并不重要。此外,如果按递减顺序减去内存地址,则不会得到传递给malloc()的大小,在您的情况下,该大小始终为sizeof(double) * N576000个字节。例如:

  

0x1020568f280 - 0x10205600120 = 586080字节(数组1)

     

0x10205738520 - 0x1020568f280 = 692896字节(数组2)

由于这些块在内存中与传递给malloc()的块大小相邻,我们可以验证确实存在一些内存块,我们无法分配576000个字节的块。在数组1和2之间,我们有一个额外的10080字节,在数组2和3之间,116896字节额外(超过块大小的20%!)。

我要做的是避免在堆上动态分配内存,而是在主机代码执行期间分配内存。但是如果你真的需要这样做,出于某种原因,我建议设置堆大小有一些开销余量,通过测试之前似乎足够。我至少会期望即使存在堆分配的一些开销,这也不应该太大,所以如果有必要,可以开始分配额外的10%并从那里上升。