完成端口内存池设计时的想法,请大家看看我想的对不对

badbadbad 2010-03-12 10:48:17
加精
最近在写高并发服务器用到了完成端口:

1、完成端口对输入输出的处理需要预先申请一个Overlapped缓存空间,并且要保持该缓存所在页面一直呆在物理内存里(一个页面大小根据情况有所不同,我的破电脑页大小是4K,也就是说如果你有一块Overlapped缓存虽然只有100个字节,但是整个4K的页都必须呆在物理内存里),而不是像一般空间一样可以被系统随意调度出物理内存(windows内存管理机制:长时间不用到的页会从物理内存中转出),但是这带来一个问题:如果输入输出请求量过于巨大,则导致被锁定的页太多,从而使的物理空间被占满影响到其他程序的运行,甚至完成端口本身也无法响应请求。(虽然windowsNT会对锁定页面做限制:即大概是不能超过物理内存空间的1/8)。

2、每次进行I/O操作时都要申请新的内存,用完后再释放,显然是非常影响运行效率的,而且也会产生大量的内存碎片。所以一般情况下高性能的服务器要求:在程序运行之初先把需要用到的内存全部申请好,每次需要内存的时候直接拿来用就可以了,等到程序退出的时候再把内存一次性释放掉。-----这个就是内存池技术。

我从网上看了很多人的内存池代码,发现他们有一个比较大的缺陷:他们的内存池不是一整块申请,而是一小块一小块申请,比如:

for i := 2 to IO_MEM_MAX_COUNT do
begin
PUNode(IOMemLast)^.Next := HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(TIOMem));
IOMemList[i] := PUNode(IOMemLast)^.Next;
IOMemLast := PUNode(IOMemLast)^.Next;
end;

我觉的这样申请的内存有一个很大的问题:由于这些一块一块的内存不是一起申请的,所以地址是不连续的,也就是这些内存会分散在不同的页中,这样就导致被锁定在物理内存中的页无形中多了很多,这应该很好理解的。

所以我认为应该一次性申请地址空间连续的内存,这样整齐的内存会减少很多被锁定的页面。

------------------------------------------------不一定成熟的想法。
...全文
2985 129 打赏 收藏 转发到动态 举报
写回复
用AI写文章
129 条回复
切换为时间正序
请发表友善的回复…
发表回复
zou_cplus 2011-09-04
  • 打赏
  • 举报
回复
hulose
说的不错 就现在而言 服务器的内存是足够用了 用固定大小的内存池 能够提高使得应用程序不用频繁的申请和释放空间。还有不会产生内存碎片。试想一下 你的服务器开一个月 因为内存碎片就要重启一次。这样还能做服务器吗
jasonke 2010-11-09
  • 打赏
  • 举报
回复
交给 fastmm, 并使用 getmem 代替 HeapAlloc ,一切问题解决, fastmm对碎片管理非常好
p2p_soft 2010-09-25
  • 打赏
  • 举报
回复
沙发,学习中
萨弗迪发个 2010-09-18
  • 打赏
  • 举报
回复
其实这个那个。我是来拿分的。!
yangtao639999 2010-07-27
  • 打赏
  • 举报
回复
正在学习 ,占位~
hulose 2010-07-17
  • 打赏
  • 举报
回复
我最近刚完成 一个IOCP模型的TCPServer

内存池 16K为最小 然后 64, 128, 256K最大 大于256K的采用动态申请(因为理论业务包不大可能达到这么大 就算有数据包很大 也肯定要拆解为小包来通讯)

比如 16K包做为 AcceptEx投递 以及 WSARecv接收用

其它大小(包括16K) 用于 发送

小于16K的的数据 就用16K 小于32K的数据 用 32K 以此类推

或许有人会说 那是不是很浪费内存?

那是肯定会浪费一些的 不过 你不要忘了 现在的服务器 小的至少4G内存 8G内存的遍地都是

16K 申请1w个好了 也不过160M而以 其它的 可以跟据自己的业务包 大小比例 业务使用频繁率 决定初始化个数

牺牲容易换取性能 这是值得的

再说了 可能你的业务量根本用不了申请这么多的量

你能保证你的连接数能达到 一定的量再说吧 你要记得一点 你开发的是服务器 不是拿PC去思考

当然 回过来想 怎么样最合理 什么样最优化 最终还是要考虑的 不过那些都是后事了


hulose 2010-07-17
  • 打赏
  • 举报
回复
在中小型的系统下 其实根本 不用太在意这个

内存池完全 可以 按定长申请 无需用多大 就是申请多大

如果你可以把池中缓冲区默认 分为 如 4K 8K 16,....256K

跟据需求 分别申请最合适的
lapal 2010-03-23
  • 打赏
  • 举报
回复
好东东,学习了
shify2000 2010-03-21
  • 打赏
  • 举报
回复
学习了,,
lin_style 2010-03-21
  • 打赏
  • 举报
回复
[Quote=引用 116 楼 babyboy 的回复:]

完成端口?上个世纪的技术,怎么还有人再用?设计大并发,你还用Windows?epoll,libevent,ACE都是现成
设计大并发服务器的技术,ACE的内存池,APR的内存池都是现成的东西,不要再自己发明砖头了。
[/Quote]

讲讲原理?
babyboy 2010-03-19
  • 打赏
  • 举报
回复
完成端口?上个世纪的技术,怎么还有人再用?设计大并发,你还用Windows?epoll,libevent,ACE都是现成
设计大并发服务器的技术,ACE的内存池,APR的内存池都是现成的东西,不要再自己发明砖头了。
ysr1980 2010-03-19
  • 打赏
  • 举报
回复
我觉得可以这样处理, 首先申请一块大小为A的内存池,然后做一个链表组freeLists[N]来管理对内存池空间的申请和释放管理,链表组分别管理大小为8、16、24、...(大小根据实际需要调整),当你申请20Byte的空间时,就从freeLists[20/8]的链表上取空的空间,如果没有,从内存池中申请24Btye空间。当内存池的大小A用完时,从新申请一块新的内存空间,这里的处理视情况而定,可以申请一块2A空间,而后把A中的数据拷贝到新的空间里,释放原来的空间,不过这种情况下注意指针的失效;也可以再申请一块A大小的空间,不过要一个新的链表来记录2块内存,便于释放。当然,还有一些细节要考虑,比如,当你申请20Btye的空间时,24链表上没有空间,内存池也没有空间,但32的链表上有空间,此时需要将32Btye拆分成8+24,再去链表上申请空间,而不是申请新的空间等等。大致的算法就是这样, 具体看情况做修订和改型。最近在做一个共享内存的实现vector效果的东西,感觉和你的有点相似,供楼主参考。具体参考《STL源码剖析》2.2.6第二级配置器__default_alloc_template剖析 ,根据它的算法改型。记得给分^o^
supview 2010-03-19
  • 打赏
  • 举报
回复
关注,不知道哪里有IOCP的开源项目可以学习下!
精锐掷矛手 2010-03-19
  • 打赏
  • 举报
回复
[Quote=引用 117 楼 cyblueboy83 的回复:]
恩,使用内存池的方法也不失是一个好方法

不过一般的做法应该都是事先给每个节点分配 一定大小的缓存,例如 1M..
[/Quote]
节点1M是不是太大了?
herman~~ 2010-03-19
  • 打赏
  • 举报
回复
恩,使用内存池的方法也不失是一个好方法

不过一般的做法应该都是事先给每个节点分配 一定大小的缓存,例如 1M..
HiIan 2010-03-18
  • 打赏
  • 举报
回复
学习学习,谢谢!
wfl568 2010-03-18
  • 打赏
  • 举报
回复
先Mark一下,看高手解答
chengwanfei 2010-03-18
  • 打赏
  • 举报
回复
不错 学些了 哈哈
woainihuajia 2010-03-18
  • 打赏
  • 举报
回复
learning.......start learning
heweilin 2010-03-18
  • 打赏
  • 举报
回复
学习 呵呵
加载更多回复(106)

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧