【问题标题】:Best STL containers to avoid heap fragmentation避免堆碎片的最佳 STL 容器
【发布时间】:2016-07-04 16:43:57
【问题描述】:

我有一个分析 150,000 个文件的程序。 Valgrind 报告没有内存泄漏,但程序会随着时间的推移而变慢。

一些问题与过于频繁地使用 std::string 和 mktime 花费太多时间有关。 (见C++ slows over time reading 70,000 files

但随着时间的推移,它仍然会变慢。 Lotharyx 建议容器使用导致堆碎片。

我阅读了关于不同 STL 容器优缺点的各种流程图,但我不太明白。

在下面的伪代码中,我不确定我是否做出了正确的选择来避免堆碎片。

fileList.clear()
scan all disks and build "fileList", a std::set of file paths matching a pattern.

// filepaths are named by date, eg 20160530.051000, so are intrinsically ordered 

foreach(filePath in fileList)
{
    if (alreadyHaveFileDetails(filePath))
        continue;

    // otherwise
    collect file details into a fileInfoStruct;  // like size, contents, mod 

    fileInfoMap[time_t date] = fileInfoStruct;
}

// fileInfoMap is ordered collection of information structs of about 100,000 files

// traverse the list in order
foreach (fileInfo in fileInfoMap)
{
    if (meetsCondition(fileInfo))
    {
        TEventInfo event = makeEventInfo()
        eventList.push_back(event);
    }
}

并且上述序列永远重复。

所以对于容器的选择,我使用过(或需要):

fileList -- 包含 150,000 个路径名的唯一字符串列表。
我选择 std::set 是因为它会自动处理重复项并自动维护排序顺序。 没有随机访问,只添加条目,对它们进行排序(手动或自动),然后迭代它们。

fileInfoMap -- 由与文件日期相对应的 time_t 时间戳键入的结构数组。 我选择了std::map。它也将有 150,000 个条目,因此会占用大量内存。 没有随机访问,只将条目添加到一端。必须遍历它们,并在必要时从中间删除条目。

eventList——“事件”结构的小列表,比如 50 个项目。 我选择了 std::vector。不知道为什么真的。 没有随机访问,只在一端添加条目,然后遍历集合。

我对 C++ 还很陌生。感谢您的考虑。

【问题讨论】:

  • 我不相信你的烦恼是由碎片引起的……
  • 如果不是碎片那又怎样?
  • 这是在调试版本的 Windows 上吗?
  • @Danny 我给你留下了关于你上一个问题的答案。
  • Centos 6 与 3.10 内核。用 -g 编译

标签: c++ stdvector heap-fragmentation


【解决方案1】:

关于内存管理,容器属于两大家族:一类是一起分配所有元素,一类是单独分配元素。

vector 和 deque 属于第一个家族,list、set 和映射到第二个家族。

在不支持全局重定位的容器中不断添加和删除元素时,会出现内存碎片。

避免该问题的一种方法是使用vectors,使用“reserve”来预测内存需要减少重定位,并在插入时保持数据排序。

另一种方法是使用“基于链接的容器”(如列表、集合等)为它们提供一个分配器,从更大的块中分配内存,回收它们而不是为每个单个元素插入/删除调用原始 malloc/free。

看看std::allocator

您可以轻松编写分配器,方法是从 std::allocator 派生并覆盖 allocate/deallocate 函数,添加所有必需的逻辑,并将 yourallocator 作为您想要的容器的可选模板参数传递使用。

【讨论】:

  • 同意自定义分配器的建议。
  • deque 不会将其所有元素一起分配。
  • @NicolBolas:是的,这有点半途而废。但比列表或设置更好。我把它和向量放在一起是因为它不是在纯 1/1 基础上分配的。
  • 感谢您的建议。你是什​​么意思“不支持全球搬迁的容器”?谷歌在这个词上一无所获。哪些容器支持它?我正在运行更多测试,但怀疑 std::map fileInfoMap 是问题...
  • @Danny:向量的所有元素都分配在同一个内存块中。如果发现它不够宽,则分配另一个更宽的块,移动元素,并释放旧的内存块。列表、映射、集合等基于指针链接节点(形成双着墨列表或 B&R b 树),每个节点包含一个元素(或键值对),单独分配。 “使那些节点留在同一个块内”的工作不是由容器完成的,默认的 std::allocator 也没有完成(它只做一个 malloc/free 的单个节点)
猜你喜欢
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 2011-08-29
  • 1970-01-01
相关资源
最近更新 更多