使用两种内存池分别分配1到1024字节,总计执行100次,也就是102400次内存分配与释放,共计两个用例。
每一种用例执行10次。
下面是测试结果
ACE_Cached_Allocator的时间是很稳定,而且基本上分配的时间开销在30ms左右
而ACE_Local_Memory_Pool的时间第一次分配时间开销较大这个可以理解,
之后释放重新分配时间逐渐减少,在第8,9,10次测试用例不再下降,基本上达到了一个平均值,
不过大概仍然是ACE_Cached_Allocator时间开销的2倍以上,当然这也大体符合预期。
begin Test nLoopA 100 nLoopB 1024 class ACE_Allocator_Adapter<class ACE_Malloc_T<class ACE_Local_Memory_Pool,class ACE_Thread_Mutex,class ACE_Control_Block> >
malloc count 0 ,cost 11844ms
free count 0 ,cost 125ms
malloc count 1 ,cost 1156ms
free count 1 ,cost 47ms
malloc count 2 ,cost 593ms
free count 2 ,cost 32ms
malloc count 3 ,cost 328ms
free count 3 ,cost 32ms
malloc count 4 ,cost 188ms
free count 4 ,cost 15ms
malloc count 5 ,cost 109ms
free count 5 ,cost 32ms
malloc count 6 ,cost 110ms
free count 6 ,cost 15ms
malloc count 7 ,cost 78ms
free count 7 ,cost 16ms
malloc count 8 ,cost 63ms
free count 8 ,cost 15ms
malloc count 9 ,cost 78ms
free count 9 ,cost 16ms
begin Test nLoopA 100 nLoopB 1024 class ACE_Cached_Allocator<char ,class ACE_Thread_Mutex>
malloc count 0 ,cost 32ms
free count 0 ,cost 0ms
malloc count 1 ,cost 31ms
free count 1 ,cost 16ms
malloc count 2 ,cost 15ms
free count 2 ,cost 16ms
malloc count 3 ,cost 15ms
free count 3 ,cost 16ms
malloc count 4 ,cost 32ms
free count 4 ,cost 0ms
malloc count 5 ,cost 32ms
free count 5 ,cost 15ms
malloc count 6 ,cost 15ms
free count 6 ,cost 16ms
malloc count 7 ,cost 15ms
free count 7 ,cost 16ms
malloc count 8 ,cost 31ms
free count 8 ,cost 0ms
malloc count 9 ,cost 32ms
free count 9 ,cost 0ms
[ 本帖最后由 modern 于 2010-7-5 18:09 编辑 ] 呵呵,感谢modern的回复。
认真的读了你的回复数遍,你对ACE的理解是十分深刻的。关于Cache和Local_Pool之间的讨论,我想说一下我的看法,看了你的测试数据,我这里也给我针对这两种状态下的测试代码。
正如你所说,Cache初始化的时候,将自由的对象指针放入free_list_,而当用户获得一个新的自由对象指针的时候,链表不需要遍历,直接返回链表的第一个指针即可。对象使用完成后的归还,会在cache追加到free_list_的尾部,从而避免了循环,理论上说,无论多大的free_list_速度分配都不受影响。这一点的设计和很多先进的内存池是一致的,非常高效。
而Local_Pool的优点正如你说的那样,动态伸缩的内存块,申请出来后便放入链表中,然后根据你每次申请的内存大小去匹配自由内存块 > 你需要的内存块。如果你有一个10K的和一个1K的内存块,如果你申请一个40字节的内存块,如果10K的在链表中在前,就会先把这个10K的块给出(先进的内存池可以匹配最适合的内存块大小,并给出结果,似乎这里ACE的开发者貌似稍微偷了点懒,不过无伤大雅)。由于需要遍历,所以它的时间消耗比较长,随着数据量的增大,Local_Pool的效能会成log2N的速度增长,对比Cache。
十分同意你的观点,ACE_Data_Block大小竟然足足40个字节这部分反复的new和delete,早高并发下会造成效能的无意义的增长。这里我的想法和你一样,必须做缓冲。
正如你所说,Cache是优秀的,但是在某些需求下,可能会引起代码开发的陷阱,毕竟,像你我那样把ACE底层翻一个底掉的人不多。
因为最近我在重构我的开源的代码,遇到了很多朋友的大力支持,给我提出了各种意见。现在新版服务器代码已经被我改的天翻地覆了,等最近满足测试后我会放出来,效能已经远远超过以前的版本了。呵呵,他们提出了关于动态Message_block的管理问题,也就是我写这篇文章的主要原因。
我反复考虑Cache下的框架应用,对于框架而言,有时候对方要求内存是多少,本身很难做一个度量。如果用cache的话,或许会给框架使用上引起一些问题。所以我个人而言,而且也从代码易读性而言,Local_Pool管理动态变长的ACE_Message_block,我个人还是比较倾向的。
cache挂掉会有两种可能,都是因为fee_list找不到,返回NULL造成的,一个是找不到合适尺寸,一个是池已经用尽,低水位的问题,正如你所说,可以通过代码解决。
在这,我贴出我的测试代码,如果可以的话,同样期待modern公布一下你的内存池代码。互相交流学习一下。
正如你所说,效能差距大概在1倍左右。随着量的增加,倍数也在缓慢增长,但是并不是很大。
[ 本帖最后由 freeeyes 于 2010-7-6 16:08 编辑 ] 我改了一下,可自动扩展的ACE_Cached_Allocator,下面是刚好解决另一个兄弟这个问题的链接,
http://www.acejoy.com/bbs/viewthread.php?tid=2584&extra=page%3D1
可以解决fee_list找不到,返回NULL的问题,实测确实在超过高水平位会自动扩展,默认100,可以设置,
如果恢复到高水平一下,会自动清理到低于高水的位置。
不过性能下降较多,今天一直在解决公司的事情,回头将测试报告贴出来。 这段时间又比较忙,很早就搞完了,一直没时间贴出来。
http://www.acejoy.com/bbs/viewth ... ge=1&extra=#pid7909 本帖最后由 wight 于 2010-8-15 18:41 编辑
其实代码一点都不复杂,只是稍微添加了一句话。
如果在多线程下调用
ACE_Guard<ACE_Recursive_Thread_Mutex> WGuard(m_ThreadWriteLock);这样的线程锁是必要的。
这个地方不太明白,楼主能解释一下吗?内存分配器已经是线程安全的了。 本帖最后由 chiarier 于 2013-2-21 11:21 编辑
例子很经典。
在ACE_queue例子程序中,对于缓冲内存池分配内存失败时的判断语句pmb->base () != NULL有点小问题,主要是执行到此处时,pmb->data_block_已经被释放掉,无法继续获取pmb->data_block_->base_,建议使用pmb->data_block () != NULL代替
页:
1
[2]