【问题标题】:Vector of 20,000 small objects vs vector of 20,000 object pointers to 20,000 heap objects20,000 个小对象的向量与 20,000 个指向 20,000 个堆对象的对象指针的向量
【发布时间】:2011-12-29 17:24:48
【问题描述】:

在 OS X Snow Leopard 下开发 32 位 C++/carbon 应用程序时遇到了一个问题,即大约 20,000 个小对象(每个 72 字节)的 stl 向量在重新分配期间失败。看起来这个大小为几兆字节的向量无法扩展为一块连续的内存,在发生故障时它只有 1.2 MB 大小。

GuardMalloc[Appname-33692]: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region

GuardMalloc[Appname-35026]: Failed to VM allocate 894752 bytes
GuardMalloc[ Appname-35026]: Explicitly trapping into debugger!!!

#0    0x00a30da8 in GMmalloc_zone_malloc_internal
#1    0x00a31710 in GMmalloc
#2    0x94a54617 in operator new
#3    0x0026f1d3 in __gnu_cxx::new_allocator<DataRecord>::allocate at new_allocator.h:88
#4    0x0026f1f8 in std::_Vector_base<DataRecord, std::allocator<DataRecord> >::_M_allocate at stl_vector.h:117
#5    0x0026f373 in std::vector<DataRecord, std::allocator<DataRecord> >::_M_insert_aux at vector.tcc:275
#6    0x0026f5a6 in std::vector<DataRecord, std::allocator<DataRecord> >::push_back at stl_vector.h:610

我能想到几个策略:

1) Reserve() 是一个非常非常大的向量,一旦应用程序启动。但是,这假设用户可能不会加载有助于该向量的其他文件,从而将其推到超出预先分配的限制并可能回到相同的情况。

2) 将对象/内存分配的向量更改为指向对象/内存分配的指针向量。显然使向量本身的大小更易于管理,但随后会创建 20,000 个小对象(最终可能会变成 50,000 个对象,具体取决于用户加载的其他文件)。这会产生巨大的开销问题吗?

3) 从向量变为列表,这可能有其自身的开销问题。

向量不断迭代,通常只附加到。

对这些问题有什么明智的想法吗?

================

附加说明:此特定向量仅包含所有导入的记录,因此它们可以通过包含排序顺序的另一个向量进行索引和排序。将项目放入此向量后,它会在应用程序的整个生命周期内一直存在(还有助于支持撤消操作,确保向量中的索引对于该特定对象始终保持不变)。

【问题讨论】:

  • 任何让它成为 64 位应用程序的机会 - Carbon 已贬值,因此您需要在合理的短时间内进行更改。
  • @Mark:你的意思是“弃用”吗?我想碳也在贬值。
  • 你能把向量拆分成多个向量吗? vector&lt;vector&lt;A&gt;*&gt; VoV[100]; 填充 Vov[0]push_back 失败。开始填写VoV[1]。等等。这合适吗?或者处理多个结构中的数据(排序、搜索……)的工作量太大。顺便说一句,您可以创建自己的函数来这样做......我太乐观了
  • @outis - 不,不是!你看到汽油的价格了吗?
  • @user516545(您可能想选择一个有意义的名称):对集合的各种操作(例如添加、删除、随机访问、迭代)的相关重要性是什么?对象是否有自然排序?多一点 background detail 会有所帮助。

标签: c++ macos optimization memory-management stl


【解决方案1】:

在您的情况下,我认为std::dequestd::liststd::vector 更合适。 std::list 在迭代或随机索引方面效率不高,而 std::vector 调整大小的速度很慢(如您所见)。 std::deque 在调整大小时不需要大量内存,代价是随机索引比向量稍慢。

【讨论】:

  • 虽然list 在相对于vector 进行迭代时可能存在缓存局部性问题,但真正的问题是您是否需要索引list,而不是对其进行迭代。
【解决方案2】:

不要使用像vector 这样的连续存储。选择dequelist,重新分配将不再失败。

如果你真的需要高性能,可以考虑编写自己的容器(即 ArrayList)。

【讨论】:

  • std::deque 是个好主意,std::list 行不通,因为他需要按索引访问记录。
  • 你对ArrayList的表现有什么建议?对于std::deque 的性能,您有什么建议?
【解决方案3】:

在您的三个选项中,1 似乎不是一个有保证的解决方案,而 2 增加了复杂性并且向量仍然需要增长。

选项 3 似乎有些合理(或者可能使用另一个答案中提到的 deque),因为虽然它在语义上类似于选项 2,但它提供了一种更规范化的分配新数据对象的方法。当然,这假设您只附加数据并且不需要随机访问。

然而,我发现令人难以置信的是,您的程序内存碎片如此严重,以至于在相当现代的硬件上它无法分配 1.2MB 的内存。更有可能的是,您的程序中潜伏着一些未定义的行为(或者可能是内存泄漏),导致它以这种方式运行,无法分配内存。你可以使用 valgrind 来帮助寻找可能发生的事情。当您使用内置的newdelete 而不是GMalloc 时,是否会出现同样的问题?

您的程序是否被ulimited 只能访问少量内存?

最后,如果 valgrind 什么也没找到,而你的程序确实严重地碎片化了内存,我会考虑退后一步,重新考虑你的方法。您可能想要评估一种不依赖于数百万(?)分配的替代方法(我只是看不到少量分配对堆造成如此多的碎片)。

【讨论】:

  • ulimit 在 OS X 下被限制为最大文件大小。关于碎片,这是我的第一个想法,但尽我所能告诉内存碎片不是 OS X 上内存分配方式的问题。
【解决方案4】:

如果即使在堆中,也没有足够的连续空间,请使用deque; deque 在需要时分配不连续的空间。所以它可以处理 1.2 MB 的限制

deque 由一定数量的内存块组成,而不仅仅是一个连续的空间。这就是为什么它可以工作。但不确定(/完全安全),因为您无法控制双端队列的行为。

参见thisabout 内存碎片(以下是从网页复制/粘贴): http://www.design-reuse.com/articles/25090/dynamic-memory-allocation-fragmentation-c.html

内存碎片

理解内存碎片的最好方法是看一个例子。对于这个例子,假设有一个 10K 的堆。首先,请求一个 3K 的区域,因此:

     #define K (1024)
     char *p1;
     p1 = malloc(3*K);

然后,请求进一步的 4K:

    p2 = malloc(4*K);

现在有 3K 内存可用。

一段时间后,由 p1 指向的第一个内存分配被取消分配:

    free(p1);

这会在两个 3K 块中留下 6K 的可用内存。发出进一步的 4K 分配请求:

   p1 = malloc(4*K);

这会导致失败——NULL 被返回到 p1——因为即使 6K 的内存可用,也没有 4K 的连续块可用。这是内存碎片。

即使对于使用 osx 等虚拟内存的内核也是一个问题。

【讨论】:

  • outis:你没有社交能力吗?批判性思维。
  • 这与个人无关。 SO 有一个非常具体的格式:Q&A。如果您不明白我之前的评论为何相关,请阅读常见问题解答。
  • 您之前的评论不相关。我回答了这个问题。 “我投票支持……”这句话的开头是我的善意公式。这是我没有赌博的证据。我给出的答案是正确的。
  • 考虑到编辑,它与目前的答案无关,但它仍然与什么才是对 SO 的适当答案相关。此外,当我发布之前的两个 cmets 时,它是相关的。 “我投票赞成”不会在英语中表现出善意。它在对冲你的答案,所以你不必支持它。如果您的回答背后有足够充分的理由,则不必equivocate
  • 你的头脑不好。我支持我所有的答案,因为我给出了代码和解释。对于一个非坏的头脑:当我说“我投票支持......”时,我完全投入到我的回应中。浪费我的时间和耐心就够了。
猜你喜欢
  • 1970-01-01
  • 2011-10-01
  • 2011-02-11
  • 1970-01-01
  • 2011-12-19
  • 2021-07-15
  • 1970-01-01
  • 1970-01-01
  • 2023-02-09
相关资源
最近更新 更多