繁体   English   中英

为高度迭代的C ++程序争用内存

[英]Wrangling memory for a highly iterative c++ program

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();
    }
}

您遇到内存问题的原因是,当您向向量添加元素时,最终必须调整其内部缓冲区的大小。 这需要分配一个新的内存块,将现有数据复制到新成员,然后删除旧缓冲区。

由于您知道向量将事先持有的元素数量,因此可以调用向量reserve()成员函数来提前分配内存。 这将消除您毫无疑问现在遇到的不断调整大小。

例如,在transaction的构造函数中,您可以在将数据添加到向量的循环之前执行以下操作。

items.reserve(itemCount);

store::populateTransactions()您应该计算向量将保存的元素总数,并tempVec.reserve()上述方法调用tempVec.reserve() 还请记住,如果您使用局部变量来填充矢量,则最终将需要复制它。 这将引起与目标向量在复制内容之前需要分配内存的相同问题(除非您使用C ++ 11中提供的移动语义)。 如果数据需要返回到调用函数(而不是作为store的成员变量),则应通过引用将其作为参数。

void store::populateTransactions(vector<transaction>& tempVec)
{
    //....
}

如果不可行提前确定元素数量,则应考虑使用std::deque代替。 来自cppreference.com

与std :: vector相反,双端队列的元素不是连续存储的:典型的实现使用一系列单独分配的固定大小的数组。

双端队列的存储会根据需要自动扩展和收缩。 扩展双端队列比扩展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总内存字节

这好有趣! 我能想到的一些简短评论。

一种。 STL clear()并不总是立即释放内存。 相反,您可以使用std::vector<transaction>().swap(tmpVec);

如果使用的编译器具有C ++ 11 vector :: emplace_back,则应删除push_back并使用它。 这应该在内存和速度上都有很大的提高。 使用push_back,您基本上可以在同一数据的两个副本之间浮动,并且您会受分配器的控制,无法将其返回给OS。

C。 有什么原因不能偶尔将DailyTransactions刷新到磁盘上吗? 您总是可以序列化向量并将其写到磁盘,清除内存,然后您应该会恢复正常。

d。 正如其他人指出的那样,储备金也应有很大帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM