在 vs2008 里使用 SGI STL 的内存池特性

来源:互联网 发布:淘宝卖家怎么买运费险 编辑:程序博客网 时间:2024/05/16 11:43
最近开发的一个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};


[更新: 2011-9-13] 考虑到一些网友按上述步骤操作后,未能编译通过上述代码,现提供源码供下载。

http://www.codepads.com/download/source-code/sgi_stl_allocator.zip

原创粉丝点击