我有一个程序启动了,在大约5分钟内,进程的虚拟大小大约为13个gigs。它运行在Linux上,使用boost、gnuc++库和其他各种第三方库。
5分钟后,大小保持在13个gigs,rss大小稳定在5个gigs左右。
我不能只在调试器中运行它,因为在启动时,大约有30个线程被启动,每个线程都开始运行自己的代码,进行各种分配。因此,在每个断点遍历并检查代码的不同部分的虚拟内存是不可行的。
我想改变程序,一次启动一个线程,这样可以更容易地跟踪内存分配,但在这样做之前,有什么好的工具吗?
Valgrind相当慢,也许tcmalloc可以提供信息?
我会使用valgrind(也许运行一整晚),或者使用Boehm GC。
或者,使用proc(5)文件系统来了解(例如,通过/proc/$pid/statm
&/proc/$pid/maps
)何时分配了大量内存。
最重要的是找到内存泄漏。如果启动后内存没有增长,那么问题就小了。
也许向每个类添加实例计数器可能会有所帮助(使用原子整数或互斥对象来序列化它们)。
如果程序的源代码很大(例如有一百万行源代码),因此花费几天/几周的时间是值得的,那么定制GCC编译器(例如使用MELT)可能是相关的。
std::set
迷你基准
您提到了基于百万行的大std::set
。
#include <set>
#include <string>
#include <string.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <time.h>
class MyElem
{
int _n;
char _s[16-sizeof(_n)];
public:
MyElem(int k) : _n(k)
{
snprintf (_s, sizeof(_s), "%d", k);
};
~MyElem()
{
_n=0;
memset(_s, 0, sizeof(_s));
};
int n() const
{
return _n;
};
std::string str() const
{
return std::string(_s);
};
bool less(const MyElem&x) const
{
return _n < x._n;
};
};
bool operator < (const MyElem& l, const MyElem& r)
{
return l.less(r);
}
typedef std::set<MyElem> MySet;
void bench (int cnt, MySet& set)
{
for (long i=0; i<(long)cnt*1024; i++)
set.insert(MyElem(i));
time_t now = 0;
time (&now);
set.insert (((now) & 0xfffffff) * 100);
}
int main (int argc, char** argv)
{
MySet s;
clock_t cstart, cend;
int c = argc>1?atoi(argv[1]):256;
if (c<16) c=16;
printf ("c=%d Kitern", c);
cstart = clock();
bench (c, s);
cend = clock();
int x = getpid();
char cmdbuf[64];
snprintf(cmdbuf, sizeof(cmdbuf), "pmap %d", x);
printf ("running %sn", cmdbuf);
fflush (NULL);
system(cmdbuf);
putchar('n');
printf ("at end c=%d Kiter clockdiff=%.2f millisec = %.f µs/Kitern",
c, (cend-cstart)*1.0e-3, (double)(cend-cstart)/c);
if (s.find(x) != s.end())
printf("set has %dn", x);
else
printf("set don't contain %dn", x);
return 0;
}
请注意16字节的sizeof(MyElem)
。在Debian/Sid/AMD64上,使用GCC 4.8.1(英特尔i3770K处理器,16GB RAM),并使用g++ -Wall -O1 tset.cc -o ./tset-01
编译该工作台
32768.千次迭代,因此有32M个元素:
总计2109592K
(pmap
给出的最后一行)
末端c=32768基特时钟差=16470.00毫秒=503µs/基特
然后我的zsh
中的隐式time
/tset-01 32768 16.77s用户0.54s系统99%cpu 17.343总
这大约是2.1G字节,因此每个元素可能有64.3字节&set
成员开销(由于sizeof(MyElem)==16
,该集合似乎具有不可忽略的成本,每个元素可能有6个字)