std :: unordered_map分配,插入和释放时间

时间:2015-03-10 17:37:58

标签: c++ c++11 stl

我在文件中有大约4百万个值,我希望将其存储在容器中以执行计算。

每个值的键由2个无符号整数组成 该值是一个包含4个双数的结构。

加载后值不会改变。

typedef pair<unsigned int, unsigned int> aa;
struct MyRecord { double a1; double a2; double a3; double a4; };

class MyRecordHash{
public:
    size_t operator()(const aa &k) const{   return k.first * 10000 + k.second;      }
};

struct MyRecordEquals : binary_function<const aa&, aa&, bool> {
  result_type operator()( nm lhs, nm rhs ) const
  {
    return (lhs.first == rhs.first) && (lhs.second == rhs.second);
  }
};     

std::unordered_map<aa,MyRecord,MyRecordHash,MyRecordEquals> MyRecords;

我在插入记录之前使用MyRecords.reserve(number_of_records)。

问题A:虽然我在开始插入数据之前调用了reserve,但是分配的内存不足,并且在插入数据时不断重新分配越来越多的内存。难道它不能用保留分配所需的内存吗?例如,对于4m记录,它分配保留38.9Mb,然后插入额外的256.5Mb。

问题B:插入过程相当慢。我检查了负载系数,它从未增加超过0.5。还有什么建议可以检查吗?我使用MyRecords.insert进行插入。

问题C:完成计算后,我打电话给MyRecords.clear()。而不是立即删除内容&#34;&#34;它开始按记录删除记录(约3Mb /秒)。如果我不打电话给clear()我会得到同样的行为。这是正常的吗?我检查了所有以前的stackoverflow问题,我发现的唯一建议是它可能与调试有关。我使用了-O3选项,但它没有改变任何东西。

我正在使用MinGW-64编译器4.9.1版本。

感谢大家阅读本文和您的建议。

在提出意见和解决方案之后编辑:

- 当使用除标准类型以外的密钥和包含的数据时,似乎没有办法释放或预分配STL的内存以用于unordered_maps。 - Reserve方法,仅为哈希值保留内存。 - 使用向量&lt;&gt;使用从值的键计算的索引非常好。只需预先分配矢量,然后使用myvector.at()= value,设置值。默认析构函数几乎立即释放向量(使用unordered_map时,4m值需要2-3秒而不是5分钟)。 - 由于没有存储密钥,因此使用向量的内存使用较少 - 对矢量的随机访问似乎有点慢,但还没有对代码进行分析。

再次感谢大家的帮助。

3 个答案:

答案 0 :(得分:1)

所有unordered_map::reserve都会增加存储区的数量,以便在插入指定数量的元素时不会超过最大加载因子。那不会帮助你。

unordered_map是一个基于节点的容器;因此,每次插入都是单独的分配。您的数据结构的析构函数是微不足道的,但释放400万块内存非常昂贵。

你可以

  • 使用可有效处理分配模式的自定义分配器
  • 或切换到不同的数据结构。 boost::flat_map是一个不错的选择(稍微增加的时间复杂度可能会被更好的数据位置的性能提升所抵消)。

答案 1 :(得分:0)

reserve可能只为哈希结构(例如指向数据的指针)而不是数据本身分配空间。

以插入4M记录为例。每条记录是4个双打或4 * 8个字节。 4M记录意味着4 * 8 * 4 = 128M字节的数据。显然,38.9Mbyte的reserve()分配是不够的。

答案 2 :(得分:0)

取自评论......


我想我会问这个问题(以不同的方式看待事物),你确定需要一个关联容器吗?

如果您的条目几乎涵盖了所有组合键,那么如果你没有为未使用的条目浪费一点空间,那么可能会有一个向量。因此,将您的密钥视为向量中的索引。这将为您提供恒定的时间查找,并允许您预先分配所有必需的内存,从而避免多次分配的成本。

此方法的值取决于密钥空间中密钥的分布以及它们映射到基于零的数组索引的容易程度。

如果您尝试这种方法,我会非常有兴趣了解这相对于您目前正在做的事情的表现。