讨论内存池(有没有存在的理由)

317 views
Skip to first unread message

关中刀客

unread,
Jul 1, 2007, 12:28:24 AM7/1/07
to 高性能网络编程邮件列表, 讨论
一般大家在server中对于固定大小的对象,都会使用"对象池"方案,但是对于非固定的大小的频繁需要的变量是否都会实现一个真正的"内存池"来隔离
new/delete呢?我最近做了一个这个"内存池",刚开始分配很多空间,比如1M,然后"内存管理器"中存在两张表:一个以分配的块表map<开
始位置,大小>,一个未分配表
map<开始位置,大小>,每次分配的时候从未分配空间中找到合适的块,从它的表中删除这个信息,然后把信息存进"以分配表"。就这样子做,结果在单线
程中测试,和new相比,速度慢了n倍,(可能我模拟的环境不对吧)。
大家一般怎么做?

qiaojie

unread,
Jul 1, 2007, 12:59:45 AM7/1/07
to dev4s...@googlegroups.com
系统本来提供了一个标准的轮子给大家使用,经过大家无数次的使用证明是个坚固耐用的好轮子,某日,楼主听说某种改装的新轮子在过弯道时可以跑的更快,于是他认定这标准轮子不是个好轮子,有必要重新发明种新轮子,既可以跑直道又可以跑弯道,可是我们的楼主自信心很强,连轮子长什么样都没搞清出就大笔一挥发明出了个三角形轮子。


 
在07-7-1,关中刀客 <guanzho...@gmail.com> 写道:

Kasicass

unread,
Jul 1, 2007, 2:04:30 AM7/1/07
to dev4s...@googlegroups.com
Hi 刀客,

你说的是内存管理吧。可以参考一下:

Lea Allocator,据说 Linux 内部的 malloc 就是用这个,很稳定、高效。- -!
偶没认真看过。
http://g.oswego.edu/dl/html/malloc.html

其实一般 freelist 的实现已经很好用了,C++ 实现参考 jjhou 的《STL源码剖
析》。pure C 实现看看 cloudwu 的 WindSoul 里面就有。

如果想看很漂亮的 C++ 实现,可以欣赏一下 Hoard,网站上还有不错的论文介绍。
http://www.hoard.org/

:-), hope it's help!!!


关中刀客 写道:

stephen.nil

unread,
Jul 1, 2007, 2:35:24 AM7/1/07
to dev4s...@googlegroups.com
Hi, qiaojie

这个要看情况。比如 memcached 中的 slab 内存池就对它的性能有很大的作用
http://code.sixapart.com/svn/memcached/trunk/server/slabs.h


Best regards,

stephen.nil
2007-07-01

stephen.nil

unread,
Jul 1, 2007, 2:35:24 AM7/1/07
to dev4s...@googlegroups.com
Hi, qiaojie

这个要看情况。比如 memcached 中的 slab 内存池就对它的性能有很大的作用
http://code.sixapart.com/svn/memcached/trunk/server/slabs.h


Best regards,

stephen.nil
2007-07-01

lwk...@gmail.com

unread,
Jul 1, 2007, 5:27:26 AM7/1/07
to 高性能网络编程邮件列表
绝对有存在的理由,内存池作为一个高性能服务器不可缺少的一部分。
小内存分配尤其重要
1是内存重用模式,2是避免长时间运行随机分配产生的内存碎片使程序的速度显著下降

sunway

unread,
Jul 1, 2007, 7:22:01 AM7/1/07
to 高性能网络编程邮件列表
恩,我怎么看到很多人买汽车都不用厂家送的轮子去买其他牌子的轮子了?


On 7月1日, 下午12时59分, qiaojie <qiao...@gmail.com> wrote:
> 系统本来提供了一个标准的轮子给大家使用,经过大家无数次的使用证明是个坚固耐用的好轮子,某日,楼主听说某种改装的新轮子在过弯道时可以跑的更快,于是他认定 这标准轮子不是个好轮子,有必要重新发明种新轮子,既可以跑直道又可以跑弯道,可是我们的楼主自信心很强,连轮子长什么样都没搞清出就大笔一挥发明出了个三角形 轮子。
>
> 在07-7-1,关中刀客 <guanzhongda...@gmail.com> 写道:


>
>
>
>
>
> > 一般大家在server中对于固定大小的对象,都会使用"对象池"方案,但是对于非固定的大小的频繁需要的变量是否都会实现一个真正的"内存池"来隔离
> > new/delete呢?我最近做了一个这个"内存池",刚开始分配很多空间,比如1M,然后"内存管理器"中存在两张表:一个以分配的块表map<开
> > 始位置,大小>,一个未分配表
> > map<开始位置,大小>,每次分配的时候从未分配空间中找到合适的块,从它的表中删除这个信息,然后把信息存进"以分配表"。就这样子做,结果在单线
> > 程中测试,和new相比,速度慢了n倍,(可能我模拟的环境不对吧)。

> > 大家一般怎么做?- 隐藏被引用文字 -
>
> - 显示引用的文字 -

NONAME

unread,
Jul 1, 2007, 11:07:08 PM7/1/07
to dev4s...@googlegroups.com
也不能这么说,
可以看下HJ的《STL源码剖析》,那里面讲了内存池的具体用法和写法。
 
 

NONAME
2007-07-02

发件人: sunway
发送时间: 2007-07-01 19:22:20
收件人: 高性能网络编程邮件列表
抄送:
主题: Re: 讨论内存池(有没有存在的理由)

arcnode

unread,
Jul 3, 2007, 2:52:33 AM7/3/07
to 高性能网络编程邮件列表
内存池的原理大家都知道,内存池的必要性也不需要讨论,关键在于设计方法,
关中刀客设计的方法有问题,内存池是和stl组件相当或者更低的组件用stl的组件去实现是不合适的,要用更低级的方法实现,我建议你去参考一下云风的
内存池,代码很简单只有200行左右,效率也还可以,值得一看。

清风雨

unread,
Jul 3, 2007, 9:42:44 PM7/3/07
to 高性能网络编程邮件列表
我做过对比测试,在linux下。用对象池,直接取出指针,也只比malloc快6倍(1/6的时间)。
linux下系统本身有cache,有个专有名词叫slab技术。
linux上,stlport的alloc实测下来也比直接malloc慢(具体倍数我忘记了),在windows上stlport当然是能提高速度的
(windows下数据相对不是很稳定,具体记不住了,好像不同情况可以到60倍吧)。--stlport的alloc缺省是用的
node_alloc,采用了内存池技术(较详细的描述可以参考《stl源码剖析》)。
用map的实现肯定是不怎么理想的(map本身是需要申请节点内存的,相对查找提高也不明显,还有插入开销),对象池的好处是因为大小固定,较通用内存
池完全也可以根据不同固定大小划分。

arcnode

unread,
Jul 3, 2007, 10:50:38 PM7/3/07
to 高性能网络编程邮件列表
根据测试,在win下用内存池可比malloc提高速度50% - 300%左右(如果不考虑多线程同步,速度可提高65倍左右,但这种情况意义不是很
大),但是有一点我提醒各位,用内存池的好处并不完全在于速度,还有一个重要好处是减少内存碎片,提高24*7服务器的稳定性,可以让服务器稳定运行更
长时间,据我所知,有些公司的游戏服务器没有用内存池的时候需要每天早上某个时间重新启动一下解决稳定性问题,如果用内存池我估计情况会好一些。

我曾用list<...> 等做基础给别人演示原理随手写了个简单的内存池,速度只有malloc的1/3 - 1/5左右,后来真正写内存池的时候又
把这个代码拿来profile 了一下,发现大多数时间消耗在list插入对象等上面,所以我认为做内存池这种底层的追求效率的东西用stl是不合适
的,需要用些最基础的高效的数据结构方面的知识。

关中刀客

unread,
Jul 3, 2007, 11:22:58 PM7/3/07
to 高性能网络编程邮件列表
和一些朋友聊了聊,他们是这么做的
一个内存池,他们分成了几个不同的"桶",一个队列专门存放8个字节大小的块,一个队列专门存放16字节大小的块,一个字节存放32字节大小的块,一个
队列存放63字节大小的块.....他们设置一个最大值,超过这个最大值的时候就是用系统的函数。每次使用的时候按照大小在合适的"桶"里面去取,这样
子好像效率非常的高.

Kalos Han

unread,
Jul 4, 2007, 12:07:59 AM7/4/07
to dev4s...@googlegroups.com

我的这段内存池技术怎么样?

/* Module Name: L_MEM.C
 * Purpose:
 *
 *  L_MEM provides a pool for whole system allocated to prevent memory fragment,
 *
 * Notes:
 *  In developing stage we need to know the peak amount be used. We define _IN_TARGET
 *        macro to include debugging environment, UC_MGR in Mercury.
 *
 * History:
 *       Date       -- Modifier,        Reason
 *  1.1 2001.12.19  -- William Chiang,  Add peak counter to track the maximun amount
 *                                                                                used in run time.
 *  1.2 2002.01.17  -- William Chiang,  Modify the amount of blocks in memory pool.
 *                                      Define _IN_TARGET for keeping tracking information
 *                                      in Un-clear Memory, not complete yet, so not recorded in
 *                                      unclear memory.
 *        1.3        2002.03.07        -- William Chiang,        Add BACKDOOR_OPEN mechanism.
 *
 * Copyright(C)      Accton Corporation, 1999, 2000
 */

/* INCLUDE FILE DECLARATIONS
 */

#include <malloc.h>
#include <memory.h>
#include <string.h>

#include <string.h>
#include  <stdio.h>
#define L_MEM_MAX_SIZE       262144




/* NAMING CONSTANT DEFINITION
 */

/*        <<< Define some directory for special purpose >>>
 *        BACKDOOR_OPEN : interact with backdoor function of L_MREF
 *                                        if no need to inact with L_MREF, do not define BACKDOOR_OPEN
 *        _IN_TARGET          : active tracking counter of each memory pool, this mechanism waste
 *                                        some calculation time.
 *                                        1 : active, 0 : inactive.
 *        _TMZ : the space to separate over range.
 */
#define        BACKDOOR_OPEN
#define        _IN_TARGET                                                1        
#define _TMZ                                                        1

#if                _TMZ
#define        MEM_TMZ_SIZE                        1024
#define        MEM_TMZ_PATTERN                        0x32
#endif        /*        end of _TMZ        */

/*
 *        <<< end of special directory definition >>>
 */

#ifdef        BACKDOOR_OPEN
//#include "backdoor_mgr.h"
#endif

/*--------------------------------------------------
 *  After this line, L_MREF used constant is defined.
 *--------------------------------------------------
 */

   /* Buffer Number definition */
   #define ADPT_NUMBER_OF_BUF_64    4096
   #define ADPT_NUMBER_OF_BUF_512   512
   #define ADPT_NUMBER_OF_BUF_4K    64
   #define ADPT_NUMBER_OF_BUF_32K   8
   #define ADPT_NUMBER_OF_BUF_256K  1

   //#define printf CON_Printf
   
   #define ENTRY_NBR_OF_buf_header  sizeof(buf_header)/sizeof(Buffer_Descriptor_T)



/* TYPE DECLARACTION */

   typedef struct
   {
      unsigned size;
      unsigned number;
      void **free;
   } Buffer_Descriptor_T;

#if        _IN_TARGET
typedef        struct        L_MEM_COUNER_REC_S
{
        unsigned        peak_amount;
        unsigned        used_counter;
        unsigned        alloc_counter;
        unsigned        free_counter;
}        L_MEM_COUNTER_REC_T;
#endif

/* LOCAL SUBPROGRAM DECLARATIONS
 */
#ifdef        BACKDOOR_OPEN
//static        void L_MEM_BackDoor_Menu (void);
#endif

#if        _TMZ
//static void L_MEM_VerifyTmz(void);
#endif

/* LOCAL STATIC VARIABLES */
#if                _TMZ
char        l_mem_tmz_front[MEM_TMZ_SIZE];
#endif        /*        end of _TMZ        */


static Buffer_Descriptor_T  buf_header [] =
{
    {  64,   ADPT_NUMBER_OF_BUF_64   },
    {  512,  ADPT_NUMBER_OF_BUF_512  },
    {  4096,  ADPT_NUMBER_OF_BUF_4K  },
    {  32768,  ADPT_NUMBER_OF_BUF_32K  },
    {  262144, ADPT_NUMBER_OF_BUF_256K }
};


static unsigned long used_buffer_number[ENTRY_NBR_OF_buf_header];  

/* Pre-Allocated buffer pool, sum of all buffers */
static void *buf_pool  [
    ADPT_NUMBER_OF_BUF_64 * ( 64/sizeof(void*) + 1) +
    ADPT_NUMBER_OF_BUF_512 * ( 512/sizeof(void*) + 1) +
    ADPT_NUMBER_OF_BUF_4K * ( 4096/sizeof(void*) + 1) +
    ADPT_NUMBER_OF_BUF_32K * ( 32768/sizeof(void*) + 1) +
    ADPT_NUMBER_OF_BUF_256K * ( 262144/sizeof(void*) + 1)  ];

static        int        alloc_cnt[ENTRY_NBR_OF_buf_header], free_cnt[ENTRY_NBR_OF_buf_header];

#if        _IN_TARGET
static        L_MEM_COUNTER_REC_T                access_counter[ENTRY_NBR_OF_buf_header];
#endif        /*        end of _IN_TARGET        */

#if                _TMZ
char        l_mem_tmz_rear[MEM_TMZ_SIZE];
#endif        /*        end of _TMZ        */


/* EXPORTED FUNCTIONS BODY
 */
void L_Mem_Init_Buffer(void)
{
   /* LOCAL VARIABLES */
   unsigned no, i;
   void **p0, **p1;
#ifdef        BACKDOOR_OPEN
//    UI8_T        mem_backdoor_name[] = "MEM";
#endif

    /* BODY */
#if                _TMZ
        for (i=0; i< MEM_TMZ_SIZE; i++)
        {
                l_mem_tmz_rear[i]  = MEM_TMZ_PATTERN;
                l_mem_tmz_front[i] = MEM_TMZ_PATTERN;
        }
#endif        /*        end of _TMZ        */

   for (p1=buf_pool, i=0; i<ENTRY_NBR_OF_buf_header; i++)
   {
      p0 =(void**) &buf_header[i].free;
      for (no=0; no< buf_header[i].number; no++)
      {
         *p0 = p1;
         p0=p1;
         p1 +=  buf_header[i].size/sizeof(void*) +1;
      }
      *p0=0;
   } /* end for */

#if        _IN_TARGET
        /*
         *        access_counter = (L_MEM_COUNTER_REC_T*) malloc (sizeof(L_MEM_COUNTER_REC_T)*ENTRY_NBR_OF_buf_header);
         */
        memset (&access_counter[0], 0, sizeof(L_MEM_COUNTER_REC_T)*(ENTRY_NBR_OF_buf_header));
#endif

        for (i=0; i<ENTRY_NBR_OF_buf_header; i++)
                alloc_cnt[i] = free_cnt[i] = 0;

#ifdef        BACKDOOR_OPEN
//BACKDOOR_MGR_Register_SubsysBackdoorFunc_CallBack(mem_backdoor_name, L_MEM_BackDoor_Menu);
#endif

} /* End of L_MEM_Initial */

void * L_Mem_Malloc ( unsigned long size_in )
{
   /* LOCAL VARIABLES
    */
   void **p;
   int index;

   unsigned long size;
   
   size = size_in;

   if (size> L_MEM_MAX_SIZE)
   {
      return 0;
   } /* end if */


   /* Find suitable min. size buffer */
   for ( index=0; index<ENTRY_NBR_OF_buf_header; index++)
   {
      if ( buf_header[index].size >= size ) break;
   }


   /* critical region -> disable all int
    */


   /* get from static free_table
    */
   for ( ; index< ENTRY_NBR_OF_buf_header; index++)
   {
      if ( (p=buf_header[index].free) == 0 )
         continue;
      /* end if */

      /* adjust pointer
       */
      buf_header[index].free = *p;
      *(int *)p = index;         /* record buffer type */
      used_buffer_number[index]++;
     
#if        _IN_TARGET
      /*        if (access_counter)        */
      {
              access_counter[index].alloc_counter++;
              access_counter[index].used_counter++;
              if (access_counter[index].peak_amount < access_counter[index].used_counter)
                         access_counter[index].peak_amount = access_counter[index].used_counter;
          }
#endif

      alloc_cnt[index]++;
 
     
     
      return p+1;
   } /* end for */
 

#if        _IN_TARGET
        //printf(" <%d> ", size);
#endif

   return 0;
} /* L_Mem_Allocate */


/*         FUNCTION NAME : L_MEM_Free
 *         PURPOSE:
 *          Free memory block to memory pool.
 *         INPUT:  
 *          ptr -- the pointer point to memory block to be free.
 *
 *         OUTPUT:  
 *          None.
 *
 *         RETURN:  
 *          TRUE        -- successfully free to memory pool.
 *                FALSE        -- error occurs when free the memory block.
 *
 *         NOTES:
 *          None.
 */
int L_Mem_Free ( void *ptr)
{
   /* LOCAL VARIABLES
    */
   unsigned index;

   register void     **p = (void**)ptr;

   /* BODY
    */
   if ( p == 0 )
   {
          //logMsg (" L_MEM_Free : null pointer..\n", 0,0,0,0,0,0);
      return 0;
   }

   /* get buffer type */
   p--;
   if ( (index = *(int *)p ) >= ENTRY_NBR_OF_buf_header )
   {
          //logMsg(" L_MEM_Free : invalid pointer (index=%d)..\n", (int)*(int*)p, 0,0,0,0,0);
      return 0;
   }
   
   /* critical region -> disable all int
    */


   *p = buf_header[index].free;

   buf_header[index].free = p;
   used_buffer_number[index]--;

#if        _IN_TARGET
        /*        if(access_counter)        */
        {
                access_counter[index].used_counter--;
                access_counter[index].free_counter++;
        }
#endif
        free_cnt[index]++;


        return        1;
} /* L_Mem_Free */

void sys_debug(char *pwrite)
{
FILE * fp;
char *filname = "c:\\client_debug.txt";
long ll_towrite;
ll_towrite = 1;
fp=fopen(filname,"a+");
fwrite(pwrite,1,ll_towrite,fp);
fclose(fp);
return ;
}
/*详细分析了内存分配算法*/
/*
*初始化的时候
1001  1004

1002
1003
1004  1006
1005
1006  1008
1007
1008
1009
1010
*/

/*内存分配的时候只要没有遇到要NULL指针,就把当前的free的分配出去,
*把free里边记录的下一条内存地址,作为新的free.
*/

/*
* 内存回收的时候,先检查当前指针类型,找到buffer的块的
*第一个首地址,然后把free赋值到新的地方,然后把自己的p作为
*一个新的free.
*/

/*充分验证了程序==算法 + 数据结构*/

int main()
{

int i;
int j;
int count = 0;
unsigned char *p;
void *pvoid[80];

L_Mem_Init_Buffer();

p =  (unsigned char*) buf_header;

count = 0;

for(i=0;i<sizeof(buf_header);i=i+1)
{
count ++;
//printf("%02x ",*(p+i));

if (count % 12 == 0) printf("\r\n");
}

p =  (unsigned char*) buf_pool;

count = 0;

for(i=0;i<sizeof(buf_pool);i=i+1)
{
count ++;
//printf("%02x ",*(p+i));

if (count % 12 == 0) printf("\r\n");
}

for(j=0;j<3;++j)
{
        pvoid[j] = L_Mem_Malloc(32);
        printf("p void  is %p j is %d\r\n",pvoid[j],j);
}

for(j=2;j>=0;j--)
{
        L_Mem_Free(pvoid[j]);
}

for(j=0;j<80;++j)
{
        pvoid[j] = L_Mem_Malloc(32);
        printf("p void  is %p j is %d\r\n",pvoid[j],j);
}

getchar();
return 1;
}


 
关中刀客 <guanzho...@gmail.com>
发件人:  dev4s...@googlegroups.com

2007-07-04 11:22


收件人
"高性能网络编程邮件列表" <dev4s...@googlegroups.com>

抄送
主题
Re: 讨论内存池(有没有存在的理由)
--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.


疯子阿虹

unread,
Jul 4, 2007, 9:32:40 PM7/4/07
to 高性能网络编程邮件列表
内存管理分成好几种,首先你不要搞混了,关于这个分类,IBM Developer有一篇文章作了详细的说明。

譬如上面几位都有提到的STL和DLMalloc,当然你也可以简单的使用Lookaside技术。

前者一般是标准的内存池管理实现,而后者则是标准的通用内存管理器实现。

再者从实际上运用来说,他们都各有所长,STL那个很擅长小对象分配,因为他只管理128k以下的小内存,再大的扔给系统。

而DLMalloc,也就是linux上使用的那个,比较适合替换程序中的global new。因为它通吃任何大小的内存。

至于Lookaside,则是太简单不过,我见过很多人的内存管理都是用这种方案,做一个模版类,然后初始分配1000块,

使用一个栈或列表机制,完成其new和delete的处理。当然,你也可以认为STL是一个Lookaside机制的管理器:)

其实除此之外,另外还有一个大家可能不太熟悉的开源研究项目,那就是PTMalloc,

我看过作者的文章,他的意思是这是一个parallelism+scalable的内存分配系统,在多线程上表现俱佳。

关于其他补充的细节,在MSDN上,你可以找到一篇文章:《堆:欢乐和痛苦》(有中文版了),

里面也讲道了关于内存的一些特性。其实当时我在看这篇文章的时候,比较感兴趣他说的一句话:

"由于几个同事的努力和辛勤工作,1998 年初 Microsoft Windows(R) 2000 中有了几个重大改进:"

关于这句话我当时特意做了例子测试了一下(XP系统),但是发现还是很慢。

可是后来我在一个需要大量分配小内存的产品中(完整运行一次大概需要3小时,大概有800万个小对象分配),

加了一个宏来开启或者管理内存池,使用STL的那种方案,结果发现开启和不开启仅仅差几十秒钟,那可是3小时周期的啊......

至于这个原因,根据我的测试经验,发现在多线程下分配大内存,系统会很慢,但是如果是小内存,似乎也可以承受。

在单线程情况下,系统的分配速度已经很快了。所以楼主在单线程下测试毫无意义。

单线程+内存池,听起来就有一些不平坦。

好了,说了这么多,我觉得,是否使用内存管理,要看个人的判断能力,代码能力,个人经验以及

程序中内存的使用次数多少,或者程序的profile等等决定。

单独的问是否使用内存池,就象问自己,明天是否还会在这家公司上班一样,问题简单,而其答案变幻莫测。


疯子阿虹

unread,
Jul 4, 2007, 9:33:49 PM7/4/07
to 高性能网络编程邮件列表
STL那个很擅长小对象分配,因为他只管理128k以下的小内存

笔误,128 bytes,呵呵~

疯子阿虹

unread,
Jul 4, 2007, 9:36:50 PM7/4/07
to 高性能网络编程邮件列表
靠,刚才看了一遍文章,总是感觉ptmalloc不对,才想起来,我文中提到的那个实验室的内存分配器是Hoard。

在yahoo group上有它的讨论组,有兴趣可以去看看。

kuoxin

unread,
Jul 19, 2007, 5:15:18 AM7/19/07
to 高性能网络编程邮件列表
请不要一概而论STL只能擅长小对象分配,这点要根据不同的STL版本和使用的容器而定,请参考effective STL

zsniper

unread,
Aug 16, 2007, 5:24:49 AM8/16/07
to 高性能网络编程邮件列表
你好,你能发一份云风的内存池代码给我吗?谢谢。。。。

christanxw

unread,
Aug 17, 2007, 9:24:02 AM8/17/07
to 高性能网络编程邮件列表

一般来说用对象池还不够么?

liam

unread,
Aug 23, 2007, 2:42:50 AM8/23/07
to 高性能网络编程邮件列表
内存池个人感觉不需要过于复杂.否则产生的结果是适得其反,在网游中我们用的最多的地方是基于缓冲区.或协议.通常我们都会预先分配一定buf供等待建
立连接的client使用,在buf和逻辑之间可能还会有一曾,我们管理的时候我遇到过下面两种实做手法.一种是直接对buf操作.比如说接收.我们在
buf中有数据的时候对buf进行加锁,然后有上层逻辑对该字节流进行解析,同时供逻辑使用,完毕后解锁.另一种做法是.把buf中的数据整理成逻辑
包.然后交由队列,然后由执行逻辑从队列中取数据执行.这时也会涉及数据重构.
这两种都有成功案例的.不过我采用的第二种.主要原因我觉得逻辑清晰,并且不会带来阻塞.我把缓冲区和逻辑包都用内存池管理.它是固定大小的.对于包好
像我定义的8K,也就是逻辑包最大包长不超过8K,对很多小包来说.也许是一种浪费.不过管理方便.并且现在内存也便宜.:),距离实做,可以参考其它
前辈的例子.都差不多的..

On 7月1日, 下午12时28分, 关中刀客 <guanzhongda...@gmail.com> wrote:

liam

unread,
Aug 23, 2007, 2:43:07 AM8/23/07
to 高性能网络编程邮件列表

sunway

unread,
Aug 23, 2007, 2:55:24 AM8/23/07
to 高性能网络编程邮件列表
8K大了点,我一般喜欢用4K,这样浪费稍微少了点。

Alec

unread,
Aug 23, 2007, 3:16:31 AM8/23/07
to dev4s...@googlegroups.com
实战中偶觉得内存池很必要。 对象池一般都是基于内存池的,内存池块对象池自
然也就快了。
内存池比 new/delete 慢通常都是设计有问题。

常规步骤就是预先分配一大块内存,然后切分成一个个的块(我爱叫 chunk 或者
node),供分配.
一般来说,每次内存池枯竭的时候内存池再向系统申请一块比预先分配的大块内存更大
(一般是 *2) 的一块内存,所谓的用户需求猜测原则,每次申请的大内存块按一个
基数倍增,

一般大块内存块用一个单链将块串在一起就可以了,被切分出来的 chunk 指针用
一个 std::list 串起
来就可以了。

我用这种方法实现的 memory/object pool 比 new/delete 快 30 ~ 50 倍左右。
而且基本没有什么内存碎片。

我觉得关键就是要每次都要向系统申请一个大内存块,偶看过很多简单实现都是做
一个 for, 然后循环
new 出很多 chunk 放到一个容器(比如 vector/map) 里,内存碎片太多。

arcnode

unread,
Aug 23, 2007, 4:18:10 AM8/23/07
to 高性能网络编程邮件列表
to Alec:

我用这种方法实现的 memory/object pool 比 new/delete 快 30 ~ 50 倍左右。
而且基本没有什么内存碎片。

你的处理了多线程同步吗,是不同步状态测试的速度比吧。我测试的结果是如果不处理同步(其实这种情况意义不是很大)速度比是65:1,处理同步速度比
是 n : 1 其中 n 介于 (1.x , 3.x) 之间

我对数据包不是分配固定大小的,而是圆整到一个尺寸,每次发送的时候浪费一点点,发送的时候吧要发送的东西挂在一起,等可发送的时候WSASend一把
吧多个包绑定发送出去,接收的时候用一个4k或8k的块接收。

!张沈鹏(电子科大 08年毕业)

unread,
Aug 23, 2007, 9:28:53 PM8/23/07
to dev4s...@googlegroups.com
很久没有写c++,下面的话如果有错误,请指教:)
1.我记得stl中是有内存池的(不应该用list,map这些高级组件再去实现)
大家可以参考这篇文章
标准库 STL :Allocator能做什么?
摘录一点如下
________________________________________________________________________
每个 C++程序员都已经知道动态内存分配:写下new X来分配内存和创建一个X类型的新对象,写下delete
p来销毁p所指的对象并归还其内存。你有理由认为allocator会使用new和delete--但它们没有。(C++标准将::operator
new()描述为"allocation function",但很奇怪,allocator并不是这样的。)

有关allocator的最重要的事实是它们只是为了一个目的:封装STL容器在内存管理上的低层细节。

________________________________________________________________________
2.不知道大家试过boost::pool没有
http://www.boost.org/libs/pool/doc/index.html
摘录一点如下
What is Pool?

Pool allocation is a memory allocation scheme that is very fast, but
limited in its usage. For more information on pool allocation (also
called "simple segregated storage"), see the concepts document.

在 07-8-23,arcnode<arc...@gmail.com> 写道:


--
我的博客:http://zsp.javaeye.com/

-- 张沈鹏(ZhangShen Peng)

sunway

unread,
Aug 23, 2007, 10:11:01 PM8/23/07
to 高性能网络编程邮件列表
你去看看大家常用的vc带的stl的allocator的代码把,看看有没有内存池?


On 8月24日, 上午9时28分, "!张沈鹏(电子科大 08年毕业)" <zsp...@gmail.com> wrote:
> 很久没有写c++,下面的话如果有错误,请指教:)
> 1.我记得stl中是有内存池的(不应该用list,map这些高级组件再去实现)
> 大家可以参考这篇文章
> 标准库 STL :Allocator能做什么?
> 摘录一点如下
> ________________________________________________________________________
> 每个 C++程序员都已经知道动态内存分配:写下new X来分配内存和创建一个X类型的新对象,写下delete
> p来销毁p所指的对象并归还其内存。你有理由认为allocator会使用new和delete--但它们没有。(C++标准将::operator
> new()描述为"allocation function",但很奇怪,allocator并不是这样的。)
>
> 有关allocator的最重要的事实是它们只是为了一个目的:封装STL容器在内存管理上的低层细节。
>
> ________________________________________________________________________

> 2.不知道大家试过boost::pool没有http://www.boost.org/libs/pool/doc/index.html


> 摘录一点如下
> What is Pool?
>
> Pool allocation is a memory allocation scheme that is very fast, but
> limited in its usage. For more information on pool allocation (also
> called "simple segregated storage"), see the concepts document.
>

> 在 07-8-23,arcnode<arcn...@gmail.com> 写道:

arcnode

unread,
Aug 23, 2007, 10:36:32 PM8/23/07
to 高性能网络编程邮件列表
to 张沈鹏:
可以断定一点你是没有写过mempool或objpool的,如果你用stl的组件去实现,那么速度和new/malloc比较不是n:1,而是
1:n

实际开发中往往需要的是一个简单的高效的牵扯少的模块,如果其他部分没有用boost我想大多数开发人员不会单为了一个mempool而去导入一个
boost库。

sunway

unread,
Aug 23, 2007, 10:48:27 PM8/23/07
to 高性能网络编程邮件列表
vc 带的stl是没有用内存池的,他使用系统的new delete这类操作来分配和释放内存
实际在new delete内部是有内存分配器的,只不过效率一向为大家所诟病。
在服务器端是需要内存池的,尤其是大块数据的分配(>1k)。

On 8月24日, 上午9时28分, "!张沈鹏(电子科大 08年毕业)" <zsp...@gmail.com> wrote:

> 很久没有写c++,下面的话如果有错误,请指教:)
> 1.我记得stl中是有内存池的(不应该用list,map这些高级组件再去实现)
> 大家可以参考这篇文章
> 标准库 STL :Allocator能做什么?
> 摘录一点如下
> ________________________________________________________________________
> 每个 C++程序员都已经知道动态内存分配:写下new X来分配内存和创建一个X类型的新对象,写下delete
> p来销毁p所指的对象并归还其内存。你有理由认为allocator会使用new和delete--但它们没有。(C++标准将::operator
> new()描述为"allocation function",但很奇怪,allocator并不是这样的。)
>
> 有关allocator的最重要的事实是它们只是为了一个目的:封装STL容器在内存管理上的低层细节。
>
> ________________________________________________________________________

> 2.不知道大家试过boost::pool没有http://www.boost.org/libs/pool/doc/index.html


> 摘录一点如下
> What is Pool?
>
> Pool allocation is a memory allocation scheme that is very fast, but
> limited in its usage. For more information on pool allocation (also
> called "simple segregated storage"), see the concepts document.
>

> 在 07-8-23,arcnode<arcn...@gmail.com> 写道:

!张沈鹏(电子科大 08年毕业)

unread,
Aug 24, 2007, 1:48:31 AM8/24/07
to dev4s...@googlegroups.com
1.首先,附件里面有一份不错的关于内存池的文章(不过是很久以前的文章了)

2.我boost的虽然有一点牵扯,但是可以试一试?boost还是很好用的:)

3.我的意思不是用stl的组件去实现内存池,而是直接调用它的allocator.不过看来有的stl库的allocator没有实现内存池,有的实现了,不统一:).

所以,我现在认为实现一个allocator,作为参数传给stl容器:)附件文章中有介绍

最后,我承认,我没有写过mempool或objpool的,纸上谈兵:)


在 07-8-24,sunway<sunh...@gmail.com> 写道:

池内春秋.pdf
Memory Pool 的设计哲学与无痛运用.htm

sunway

unread,
Aug 24, 2007, 2:38:24 AM8/24/07
to 高性能网络编程邮件列表
你附带的文件很早就看过, stl的allocator我们项目里已经实现了,测试下来性能提升比较大,
类arcnode的测试数据,VC的STL算法并不好,适合客户端开发,就拿一个vector来说,他的clear
方法居然会释放内存,在服务器端这个操作不是很有必要,因为大家一般都在初始化下vector的大小
, clear方法只需要设置下长度就OK了。在服务器端开发还是自己去实现一些容器比较好。

On 8月24日, 下午1时48分, "!张沈鹏(电子科大 08年毕业)" <zsp...@gmail.com> wrote:
> 1.首先,附件里面有一份不错的关于内存池的文章(不过是很久以前的文章了)
>
> 2.我boost的虽然有一点牵扯,但是可以试一试?boost还是很好用的:)
>
> 3.我的意思不是用stl的组件去实现内存池,而是直接调用它的allocator.不过看来有的stl库的allocator没有实现内存池,有的实现了,不 统一:).
>
> 所以,我现在认为实现一个allocator,作为参数传给stl容器:)附件文章中有介绍
>
> 最后,我承认,我没有写过mempool或objpool的,纸上谈兵:)
>
> 在 07-8-24,sunway<sunhui...@gmail.com> 写道:

> 池内春秋.pdf
> 625K下载
>
> [Memory Pool 的设计哲学与无痛运用.htm]
> ± ... § Memory Pool ,,è è " - - è "¨
>
>
>
> - programmer-13-memory-pool.pdf
>
> ,, è€...: "Peng Chunhua" <chp...@psh.com.cn>
> é€ - : 2002 9 12 - AM 11:00
> -¨: ... - [ ± ... § ] - ,, € é- é
>
>
>
> To: ... "
> ,¨ € ... " ,, - ,, ... è è° ' é- é è è ... é € è · é ... €,è° è° €,
> ... " ¨ €è ' € ,, € ¨ ' € , - 2002 9 ‰ 'è ¨ € - [ ± ... § ] Memory Pool ,, € €, è é " €é ¨ € ¨ ' € € è 'è ¨ €, ‰€ ‰ è ¤ ... Memory Pool ,, è ¨ ... €, è ¨ ... - [ é- ,,é ¤- €é"€] ' [é€ ,,é ¤- €é"€] ,,è · ‰ , -'é- ... " · €,
> € é- ,,é ¤- €é"€
> ... " è ¤ ¨C++ ° ,, ... é... · ...· é ¤- ,,é... Cookie €, è è è €,
> ... 'è ¤ è è ‰è ,, é - ... VC6 ' C++Builder ¨ ... °± € · ,, €,é€ è ' ' °new ¨ · ±,è, è° "¨malloc ,, ¨C++ ° ‰ €, ‰€ è € malloc ,, ° °± €, ¨VC6.0 , "¨Debug ‰ ,,è °± ' ... " è ,, € · ‰é ¤- €é"€ 32 -è , "¨ è ° · ... é" è ¨ ' é... ... ,, - è · -è , ·° é... ,, € €,è °± VC6 ¨Debug ‰ € ... ,, ,, €, ...· " è€ € malloc ,,Debug ‰ ° ,, °± ,, €, ¨VC6.0 ,,Release ‰ °± ‰è é ¤-è ° · malloc ,, ° è° "¨HeapAlloc ,, é " è° "¨HeapFree ,, VC è ‰ ... è è · ¤ é € ,, " €, ‰€ ¨Release ‰ malloc ,,è " € ‰ - HeapAlloc ,, € è€ Debug malloc ,, € °± ‰ - HeapAlloc() + 0x20 ,, € €,è - ... ,, ¤§ ° ¨Release ‰ é€ è HeapSize - ° ,, €, " ¨VC € ¨ - ‰€ ‰ ,,malloc, newé ¨ € Heap " ,, ¨VC ,,è è _crtheap €,è °± €é ¨ " è ·10000000 C1 ,, è± - VC "C++ Builder è ±è ,, - é- "è é· ,, € €, è ¨Release ‰ " è · ... ,, ¤ é ¨ ‰32 -è ,è ° · ... ,, € " , ¤§ ° €, è Cookie ' C++ ° - ... °± è ‰€ ‰ "¨HeapAlloc é... ,, ... ¨ ¤ é ¨é ‰32 -è , ,,é ¤- €é"€ €, 32 -è , " ¨ " è · ... ,, ° ‰ € è ° ‰
> ¨C++Builder ... ' VC € · ,, €,C++Buider "¨Heapè è ... ,, é... è€ è è é€ è TMemoryManagerè è ... €, ...· " - é€ è VirtualAlloc € é... 16KByte -è , " ¨ é€ è malloc é... ... - C++Builder °±é è è ,, ... ° é- ,, ... è è é... ¨ ¤ é ¨è ° · ... ,, ¤§ ° €, " ... è - € è° "¨VirtualAlloc ' " è · ... "±C++Builderè è é... €, ‰€ ... " ¨ é- è è C++Builderè ¨ ° €,
> é€ è
> ‰ é "è VC6.0 ' C++Builder ,, ... ,, é, é - ¨é€ VC ' C++Builder ,, · , °± é è§ €,VC ,, ... é... ¨ € ¨ - é ¨_crtheap é... ,, " " è · ... - VC ...é "± Heapé · Heap è è " è · €,è€ C++Builder é €é - ... é è è ,,VirtualAllocé" è ¨ ' °VirtualAlloc ‰ é- ... - é è VirtualAlloc ,, ... €,è · "± - € VirtualAlloc " ¤ malloc ,, ... ¨é · ... ,, - é- °± "VC €, " , € VirtualAlloc ,, ... m malloc é, é · é... n*m ... è€ è¨€ é... € ... ,,è ±è
> VC = n*m
> C++Builder=n+m
> · ¨ - , C++Builder ¨ ... € VirtualAlloc ,, ¤§ ° 16K(0x4000)è€ ¤§ ¨è § é... 1000000 ... - é€ "è € , €,( è , · °è ) é C++Builder ,, ... °± è§ €é ¨C++Builder ,, ¨ ° è € - ... " è · ' é " é€ è TaskManagerè§, ... "¨é € ° ‰§è è " ‰ €, VirtualAlloc ,, € - ... è "¨ ,,è ... - ... é è é " C++Builder è ° è ... ¤ é " €,
> - GCC ,, -è ' ¨ ' ‰ " è ¨ ¤ · 'è ¨ · €,
>
>
>
> E-mail:chp...@psh.com.cn
>
>
>
> - · è
>
>
>
> ,, è° ,¨ ,, ,, è§ ' è ...... €, ‰ · € è · ... è ' - ° ,¨ ,, " ¨ · ' " ± ... § " € - ,,è ...... €,
> ' ° è ¤ " € è€ ‰€ ,, € ·± ¤,, €, ...
>
> 阅读更多 - 隐藏被引用文字 -
>
> - 显示引用的文字 -

Robert FENG

unread,
Sep 3, 2007, 9:37:00 PM9/3/07
to 高性能网络编程邮件列表
boost的内存池用起来效率确实是高,但还是有些诡异啊。我们用过Boost的singleton_pool的时候,当开一个pool后能够分配
1GB上限的内存,然后再开一个pool时只能分到约500MB上限的内存了,没有查到相应的文档解释这种事情啊。

On 8月24日, 上午9时28分, "!张沈鹏(电子科大 08年毕业)" <zsp...@gmail.com> wrote:

> 很久没有写c++,下面的话如果有错误,请指教:)
> 1.我记得stl中是有内存池的(不应该用list,map这些高级组件再去实现)
> 大家可以参考这篇文章
> 标准库 STL :Allocator能做什么?
> 摘录一点如下
> ________________________________________________________________________
> 每个 C++程序员都已经知道动态内存分配:写下new X来分配内存和创建一个X类型的新对象,写下delete
> p来销毁p所指的对象并归还其内存。你有理由认为allocator会使用new和delete--但它们没有。(C++标准将::operator
> new()描述为"allocation function",但很奇怪,allocator并不是这样的。)
>
> 有关allocator的最重要的事实是它们只是为了一个目的:封装STL容器在内存管理上的低层细节。
>
> ________________________________________________________________________

> 2.不知道大家试过boost::pool没有http://www.boost.org/libs/pool/doc/index.html


> 摘录一点如下
> What is Pool?
>
> Pool allocation is a memory allocation scheme that is very fast, but
> limited in its usage. For more information on pool allocation (also
> called "simple segregated storage"), see the concepts document.
>

> 在 07-8-23,arcnode<arcn...@gmail.com> 写道:

Reply all
Reply to author
Forward
0 new messages