tl:dr我需要一种方法来更好地管理C ++中的内存,同时保留大型数据集。
我目前正在创建一个程序,输出我以后需要的数据库,我正在努力进行内存控制。我将程序写入功能级别,输出我需要的小规模数据集,但是为了将大小增加到我需要的位置并保持现实,我需要增加迭代次数。问题是,当我这样做时,我最终耗尽了计算机内存(4gb),并且必须开始页面打印,这会大大减慢处理速度。
基本概要是我正在创建商店,然后为所述商店创建一年的交易数据。创建商店时,会生成一个数字列表,表示交易的每日销售目标,然后随机生成交易,直到达到该数量。这种方法给出了一些很好的有机结果,我很满意。不幸的是,所有这些事务都必须存储在内存中,直到它们输出到我的文件中。
创建事务时,它们会暂时存储在一个向量中,我将矢量副本存储在永久存储位置后执行.clear()。
我已经开始尝试移动到unique_ptr用于临时存储,但我不确定它们是否在从生成数据的函数返回时被正确删除。
代码是这样的(我删除了一些与手头问题无关的多余代码)
void store::populateTransactions() {
vector<transaction> tempVec;
int iterate=0, month=0;
double dayTotal=0;
double dayCost=0;
int day=0;
for(int i=0; i<365; i++) {
if(i==dsf[month]) {
month++;
day=0;
}
while(dayTotal<dailySalesTargets[i]) {
tempVec.push_back(transaction(2013, month+1, day+1, 1.25, 1.1));
dayTotal+=tempVec[iterate].returnTotal();
dayCost+=tempVec[iterate].returnCost();
iterate++;
}
day++;
dailyTransactions.push_back(tempVec);
dailyCost.push_back(dayCost);
dailySales.push_back(dayTotal);
tempVec.clear();
dayTotal = 0;
dayCost = 0;
iterate = 0;
}
}
transaction::transaction(int year, int month, int day, double avg, double dev) {
rng random;
transTime = &testing;
testing = random.newTime(year, month, day);
itemCount = round(random.newNum('l', avg, dev,0));
if(itemCount <= 0) {
itemCount = 1;
}
for(int i=0; i<itemCount; i++) {
int select = random.newNum(0,libs::products.products.size());
items.push_back(libs::products.products[select]);
transTotal += items[i].returnPrice();
transCost += items[i].returnCost();
}
}
答案 0 :(得分:3)
您遇到内存问题的原因是因为当您向向量添加元素时,它最终必须调整其内部缓冲区的大小。这需要分配一个新的内存块,将现有数据复制到新成员,然后删除旧缓冲区。
由于你知道向量将在手持的元素数量,你可以调用向量reserve()
成员函数来提前分配内存。这将消除你现在无疑遇到的不断调整大小。
例如,在transaction
的构造函数中,您将在将数据添加到向量的循环之前执行以下操作。
items.reserve(itemCount);
在store::populateTransactions()
中,您应该计算向量将保留的元素总数,并在上面描述的相同内容中调用tempVec.reserve()
。另请注意,如果您使用局部变量来填充矢量,则最终需要复制它。这将导致相同的问题,因为目标向量在复制内容之前需要分配内存(除非您使用C ++ 11中提供的移动语义)。如果需要将数据返回给调用函数(而不是store
的成员变量),则应该通过引用将其作为参数。
void store::populateTransactions(vector<transaction>& tempVec)
{
//....
}
如果提前确定元素数量是不切实际的,您应该考虑使用std::deque
。来自cppreference.com
与std :: vector相反,deque的元素不是连续存储的:典型的实现使用一系列单独分配的固定大小的数组。
双端队列的存储会根据需要自动扩展和收缩。扩展deque比std :: vector的扩展便宜,因为它不涉及将现有元素复制到新的内存位置。
关于Rafael Baptista关于调整大小操作如何分配内存的注释,以下示例应该可以让您更好地了解它的作用。列出的内存量是调整大小时所需的数量
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> data;
for(int i = 0; i < 10000001; i++)
{
size_t oldCap = data.capacity();
data.push_back(1);
size_t newCap = data.capacity();
if(oldCap != newCap)
{
std::cout
<< "resized capacity from "
<< oldCap
<< " to "
<< newCap
<< " requiring " << (oldCap + newCap) * sizeof(int)
<< " total bytes of memory"
<< std::endl;
}
}
return 0;
}
使用VC ++ 10编译时,向向量添加1,000,001个元素时会生成以下结果。这些结果特定于VC ++ 10,并且可以在std::vector
的实现之间变化。
将容量从0调整为1,需要4个总字节的内存
调整容量从1到2,需要12个总字节的内存
将容量从2调整为3,需要20个总字节的内存 调整容量从3到4需要28个总字节的内存
调整容量从4到6需要40个总字节的内存
调整容量从6到9,需要60个字节的内存
调整容量从9到13需要88个总字节的内存
将容量从13调整为19,需要128个总字节的内存...略...
将容量从2362204调整为3543306,需要23622040个内存总字节数 调整容量从3543306调整到5314959需要35433060总字节的内存
将容量从5314959调整为7972438,需要53149588个内存总字节数 将容量从7972438调整为11958657,需要79724380个总内存字节
答案 1 :(得分:1)
这很有趣!我能想到的一些快速评论。
一个。 STL clear()并不总是瞬间释放内存。相反,您可以使用std::vector<transaction>().swap(tmpVec);
。
湾如果您使用的是具有C ++ 11 vector :: emplace_back的编译器,那么您应该删除push_back并使用它。它应该是内存和速度的一大提升。使用push_back,你基本上有两个相同数据的副本,你可以使用分配器将它返回给操作系统。
℃。有什么理由你不能每隔一段时间将dailyTransactions刷新到磁盘上吗?你总是可以序列化向量并将其写入磁盘,清除内存,你应该再次恢复正常。
d。正如其他人所指出的那样,储备也应该有很多帮助。