C++创建巨大的向量

4

我想运行一个进程,其中需要有一个std::vectorstd::tuple<long unsigned int, long unsigned int>。我正在进行的测试应该创建一个大约为47,614,527,250(约47亿)个元组的向量,但实际上在创建时就会崩溃,并显示错误消息terminate called after throwing an instance of 'std::bad_alloc'。我的目标是使用大约两倍大小的向量运行此脚本。下面是代码:

arc_vector = std::vector<std::tuple<long unsigned int, long unsigned int>>(arcs);

这里的arcs是一个被引用数值的long unsigned int

我能否并且如何增加内存大小?这个脚本正在一台拥有40个核心和大约200GB内存的机器上运行,所以我知道内存本身不是问题。


4
你确定你的操作系统允许你连续地分配470亿个元素的空间吗?即使操作系统允许这样做,你确定内存中有足够大的连续空间来容纳这么多数据吗?(如果出现异常,这意味着答案是否定的) - UnholySheep
9
你需要拥有 sizeof(std::tuple<long unsigned int, long unsigned int>) 乘以 470 亿字节的连续内存。在64位机器上(带有64位的 long),这相当于超过750 GiB的内存作为一个单一可用块。 - Some programmer dude
5
根据我的粗略估算,你的计算机需要大约800GB的RAM。 - Eljay
2
总的来说,您可能需要重新考虑您的算法以及同时将所有元素保存在内存中的必要性。 - Some programmer dude
3
如果你的遗留库API需要一个近100亿元素的数组,那么我怀疑你可能误解了该库的使用方式。你建立一个std::tuple向量作为旧库参数的事实也令人生疑。 - François Andrieux
显示剩余5条评论
2个回答

11

470亿个元组,每个元组占16字节,总共需要7800亿字节,大约为760GB。你的机器内存少于所需的三分之一,因此无论程序崩溃的原因是什么,你都需要另一种方法。

我的建议是使用一个1TB的内存映射文件来存储该数组,如果你确实需要使用标准向量作为接口,你可以编写一个自定义分配器来使用映射内存。这样可以在某种程度上透明地解决缺乏主存的问题。如果你的接口要求标准向量和标准分配器,则最好重新设计。

另外,检查一下运行进程的用户的ulimit值,因为它可能有比760GB更严格的虚拟内存限制。


不错的解决方案,但很可能会非常慢...而且根据程序的功能,它可能只是一个已经存在于磁盘上的数据的简单复制。在这种情况下,直接修改输入文件中的各个值可能是更好的解决方案。 - Sander
2
@Sander 它是否会变慢完全取决于算法的操作。请注意,由于您的工作集大于主内存,因此无论如何都需要进行一些磁盘访问。如果您的算法可以以流式方式执行,或者具有某种可预测的内存访问模式,则通过显式实现可能会获得更好的结果,但在大多数情况下,您最终将实现一个较差的缓存机制或磁盘数据结构,而操作系统可能可以更好地处理:与流io相比,内存映射非常快速。 - pqnet
或者只需添加必要的交换,可以非常容易地尝试。 - geza

4
你可能拥有一台内存很大的计算机,但问题是你需要这些内存是连续的。

即使使用内存虚拟化,也不太可能实现。

对于这么多数据,你需要使用不同的存储容器。你可以基于链表的向量列表来划分数据,或者一个指向元组子向量的指针向量,或者找到一个已经构建了这样结构的库。


2
std::deque 怎么样? - François Andrieux
std::deque 的元素访问复杂度是常数级别的。然而,基于链表的解决方案可能会存在访问时间问题。 - François Andrieux
6
我认为连续内存并不是一个问题,因为你永远不会在一个页面中分配750G。操作系统可能需要将其拆分为多个页面,因此地址在虚拟地址空间中是连续的,但在实际地址空间中不是连续的。 - Tyker
2
你为什么认为连续的内存是问题所在?在64位机器上,地址空间非常巨大。即使操作系统没有使用整个64位,像通常的48位地址空间已经足够了。 - geza
1
抱歉,我不明白你所说的“现实已经足够好了”的意思。由于操作系统拒绝分配,因此OP没有足够的内存。在我看来,连续性在这里并不重要。正如我所说,48位地址空间足以容纳1TB缓冲区。 - geza
显示剩余7条评论

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