在 vs2008 里使用 SGI STL 的内存池,包括修改内存块大小(转)
最近开发的一个C++项目是针对性能进行优化的,所以在很多细节上不得不"咬文嚼字", 而C++其中强大的地方是对内存使用有很强的可控性, 而任何一个C++项目都离不开对内存的管理,所以要进行内存管理的优化,而使用内存池是要考虑的.
内存池的做法理论在网上很容易找到,但却真正可靠高效的实现,还真是凤毛麟角,也许你会说ACE,boost之类的现成实现,然而这些大家伙动辄就得找半天资料才能编译通过,而提取使用起来就更麻烦了.如果自己实现,那工作量还真的不少,光就是测试就让你费尽心思了,更何况咱们的代码并不能保证不差过专家级的代码,所以最好能有现成的公认的,那样就省心得多了
幸好的是,SGI STL实现了内存池(不要以为STL就一定有内存池,C++标准可没这样说,vs2008的STL就没有实现内存池,不信看看源码)相对还是比较简单的.
下面就说说我的做法:
1 .首先从 SGI 的网站下载官方源码 : http://www.sgi.com/tech/stl/download.html
解压打开后有很多源码文件,这里只需要把: stl_config.h, stl_threads.h, sgi_stl_alloc.h 拿出来就行了.
真正的内存分配管理的源码是在 sgi_stl_alloc.h, 然而因为 SGI STL是跨平台的,所以全涉及到不少的编译宏.
2. 打开 stl_config.h
找到下面针对VC编译器的宏开关, 先把 __STL_NEED_TYPENAME 宏关闭, 因为这个开关把 typename 定义为空, 所以到后面的"模糊"定义时类型时typename就不起作用,导致编译不通过
// Microsoft compiler. # if defined(_MSC_VER) && !defined(__ICL) && !defined(__MWERKS__) # define __STL_NO_DRAND48 # define __STL_STATIC_CONST_INIT_BUG //# define __STL_NEED_TYPENAME // 把这个注释掉 # define __STL_NO_USING_CLAUSE_IN_CLASS # define __STL_NO_FRIEND_TEMPLATE_CLASS .....
3. 在 stl_config.h 里定义宏: #define __STL_USE_STD_ALLOCATORS
这个宏是让SGI的 std::allocator() 有效, 这是STL标准的分配器接口,要想让vs2008使用这个分配器的话,就得把这个宏打开.
4. 修改命名空间名. 很多时候我们还是想使用vs自带的STL(vc6除外, ^_^),又想使用SGI STL的内存池功用,所以就会同时出现两个STL共存,而它们的命名空间的名字是一样,所以得把其中一个名字改一下就行了.
打开 stl_config.h, 定位到下面的源码, 把std改为 sgi_std, 这时候, 当你想用vs2008 分配器这样定义: std::allocator() ,而使SGI STL的分配器时: sgi_std::allocator()
// __STL_NO_NAMESPACES is a hook so that users can disable namespaces // without having to edit library headers. __STL_NO_RELOPS_NAMESPACE is // a hook so that users can disable the std::rel_ops namespace, keeping // the relational operator template in namespace std, without having to // edit library headers. # if defined(__STL_HAS_NAMESPACES) && !defined(__STL_NO_NAMESPACES) # define __STL_USE_NAMESPACES # define __STD std # define __STL_BEGIN_NAMESPACE namespace sgi_std { // 这里原来是 std , 现在改为 sgi_std # define __STL_END_NAMESPACE } # if defined(__STL_FUNCTION_TMPL_PARTIAL_ORDER) && \ .....
这时候你已可以直接使用 SGI STL 的 sgi_std::allocator() 来分配内存了,这个分配器在内部是使用了内存池的.
如果想让 vs2008 的stl容器使用这个分配器,可以这样写:
std::vector<int,sgi_std::allocator<int> > my_vector ;
======================== 扩展内容 =======================
SGI STL 的内存池默认是把以8字节最小块,再以8字节为递增,最大的是126,共16个分块管理. 其实这是使用效率最高的区域,因为能省去不少的cookies空间,但现在的程序动辄就使用较大的内存,比如一个结构体假如包含多几个int,很快就大于 126的空间了,而这样的话,SGI STL就不使用内存池管理,而直接向系统要内存,这样经常频繁申请释放的话,程序的效率肯定有所影响.
所以有时候我们需要调整 SGI STL的分块数.
1 打开 sgi_stl_alloc.h 定位到,并修改代码如下
#if defined(__SUNPRO_CC) || defined(__GNUC__) // breaks if we make these template class members: enum {_ALIGN = 8}; enum {_MAX_BYTES = 256}; // 改为 256 enum {_NFREELISTS = _MAX_BYTES/_ALIGN}; // 把 16 改为: _MAX_BYTES/_ALIGN #endif
2 打开 sgi_stl_alloc.h 定位到,并修改代码如下:
template <bool __threads, int __inst> typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE __default_alloc_template<__threads, __inst> ::_S_free_list[ # if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC) _NFREELISTS # else __default_alloc_template<__threads, __inst>::_NFREELISTS # endif //] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // 因为最大的块由128 改为256,所以这里应是比 16 个更多,改成下面的代码就行了 ] = {0};