为高度迭代的c++程序争用内存
Wrangling memory for a highly iterative c++ program
tl:dr我需要一种在c++中更好地管理内存同时保留大型数据集的方法。
我目前正在创建一个程序,输出一个数据库,我需要为以后的项目,我正在努力与内存控制。我已经将程序编写到一个功能级别,输出我需要的小规模数据集,但是为了将大小增加到我需要的地方并保持它的真实性,我需要增加迭代的次数。问题是,当我这样做的时候,我的电脑内存(4gb)用光了,它不得不开始页面归档,这大大减慢了处理速度。
基本大纲是,我正在创建商店,然后为该商店创建一年的事务数据。当创建商店时,生成一个数字列表,表示交易的每日销售目标,然后随机生成交易,直到达到该数字。这种方法给出了一些很好的有机结果,我很高兴。不幸的是,所有这些事务都必须存储在内存中,直到它们输出到我的文件。
当事务被创建时,它们被临时存储在一个vector中,在我将vector的副本存储在我的永久存储位置之后,我对这个vector执行.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();
}
}
你遇到内存问题的原因是因为当你向向量添加元素时,它最终必须调整它的内部缓冲区的大小。这需要分配一个新的内存块,将现有数据复制到新成员中,然后删除旧的缓冲区。
由于事先知道vector将保存的元素数,因此可以调用vector的reserve()
成员函数提前分配内存。这将消除你现在无疑会遇到的不断调整大小的问题。
例如,在transaction
的构造函数中,您将在向vector添加数据的循环之前执行以下操作:
items.reserve(itemCount);
在store::populateTransactions()
中,您应该计算向量将保存的元素总数,并以上述相同的方式调用tempVec.reserve()
。还要记住,如果您使用一个局部变量来填充向量,您最终将需要复制它。这将导致与目标向量在复制内容之前需要分配内存相同的问题(除非您使用c++ 11中可用的move语义)。如果数据需要返回给调用函数(而不是作为store
的成员变量),则应该通过引用将其作为参数。
void store::populateTransactions(vector<transaction>& tempVec)
{
//....
}
如果提前确定元素的数量是不实际的,你应该考虑使用std::deque
代替。从cppreference.com
与std::vector不同,deque的元素不是连续存储的:典型的实现是使用一系列单独分配的固定大小数组。
deque的存储空间会根据需要自动扩展和收缩。deque的扩展比std::vector的扩展成本低,因为它不涉及将现有元素复制到新的内存位置。
关于Rafael Baptista关于resize操作如何分配内存的评论,下面的例子应该会让你更好地了解它是如何发生的。列出的内存量是在 resize
期间所需的的量。#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中编译时,向vector中添加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);
。
b。如果你使用的编译器有c++ 11 vector::emplace_back,那么你应该删除push_back并使用它。它应该在内存和速度上都有很大的提升。使用push_back,你基本上有两个相同数据的副本,你可以任由分配器把它返回给操作系统。
c。为什么不能每隔一段时间就将dailyTransactions刷新到磁盘?你总是可以序列化向量并将其写入磁盘,清除内存,然后你应该又好了。
d。正如其他人所指出的,储备也应该大有帮助。
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 并行块(线程清理器)之外的 OpenMP 中的争用条件;误报?
- 如何在C++中创建争用条件
- C++上的手动重置事件(来自 C#)实现:如何避免争用条件
- 作为随机数生成器的争用条件
- 设计低线程争用的多线程聊天服务器
- 智能指针析构函数争用条件
- 尽管互斥锁,线程中的争用条件
- DPDK应用程序可以用c++编写吗?如果是,如何完成?
- ThreadSanitizer 报告的数据争用
- 在C++中递增和递减全局变量时的争用条件
- 线程清理器检测到数据争用,问题出在哪里?
- 为什么此代码不创建争用条件?
- 使用独立的 c++ 程序处理用 C 编写的字符设备驱动程序
- 防止派生析构函数中的 vtable 数据争用
- __has_include() 和后续 #include 之间是否存在争用条件
- 一次加载整个缓存行以避免争用其中的多个元素
- 什么保证两个不相关的线程中的不同不相关对象没有(不可避免的)争用条件?
- 此工厂方法是否会导致争用条件?
- 为高度迭代的c++程序争用内存