C++内存管理

来源:互联网 发布:java基础语法总结 编辑:程序博客网 时间:2024/06/05 00:29

C++内存池初探


原创文章,转载请注明出处:http://blog.csdn.net/fastsort/article/details/12356369

///mymem.h    使用自己定义的内存管理需要包含这个头文件

#ifndef__mymem_h__#define__mymem_h__template<classT>classCachedObj{public:void*operator new(std::size_t);voidoperator  delete(void *, std::size_t);virtual~CachedObj(){}protected:T* next;private:staticvoidAddToFreeList(T *);staticstd::allocator<T>allocMem;staticT *freeStore;staticconststd::size_tchunk;};/////////////////////////////////////////////////////////函数实现和数据初始化///模板中将头文件和实现文件分离在vs中有问题//////////////////////////////////////////////////////template<classT>void*CachedObj<T>::operator new(size_t sz){//cout<<"op new"<<endl;if(sz != sizeof(T))///在vs下这里没问题,在Linux下这里需要修改一下throwstd::runtime_error("CachedObj : wrong size object in op new. ");if(!freeStore){T * a = allocMem.allocate(chunk);for(size_t i=0; i!= chunk; i++)AddToFreeList(&a[i]);}T* p = freeStore;freeStore = freeStore->CachedObj<T>::next;return p;}template<class T>voidCachedObj<T>::operator delete(void *p, size_t sz){//cout<<"op delete"<<endl;if(p)AddToFreeList(static_cast<T*>(p));}template<class T>voidCachedObj<T>::AddToFreeList(T * p){p->CachedObj<T>::next = freeStore;freeStore = p;}template<class T>std::allocator< T >CachedObj<T>::allocMem ;template<class T>T *CachedObj<T>::freeStore = 0;template<class T>constsize_tCachedObj<T>::chunk = 20 ;#endif //#endif __mymem_h__

由于使用了模板,如果把头文件和实现代码分离,编译出错。

在网上也查询了一些解决办法,但是都有局限性,不同编译器的支持度也不一样,所以干脆实现代码也放在头文件里了。


//// main.cpp   测试用的主函数

#include<iostream>#include<time.h>#include<fstream>#include"mymem.h"///自己定义的内存管理基类usingnamespacestd;ofstreamofs("perfRec.txt",ios::app);/***优化S类的内存管理 **/classS:publicCachedObj<S>{public:S(){}voidfun(){cout<<"fun is called!"<<endl;}~S(){}};/**不优化T的内存管理**/classT{public:T(){}voidfun(){cout<<"fun is called!"<<endl;}~T(){}};/**记录测试结果的函数**/voidrec(intcnt, int sd, int td){ofs<<cnt<<"\t\t";///countofs<<sd<<"\t\t"<<td<<"\t\t";//s , dif(sd)ofs<< ((int)(1.0*td/sd*10)/10.0) <<endl;///加速比,保留一位小数elseofs<< "-" <<endl;ofs.flush();}/**申请完立即释放测试**/voidtestA(int n=1024000){cout<< __FUNCTION__ <<" count = "<< n <<endl;//intCTest = n;clock_tstart,end;start = clock();for(int i=0; i<n; i++){S*ps = newS();deleteps;}end = clock();intsd = end-start;cout<<"S duratuon : "<< sd <<"ms"<<endl;start = clock();for(int i=0; i<n; i++){T*ps = newT();deleteps;}end = clock();int td = end-start;cout<<"T duration : "<< td <<"ms"<<endl;rec(n,sd,td);}/**申请完不立即释放测试***/voidtestB(int n=1024000){cout<< __FUNCTION__ <<" count = "<< n <<endl;S** psa = new S*[n] ;clock_tstart,end;start = clock();for(int i=0;  i<n; i++){psa[i] = new S();}for(int i=n-1;  i>=0; i--){delete psa[i];}end  = clock();intsd = end-start;cout<<"S duratuon : "<< sd <<"ms"<<endl;T** pta = new T*[n];start = clock();for(int i=0;  i<n; i++){pta[i] = new T();}for(int i=n-1;  i>=0; i--){delete pta[i];}end  = clock();int td = end-start;cout<<"T duration : "<< td <<"ms"<<endl;rec(n,sd,td);delete[]psa;delete[]pta;}voidfa(int n){int i,s;ofs<<"===================Table a==================="<<endl;ofs<<"!!! release after per alloc at once !!!"<<endl;ofs<<"count"<<"\t\t"<<"s duration(ms)"<<"\t\t"<<"t duration(ms)"<<endl;for(i=0,s=1000; i<n; i++){s *= 10;testA(s);}//testA(s*5);}voidfb(int n){int i,s;ofs<<"===================Table B==================="<<endl;ofs<<"!!! release after alloc all elements !!!"<<endl;ofs<<"count"<<"\t\t"<<"s duration(ms)"<<"\t\t"<<"t duration(ms)"<<endl;for(i=0,s=1000; i<n; i++){s *= 10;testB(s);}//testB(s*5);}intmain(){ofs<<"========performance test in win7(x64)========"<<endl;int n=4;int cnt=10;while(cnt--){fa(n);fb(n);}ofs<<"======test end=========\n\n\n\n\n\n\n"<<endl;return 0;}

如果要对某个类进行手动内存管理(或者说用我们自己定义的内存池),只需要让该类继承CachedObj类即可。

需要注意的是,CachedObj是一个模板,继承应该这样写:

class S:publicCachedObj<S>{...};

为了对比,同时定义了另外一个类T,使用系统的内存管理策略。


测试结果:

(第一列为申请/释放内存次数,第二列为S的结果,第三列为T的结果,最后一列是T/S的值)

在win7(x64)下:

===================Table a===================!!! release after per alloc at once !!!counts duration(ms)t duration(ms)1000001-10000011010100000089612100000007197713.7===================Table B===================!!! release after alloc all elements !!!counts duration(ms)t duration(ms)1000001-10000021261000000121159.51000000012311859.6
效果十分明显,效率提升约十倍!


但是代码放到Linux下,就不是那么明显了:

========performance test in ubuntu ===========================Table a===================!!! release after per alloc at once !!!counts duration(m)t duration(ms)1000000-10000010000100001100000050000600001.210000000530000560000150000000264000028600001===================Table B===================!!! release after alloc all elements !!!counts duration(m)t duration(ms)1000000-10000010000100001100000070000800001.1100000006700008300001.250000000325000041300001.2

不得不说,Linux比win的内存管理貌似牛x很多。

继续测试,将chunk修改为每次增大一倍,直到不能增大,但是效果还是不明显,仍然在1附近。

后来无意中想起优化选项,

于是编译时添加-O0和-O3,对比其结果有什么不同。

开始很奇怪的是,-O3不仅没有提高效率,反而降低了效率。

但是后来分析发现,只是加速比增加的很明显,而实际上,绝对速度都比未优化的要快:


使用-O0和-O3优化选项的结果对比图。


由此看出,使用自己的内存池管理内存还是有必要的。


遗留问题:

申请的内存始终没有返回给系统。

如果系统出现某个很高的峰值后,一直处在较低的负载下运行,则有大量的内存被本程序占用且未被使用。

一个解决办法是每隔一段时间,看内存池空闲的的比例是多少,如果很高,则释放一部分返回给系统。



附:整个测试代码和结果

http://download.csdn.net/detail/hello_world_2012/6360453


参考:《C++ primer 第四版》

原创文章,转载请注明出处: blog.csdn.net/fastsort/article/details/12356369