std :: vector,std :: map和memory问题

时间:2010-02-21 13:28:10

标签: memory-management stl vector map

我正在编写将数据库中的行插入向量的代码。然后将矢量存储在std :: map中。这种架构允许我根据映射键对数据集(向量)进行逻辑分区。

在我的代码中,我将从std :: map中检索数据集(即向量),向其添加/删除行或在其上执行其他逻辑,然后将向量粘贴回地图(所有这些在while()循环中继续。)

我有两个问题,这两个问题都与存储在矢量中的(可能)大量元素有关。向量可以包含从几十到几万个元素的任何东西。我无法事先知道,将从数据库中检索多少条记录。因此,std :: vector的内存管理方案(即alloc / dealloc)变得非常重要,以便有效地使用内存,并避免不必要的(内存)碎片:

我的问题是:

  1. 考虑到行可以存储的潜在大量元素,理想情况下我想从内存池中分配/解除分配。但我不知道如何将std :: vector与内存池一起使用,我不知道这是否会不必要地复杂化。如果那是过度杀伤(或太复杂),那么我的另一个想法是在首次创建向量时预先分配一块固定的内存块。但这也可能过于简单化,因为从一个矢量实例到另一个矢量实例的元素数量可能差别很大 - 导致(内存)碎片等,更不用说内存的低效使用了。

    这里推荐的方法是什么?

  2. 鉴于std :: map(所有STL容器IIRC)存储了value元素的COPY,制作包含数万个元素的向量副本的前景是完全错误的。因此,我正在考虑编写一个SmartPointerMap包装器arround stl :: map,然后存储指向矢量而不是实际矢量的指针。

    我走在正确的轨道上吗?如果否,那么什么是更好的解决方案。如果是,是否有可以使用的升级库(而不是编写我的SmartPointerMap类模板)?

  3. [编辑]

    我正在使用gcc 4.4.1

    在Linux(Ubuntu 9.10)上构建

4 个答案:

答案 0 :(得分:6)

假设您使用vector作为地图的data_type而非key_type,则可以在不复制数据的情况下修改数据。 std::map::operator[]()返回对非const data_type的引用,并且从{const {{}}的非const版本返回的迭代器允许您修改数据。

如果您需要在更改数据时更改密钥,该怎么办?您可以使用std::map::find()将数据从一个向量移动到另一个向量而无需复制。

请勿忘记std::swap()在删除元素时不会减少其vector。此外,capacity()通常会分配比您需要的vector更多的capacity(),因此push_back()需要按摊销时间进行摊销。对于非常大的向量,如果您不小心,这些行为可能会显着增加程序的内存使用量。

如果你正在使用vector作为地图的key_type并且地图有非常大的键,那么指针或智能指针可能会有所帮助。但是,如果是这种情况,则必须确保不要修改其中一个映射值指向的键的内容,因为std::map不是为处理它而设计的。

至于自定义分配器的想法,首先让代码使用标准分配器,然后查看它是否足够快。使用标准分配器可能没问题。如果您的代码与标准分配器的速度不够快,那么可以查看实际花费时间的位置(可能是其他地方,如数据库代码)。如果您编写自定义分配器并且从未将其与标准分配器进行比较,那么您将无法知道自定义分配器是否实际上是一种改进;它可能比标准分配器慢得多。

答案 1 :(得分:2)

Wrt#1,GCC / Linux(ptmalloc)中的默认堆实现将对小对象使用空闲列表(也称为内存池)(上次检查时默认为<= 64字节)。如果您仍想使用自定义分配器,则Boost.Pool库的allocators满足Standard Allocator要求。正如bk1e建议的那样,之前和之后的基准测试是否值得。

当从数据库填充向量时,如果可能/实用,请尝试使用vector<T>::reserve()使向量从一开始就分配足够的内存并避免重新分配。

希望这有帮助。

答案 2 :(得分:1)

回答问题2:

using namespace std;

map<string, vector<int>* > foo;
vector<int>* pointer= foo["thekey"];

如果需要使用智能(引用计数)指针:

#include<tr1/shared_ptr.h>
using namespace std::tr1;
using namespace std;

map<string, shared_ptr<vector<int> > > foo;
shared_ptr<vector<int> > pointer= foo["thekey"];

要回答问题#1,您可以编写一个新的allocator模板类并声明您的向量使用该分配器,但我对编写分配器并不知情:

map<string, vector<int, myallocator<int> >* > foo;

特别是我不知道如何设计一个避免碎片内存池的分配器。 (但是如果你已经回答了那个部分,那么编写一个自定义分配器就可以了。)

答案 3 :(得分:0)

我将建议在C / C ++中使用更通用的方法来处理数据库查询集。

执行db查询并将行放在结构数组中。如果您没有足够的物理内存,请使用内存映射文件,该文件将返回指向结构数组基础的指针,处理器芯片上的MMU将担心在需要时驻留在内存中。

你现在可以在一个键/ s上排序()这个结构数组,它将为你提供ISAM访问,在关系宗教中是异端,但正是游标给你的。这是数据访问方法#1

现在,如果您仍然需要以其他方式查看该数据,只需创建一个或多个索引/映射,其中映射的键是适当的(列值/结构成员)数组结构,并且映射的数据值是指向该结构在存储器映射文件中的位置的指针。这提供了与db索引完全相同的功能。通过任何给定行的struct.member值为地图订阅将使您回到该行。

对于多个索引,您可以使用多个映射,指向数组中的相同结构/行,使用不同的struct.member数据作为不同索引的映射键。

我忽略了你声明的添加新行/结构的要求,因为看起来你正在从数据库中读取一行,并且实际上并不需要添加新的行/结构。 / p>

但是,如果我错了,你确实需要添加新的结构/行,那么不要分配一个大的结构数组,只需要malloc()和realloc()一个适当大小的结构数组。把它们挂在地图的数据&#34;数据&#34;上。您以这种方式失去了真正的ISAM访问权限,因为结构体不是在内存中物理上相邻存储,而是获得realloc()功能。这是一种权衡。可以通过遍历映射来模拟ISAM访问,但是它比从内存中顺序读取结构数组要慢。