在我的程序中分配统一内存。运行后,它抛出CUDA错误:内存不足,但仍有可用内存。

3

在提问之前,我已经阅读了 这个问题,它与我的问题类似。

这里我将详细介绍我的程序。

#define N 70000
#define M 1000

class ObjBox
{public:

    int oid; float x; float y; float ts};

class Bucket
{public:

    int bid; int nxt; ObjBox *arr_obj; int nO;}

int main()
{

   Bucket *arr_bkt;

   cudaMallocManaged(&arr_bkt, N * sizeof(Bucket));

   for (int i = 0; i < N; i++)

   {

       arr_bkt[i].bid = i; 

       arr_bkt[i].nxt = -1;

       arr_bkt[i].nO = 0;

       cudaError_t r = cudaMallocManaged(&(arr_bkt[i].arr_obj), M * sizeof(ObjBox));

       if (r != cudaSuccess)

       {

           printf("CUDA Error on %s\n", cudaGetErrorString(r));

           exit(0);

       }

       for (int j = 0; j < M; j++)

       {

           arr_bkt[i].arr_obj[j].oid = -1;

           arr_bkt[i].arr_obj[j].x = -1;

           arr_bkt[i].arr_obj[j].y = -1;

           arr_bkt[i].arr_obj[j].ts = -1;

        }

   }

   cout << "Bucket Array Initial Completed..." << endl;

   cudaFree(arr_bkt);

   return 0;

}

在我的主程序中,我分配了一个类型为Bucket的数组,其中包含一个嵌套数组ObjBox。数组中共有N(70000)个Bucket,每个Bucket中有M(1000)个ObjBox。我可以正常编译程序,但在运行时会出现内存不足错误,错误位于代码行cudaError_t r = cudaMallocManaged(&(arr_bkt[i].arr_obj), M * sizeof(ObjBox));
我已经尝试解决这个问题很长时间了,以下是我发现的一些要点:
1、当N较小时,如30000、40000、60000等,程序可以正常工作。也就是说,它可以在一个结构中分配这么多统一的内存;
2、仍然有很多空闲内存。在我的服务器上,有16G的主机内存和11G的GPU全局内存。但在这个程序中,Bucket数组几乎占用了所有内存。
 N * M * sizeof(ObjBox) = 70000 * 1000 * 16Byte = 1120M; 

3、值 M 几乎与内存不足错误无关;当 N 保持不变(70000),M 减少到100时,程序也会崩溃;

我的 GPU 类型是 Tesla K40c,我已向导师提出了我的问题,她将其转交给她的朋友,在她的 CUDA 版本为7.0 的 Tesla K20 上运行该程序,可以正常分配结构。

怎么样?我如何在我的 Tesla K40c 中分配结构?我的导师认为 GPU 驱动程序中可能有一些限制设置,但我还没有解决它;


通过使用您建议的统一内存,我解决了之前的问题,您能给我一些关于新问题的建议吗?@Robert - Tom Russel
1个回答

2
如果我使用一些工具对你的代码进行修改,例如这样:
#include <cstdio>
#include <iostream>

#define N 70000
#define M 1000

class ObjBox
{
    public:

        int oid; 
        float x; 
        float y; 
        float ts;
};

class Bucket
{
    public:

        int bid; 
        int nxt; 
        ObjBox *arr_obj; 
        int nO;
};

int main()
{

    Bucket *arr_bkt;
    cudaMallocManaged(&arr_bkt, N * sizeof(Bucket));

    for (int i = 0; i < N; i++) {
        arr_bkt[i].bid = i; 
        arr_bkt[i].nxt = -1;
        arr_bkt[i].nO = 0;

        size_t allocsz = size_t(M) * sizeof(ObjBox);
        cudaError_t r = cudaMallocManaged(&(arr_bkt[i].arr_obj), allocsz);
        if (r != cudaSuccess) {
            printf("CUDA Error on %s\n", cudaGetErrorString(r));
            exit(0);
        } else {
            size_t total_mem, free_mem;
            cudaMemGetInfo(&free_mem, &total_mem);
            std::cout << i << ":Allocated " << allocsz;
            std::cout << " Currently " << free_mem << " bytes free" << std::endl;
        } 

        for (int j = 0; j < M; j++) {
            arr_bkt[i].arr_obj[j].oid = -1;
            arr_bkt[i].arr_obj[j].x = -1;
            arr_bkt[i].arr_obj[j].y = -1;
            arr_bkt[i].arr_obj[j].ts = -1;
        }
    }

    std::cout << "Bucket Array Initial Completed..." << std::endl;
    cudaFree(arr_bkt);

    return 0;
}

当我在具有16Gb物理主机内存和2Gb物理设备内存的统一内存系统上使用Linux 352.39驱动程序编译并运行它时,我得到了以下结果:
0:Allocated 16000 Currently 2099871744 bytes free
1:Allocated 16000 Currently 2099871744 bytes free
2:Allocated 16000 Currently 2099871744 bytes free
3:Allocated 16000 Currently 2099871744 bytes free
4:Allocated 16000 Currently 2099871744 bytes free
5:Allocated 16000 Currently 2099871744 bytes free
6:Allocated 16000 Currently 2099871744 bytes free
7:Allocated 16000 Currently 2099871744 bytes free
8:Allocated 16000 Currently 2099871744 bytes free
9:Allocated 16000 Currently 2099871744 bytes free
....
....
....
65445:Allocated 16000 Currently 1028161536 bytes free
65446:Allocated 16000 Currently 1028161536 bytes free
65447:Allocated 16000 Currently 1028161536 bytes free
65448:Allocated 16000 Currently 1028161536 bytes free
65449:Allocated 16000 Currently 1028161536 bytes free
65450:Allocated 16000 Currently 1028161536 bytes free
65451:Allocated 16000 Currently 1028161536 bytes free
CUDA Error on out of memory    

即使设备上有大量的可用内存,它也会报告内存不足。我认为理解这一点的关键在于失败点的分配数量,而不是它们的大小。65451非常接近65535(即2^16)。考虑到运行时所做的内部内存分配,我猜测存在某种意外或故意的限制,将内存管理的总分配数限制为65535。如果您能重现此问题,我将非常感兴趣。如果可以,我将考虑向NVIDIA提交错误报告。

我同意并已向NVIDIA内部提交了一个错误报告。然而,我还不确定这是否是一个缺陷;无论如何,它似乎是一个未记录的限制。我的建议是通过重新安排程序,使其不需要超过65000个托管内存分配来解决此问题。 - Robert Crovella
@RobertCrovella:感谢确认。我从未想过编写需要65k个malloc调用的代码,但这只是我的想法 :) - talonmies
根据我的测试,问题似乎在CUDA 8.0RC中已经修复。也就是说,使用上述代码,在M=100和CUDA 8.0RC的情况下,程序将成功完成而不会抛出错误,即使调用了超过70,000次的cudaMallocManaged。我没有测试更高的限制,如果有的话。 - Robert Crovella
如果有的话,CUDA 8RC上的限制似乎比1000000更高,根据我的测试在这里。可能现在的限制只是内存空间,而不是分配数量,但我还没有证明。 - Robert Crovella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接