我有My_Partition_Vector
个Partition
个对象的STL向量,定义为
struct Partition // the event log data structure
{
int key;
std::vector<std::vector<char> > partitions;
float modularity;
};
Partition.partitions
的实际嵌套结构因对象而异,但Partition.partitions中存储的字符总数始终为16。
因此我假设对象的总大小应该大于或等于24字节(16 + 4 + 4)。但是,对于我添加到My_Partition_Vector
的每100,000个项目,内存消耗(使用ps -aux找到)增加了大约20 MB,表示每个分区对象大约209个字节。
这增加了近9倍!?所有这些额外的内存使用来自何处? STL向量中的某种填充,还是结构?我该如何解决这个问题(并阻止它进入交换)?
答案 0 :(得分:3)
有一件事std::vector
为动态数组建模,所以如果你知道partitions
使用std::vector
总是有16个字符,那就太过分了。使用一个好的旧C风格数组/矩阵,boost::array或boost::multi_array。
为减少插入/添加元素所需的重新分配数量,因为它的内存布局约束std::vector
允许预先为一定数量的元素预分配内存(并且它是capacity()
成员函数会告诉你多少)。
答案 1 :(得分:2)
虽然我认为他可能会夸大局势,但我总体上同意DeadMG的结论,即你所做的就是在寻找麻烦。
虽然我一般都是那个看着(无论有人制造什么样的混乱)并说“不要那样做,只是使用矢量”,但这种情况可能是一个例外。你正在创造大量应该很小的物体。不幸的是,矢量通常看起来像这样:
template <class T>
class vector {
T *data;
size_t allocated;
size_t valid;
public:
// ...
};
在典型的32位机器上,已经是12个字节。由于您使用的是vector<vector<char> >
,因此外部向量将有12个字节,而对于它所拥有的每个向量,还有12个字节。然后,当您实际在向量中存储任何数据时,每个数据都需要从免费存储中分配一块内存。根据您的免费存储的实现方式,您通常具有最小块大小 - 通常为32或甚至64字节。更糟糕的是,堆通常有一些自己的开销,所以它会在每个块上添加一些更多内存,用于自己的簿记(例如,它可能使用一个链接的列表,添加另一个指向每个分配的指针数据。)
只是为了咧嘴笑,让我们假设你平均每个四个字节的四个向量,并且你的堆管理器有一个32字节的最小块大小和一个额外的指针(或int)用于其簿记(给出一个真正的最小值36字节每块)。相乘,我得到每个204字节 - 足够接近你的209,相信它与你正在处理的相当接近。
那时的问题是如何处理这个问题。一种可能性是尝试在幕后工作。标准库中的所有容器都使用分配器来获取内存。虽然默认分配器直接从免费商店获取内存,但如果您选择,可以替换其他内存。如果你做一些环顾四周,你可以找到任意数量的替代分配器,其中很多/大部分是为了帮助你完全处理你所处的情况 - 减少分配大量小对象时浪费的内存。要看的几个是Boost Pool Allocator和Loki小对象分配器。
另一种可能性(可以与第一种结合使用)将是完全退出vector<vector<char> >
,并将其替换为:
char partitions[16];
struct parts {
int part0 : 4;
int part1 : 4;
int part2 : 4;
int part3 : 4;
int part4 : 4;
int part5 : 4;
int part6 : 4
int part7 : 4;
};
目前,我假设最多有8个分区 - 如果它可以是16分,则可以向parts
添加更多分区。这可能会减少内存使用量,但是(按原样)会影响您的其他代码。您还可以将其包装到自己的一个小类中,提供2D样式的寻址,以最大限度地减少对代码其余部分的影响。
答案 2 :(得分:1)
如果存储近乎恒定数量的对象,那么我建议使用二维数组。
内存消耗的最可能原因是调试数据。 STL实现通常存储 A LOT 的调试数据。切勿使用调试标志配置应用程序。
答案 3 :(得分:1)
在我的系统上,sizeof(向量)是24.这可能对应于3个8字节成员:容量,大小和指针。此外,您需要考虑内部向量的1到16个字节(加上分配开销)之间的实际分配,以及外部向量(sizeof(vector)* partitions.capacity())的24到384个字节之间的实际分配。 / p>
我写了一个程序来总结一下......
for ( int Y=1; Y<=16; Y++ )
{
const int X = 16/Y;
if ( X*Y != 16 ) continue; // ignore imperfect geometries
Partition a;
a.partitions = vector< vector<char> >( Y, vector<char>(X) );
int sum = sizeof(a); // main structure
sum += sizeof(vector<char>) * a.partitions.capacity(); // outer vector
for ( int i=0; i<(int)a.partitions.size(); i++ )
sum += sizeof(char) * a.partitions[i].capacity(); // inner vector
cerr <<"X="<<X<<", Y="<<Y<<", size = "<<sum<<"\n";
}
结果显示每个简单几何体需要多少内存(不包括分配开销)......
X=16, Y=1, size = 80
X=8, Y=2, size = 104
X=4, Y=4, size = 152
X=2, Y=8, size = 248
X=1, Y=16, size = 440
看看如何计算“总和”以查看所有组件是什么。
发布的结果基于我的64位架构。如果你有一个32位架构,它的大小几乎是你预期的一半 - 但仍然比你预期的要多很多。
总之,std :: vector&lt;&gt;对于进行一大堆非常小的分配来说,空间效率不高。如果要求您的应用程序有效,那么您应该使用不同的容器。
我解决这个问题的方法可能是用
分配16个字符std::tr1::array<char,16>
并使用将2D坐标映射到数组分配的自定义类来包装它。
下面是一个非常粗略的方法,这是一个让你入门的例子。您必须更改此设置以满足您的特定需求 - 尤其是动态指定几何体的功能。
template< typename T, int YSIZE, int XSIZE >
class array_2D
{
std::tr1::array<char,YSIZE*XSIZE> data;
public:
T & operator () ( int y, int x ) { return data[y*XSIZE+x]; } // preferred accessor (avoid pointers)
T * operator [] ( int index ) { return &data[index*XSIZE]; } // alternative accessor (mimics boost::multi_array syntax)
};
答案 4 :(得分:1)
...这是一个侧面对话,但建议使用boost :: multi_array作为OP使用嵌套向量的替代方法。我的发现是,当应用于OP的操作参数时,multi_array使用了相似数量的内存。
我从Boost.MultiArray的示例中获取了此代码。在我的机器上,这表明multi_array使用的内存比理想情况下多10倍,假设16个字节以简单的矩形几何排列。
为了评估内存使用情况,我在程序运行时检查了系统监视器,并使用
编译( export CXXFLAGS="-Wall -DNDEBUG -O3" ; make main && ./main )
这是代码......
#include <iostream>
#include <vector>
#include "boost/multi_array.hpp"
#include <tr1/array>
#include <cassert>
#define USE_CUSTOM_ARRAY 0 // compare memory usage of my custom array vs. boost::multi_array
using std::cerr;
using std::vector;
#ifdef USE_CUSTOM_ARRAY
template< typename T, int YSIZE, int XSIZE >
class array_2D
{
std::tr1::array<char,YSIZE*XSIZE> data;
public:
T & operator () ( int y, int x ) { return data[y*XSIZE+x]; } // preferred accessor (avoid pointers)
T * operator [] ( int index ) { return &data[index*XSIZE]; } // alternative accessor (mimics boost::multi_array syntax)
};
#endif
int main ()
{
int COUNT = 1024*1024;
#if USE_CUSTOM_ARRAY
vector< array_2D<char,4,4> > A( COUNT );
typedef int index;
#else
typedef boost::multi_array<char,2> array_type;
typedef array_type::index index;
vector<array_type> A( COUNT, array_type(boost::extents[4][4]) );
#endif
// Assign values to the elements
int values = 0;
for ( int n=0; n<COUNT; n++ )
for(index i = 0; i != 4; ++i)
for(index j = 0; j != 4; ++j)
A[n][i][j] = values++;
// Verify values
int verify = 0;
for ( int n=0; n<COUNT; n++ )
for(index i = 0; i != 4; ++i)
for(index j = 0; j != 4; ++j)
{
assert( A[n][i][j] == (char)((verify++)&0xFF) );
#if USE_CUSTOM_ARRAY
assert( A[n][i][j] == A[n](i,j) ); // testing accessors
#endif
}
cerr <<"spinning...\n";
while ( 1 ) {} // wait here (so you can check memory usage in the system monitor)
return 0;
}
答案 5 :(得分:0)
16个字节是完全浪费的。你存储了很多关于非常小的物体的数据。向量向量是使用的错误解决方案。你应该记录sizeof(向量) - 它并不是无关紧要的,因为它执行一个重要的功能。在我的编译器上,sizeof(向量)是20.因此每个分区是4 + 4 + 16 + 20 + 20 *内部分区的数量+内存开销,例如矢量不是完美的大小。
你只存储了16个字节的数据,浪费了大量内存,以你可能想到的最分离,最高开销的方式分配它们。矢量不会占用大量内存 - 你的设计很糟糕。