开源C++函数库Boost内存池使用与测试

来源:岁月联盟 编辑:exp 时间:2011-09-28

 Boost库是一个可移植的开源C++函数库,鉴于STL(标准模板库)已经成为C++语言的一个组成部分,可以毫不夸张的说,Boost是目前影响最大的通用C++库。Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容,是一个“准”标准库。
  Boost内存池,即boost.pool库,是由Boost提供的一个用于内存池管理的开源C++库。作为Boost中影响较大的一个库,Pool已经被广泛使用。
  1. 什么是内存池“池”是在计算机技术中经常使用的一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。经常使用的池技术包括内存池、线程池和连接池等,其中尤以内存池和线程池使用最多。
  内存池(Memory Pool)是一种动态内存分配与管理技术。通常情况下,程序员习惯直接使用new、delete、malloc、free等API申请分配和释放内存,导致的后果时:当程序长时间运行时,由于所申请内存块的大小不定,频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能。内存池则是在真正使用内存之前,先申请分配一大块内存(内存池)留作备用,当程序员申请内存时,从池中取出一块动态分配,当程序员释放内存时,将释放的内存再放入池内,并尽量与周边的空闲内存块合并。若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池。
  内存池的应用场景早期的内存池技术是为了专门解决那种频繁申请和释放相同大小内存块的程序,因此早期的一些内存池都是用相同大小的内存块链表组织起来的。
  Boost的内存池则对内存块的大小是否相同没有限制,因此只要是频繁动态申请释放内存的长时间运行程序,都适用Boost内存池。这样可以有效减少内存碎片并提高程序运行效率。
  安装Boost的pool库是以C++头文件的形式提供的,不需要安装,也没有lib或者dll文件,仅仅需要将头文件包含到你的C++工程中就可以了。Boost的最新版本可以到http://www.boost.org/下载。
  2. 内存池的特征2.1 无内存泄露正确的使用内存池的申请和释放函数不会造成内存泄露,更重要的是,即使不正确的使用了申请和释放函数,内存池中的内存也会在进程结束时被全部自动释放,不会造成系统的内存泄露。
  2.2 申请的内存数组没有被填充例如一个元素的内存大小为A,那么元素数组若包含n个元素,则该数组的内存大小必然是A*n,不会有多余的内存来填充该数组。尽管每个元素也许包含一些填充的东西。
  2.3 任何数组内存块的位置都和使用operator new[]分配的内存块位置一致这表明你仍可以使用那些通过数组指针计算内存块位置的算法。
  2.4 内存池要比直接使用系统的动态内存分配快这个快是概率意义上的,不是每个时刻,每种内存池都比直接使用new或者malloc快。例如,当程序使用内存池时内存池恰好处于已经满了的状态,那么这次内存申请会导致内存池自我扩充,肯定比直接new一块内存要慢。但在大部分时候,内存池要比new或者malloc快很多。
  3. 内存池效率测试3.1 测试1:连续申请和连续释放分别用内存池和new连续申请和连续释放大量的内存块,比较其运行速度,代码如下:#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std;using namespace boost;
  const int MAXLENGTH = 100000;
  int main ( )
  { boost::pool<> p(sizeof(int));int* vec1[MAXLENGTH];int* vec2[MAXLENGTH];
  clock_t clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { vec1[i] = static_cast<int*>(p.malloc());} for (int i = 0; i < MAXLENGTH; ++i)
  { p.free(vec1[i]);vec1[i] = NULL;}
  clock_t clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { vec2[i] = new int;} for (int i = 0; i < MAXLENGTH; ++i)
  { delete vec2[i];vec2[i] = NULL;}
  clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  return 0;}测试环境:VS2008,WindowXP SP2,Pentium 4 CPU双核,1.5GB内存。
  结论:在连续申请和连续释放10万块内存的情况下,使用内存池耗时是使用new耗时的47.46%.
  3. 内存池效率测试3.1 测试1:连续申请和连续释放分别用内存池和new连续申请和连续释放大量的内存块,比较其运行速度,代码如下:#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std;using namespace boost;
  const int MAXLENGTH = 100000;
  int main ( )
  { boost::pool<> p(sizeof(int));int* vec1[MAXLENGTH];int* vec2[MAXLENGTH];
  clock_t clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { vec1[i] = static_cast<int*>(p.malloc());} for (int i = 0; i < MAXLENGTH; ++i)
  { p.free(vec1[i]);vec1[i] = NULL;}
  clock_t clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { vec2[i] = new int;} for (int i = 0; i < MAXLENGTH; ++i)
  { delete vec2[i];vec2[i] = NULL;}
  clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  return 0;}测试环境:VS2008,WindowXP SP2,Pentium 4 CPU双核,1.5GB内存。
  结论:在连续申请和连续释放10万块内存3.2 测试2:反复申请和释放小块内存代码如下:#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std;using namespace boost;
  const int MAXLENGTH = 500000;
  int main ( )
  { boost::pool<> p(sizeof(int));
  clock_t clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { int * t = static_cast<int*>(p.malloc());p.free(t);} clock_t clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { int* t = new int;delete t;} clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  return 0;}测试结果如下:
  结论:在反复申请和释放50万次内存的情况下,使用内存池耗时是使用new耗时的64.34%.的情况下,使用内存池耗时是使用new耗时的47.46%.
  3.3 测试3:反复申请和释放C++对象C++对象在动态申请和释放时,不仅要进行内存操作,同时还要调用构造和析购函数。因此有必要对C++对象也进行内存池的测试。
  代码如下:#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std;using namespace boost;
  const int MAXLENGTH = 500000;class A { public:A()
  { m_i++;} ~A( )
  { m_i——;} private:int m_i;};
  int main ( )
  { object_pool<A> q;
  clock_t clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { A* a = q.construct();q.destroy(a);}
  clock_t clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  clock_begin = clock();for (int i = 0; i < MAXLENGTH; ++i)
  { A* a = new A;delete a;} clock_end = clock();cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
  return 0;}测试结果如下:
  结论:在反复申请和释放50万个C++对象的情况下,使用内存池耗时是使用new耗时的112.03%.这是因为内存池的construct和destroy函数增加了函数调用次数的原因。这种情况下使用内存池并不能获得性能上的优化。
  4. Boost内存池的分类Boost内存池按照不同的理念分为四类。主要是两种理念的不同造成了这样的分类。
  一是Object Usage和Singleton Usage的不同。Object Usage意味着每个内存池都是一个可以创建和销毁的对象,一旦内存池被销毁则其所分配的所有内存都会被释放。Singleton Usage意味着每个内存池都是一个被静态分配的对象,直至程序结束才会被销毁,这也意味着这样的内存池是多线程安全的。只有使用release_memory或者 purge_memory方法才能释放内存。
  二是内存溢出的处理方式。第一种方式是返回NULL代表内存池溢出了;第二种方式是抛出异常代表内存池溢出。
  根据以上的理念,boost的内存池分为四种。
  4.1 Pool Pool是一个Object Usage的内存池,溢出时返回NULL. 4.2 object_pool object_pool与pool类似,唯一的区别是当其分配的内存释放时,它会尝试调用该对象的析购函数。
  4.3 singleton_pool singleton_pool是一个Singleton Usage的内存池,溢出时返回NULL. 4.4 pool_alloc pool_alloc是一个Singleton Usage的内存池,溢出时抛出异常。
  5. 内存池溢出的原理与解决方法5.1 必然溢出的内存内存池简化了很多内存方面的操作,也避免了一些错误使用内存对程序造成的损害。但是,使用内存池时最需要注意的一点是要处理内存池溢出的情况。
  没有不溢出的内存,看看下面的代码:#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std;using namespace boost;
  int _tmain(int argc, _TCHAR* argv[])
  { clock_t clock_begin = clock();int iLength = 0;for (int i = 0; ;++i)
  { void* p = malloc(1024*1024);&

下一篇:C++链表