用C++创建一个非常大的三维数组

4
重点是:我正在编写一种程序,它可以将手绘声波的BMP图像转换为实际声音。它运行良好,但我正在寻找一种将整个图像加载到内存的好方法。我目前使用std::vector来加载一个500 MB的图像,只看到任务管理器中进程已经分配了我的8GB RAM的很多空间,因此需要额外的分页(在大约6 GB左右停止)。我猜这是某种开销...

这里是我的问题:是否有更节省内存的方法来分配巨大的3D数组?并且是否有任何高效读取大文件的方法? (我正在等待它加载时编写这篇文章,它已经完成了大约85%,耗时约50分钟)

编辑 操作系统为64位Windows。它甚至超过了6GB,RAM对它来说太小了,因此Windows开始将数组放入分页文件中(我制作了一个小百分比指示器,并且此时分配大约80%)。

这是我用于分配的代码:

vector <vector<vector<char> > > raster_data; //declaration
raster_data.resize(width);
for(int i=0; i<width; i++)
{
    raster_data[i].resize(height);

    for(int j=0; j<height; j++)
    {
        raster_data[i][j].resize(3); //(24bpp=3Bpp)
    }
}

所以我不是通过push_back()函数来写入数据,而是像访问一个普通数组一样来访问vector:

raster_data[i][j][k] // in a 3d for loop

1
这是一种适合仅读取无符号字符数组并使用操作系统API(例如Win32上的ReadFile)的任务类型。 - marom
5
500MB如何变成6GB? - Mustafa Ozturk
2
你在使用构造函数指定项目数量还是调用了 vector.reserve() 函数?通过 push_back 填充可能会导致大量重新分配,从而导致 6GB 的内存占用。 - Severin Pappadeux
你可能想尝试使用内存映射文件。在Windows/Linux上的实现方式不同,而你没有指定使用哪个操作系统。 - Robert Jacobs
1
你可以先预留一个巨大的空间(std::vector::reserve),然后在计算完成后再缩小它(std::vector::shrink_to_fit)。这样做可能会导致内存碎片化,但只要在最后一次计算完成后进行处理即可。 - user2249683
对于大文件处理,我建议不要使用std::容器,而是使用原始字节进行操作...我不确定为什么500MB变成了6GB。即使您尝试将其加载到std::vector<unsigned char>中,这似乎也是一个非常大的开销。 - Aumnayan
1个回答

5
你所称之为3D数组更像是包含RGB元素的2D数组,而500Mb在当今并不算“非常巨大”。
你提出使用std::vector仅存储3个字节(char)的方法应该避免,因为:
  • std::vector本身消耗的内存比3个字节多得多(您可以通过检查sizeof(std::vector<char>)的结果来了解具体数值)。
  • 您分配的内存是碎片化的。
对于您的用例,即加载2D位图,传统做法是在单个块中分配内存,然后使用计算出的索引访问值。
过度简化来说,这可能是这样的:
struct rgb {            // you should check that sizeof(rgb) == 3*sizeof(char), just to be sure
  char channels[3];
};
std::vector<rgb> raster_data;
raster_data.resize(width*height);
// channel access :
raster_data[j*width+i][k] = ...;

您可以像这样在缓冲区中高效地读取整个文件内容,但您需要考虑每一行的对齐方式,而我目前无法确定它是哪种(我相信每一行都对齐在4字节边界上)。
如果您想了解如何在C/C++中处理BMP位图数据,我强烈建议您查看Handmade Hero,该视频从头开始讲解此主题,我相信该集数是:https://www.youtube.com/watch?v=ofMJUSchXwo

3
确切地说:除了三个字节的有效载荷之外,该向量需要存储两个指针大小的值。从给定的数据中,我还推断出所使用的std :: vector <>实现没有针对小内存分配进行优化,因此操作者还必须为动态分配付出代价,这是另外两个指针大小的值。在64位机器上,存储三个字节需要的空间为 4*sizeof(size_t) + 3 + alignment = 35 + alignment字节,这与操作者提供的因子12相符。 - cmaster - reinstate monica

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