没有合适的资源?快使用搜索试试~ 我知道了~
c++应用程序性能优化之内存池
4星 · 超过85%的资源 需积分: 9 77 下载量 95 浏览量
2010-10-15
11:33:02
上传
评论 1
收藏 221KB PDF 举报
温馨提示
试读
20页
本文摘自于《C++应用程序性能优化》,介绍了自定义内存池性能优化的原理,然后列举软件开发中常用的内存 池的不同类型,并给出具体实现的实例。
资源推荐
资源详情
资源评论
引言
本书主要针对的是 C++ 程序的性能优化,深入介绍 C++ 程序性能优化的方法和实
例。全书由 4 个篇组成,第 1 篇介绍 C++ 语言的对象模型,该篇是优化 C++ 程
序的基础;第 2 篇主要针对如何优化 C++ 程序的内存使用;第 3 篇介绍如何优化
程序的启动性能;第 4 篇介绍了三类性能优化工具,即内存分析工具、性能分析工
具和 I/O 检测工具,它们是测量程序性能的利器。
本章首先简单介绍自定义内存池性能优化的原理,然后列举软件开发中常用的内存
池的不同类型,并给出具体实现的实例。
6.1 自定义内存池性能优化的原理
如前所述,读者已经了解到"堆"和"栈"的区别。而在编程实践中,不可避免地要大
量用到堆上的内存。例如在程序中维护一个链表的数据结构时,每次新增或者删除
一个链表的节点,都需要从内存堆上分配或者释放一定的内存;在维护一个动态数
组时,如果动态数组的大小不能满足程序需要时,也要在内存堆上分配新的内存空
间。
6.1.1 默认内存管理函数的不足
利用默认的内存管理函数 new/delete 或 malloc/free 在堆上分配和释放内存会有一
些额外的开销。
系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并
且需要根据一定的算法(例如分配最先找到的不小于申请大小的内存块给请求者,
或者分配最适于申请大小的内存块,或者分配最大空闲的内存块等)找到合适大小
的空闲内存块。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲
块。然后系统更新内存空闲块表,完成一次内存分配。类似地,在释放内存时,系
统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,可以把相邻的空
闲块合并成较大的空闲块。
默认的内存管理函数还考虑到多线程的应用,需要在每次分配和释放内存时加锁,
同样增加了开销。
可见,如果应用程序频繁地在堆上分配和释放内存,则会导致性能的损失。并且会
使系统中出现大量的内存碎片,降低内存的利用率。
默认的分配和释放内存算法自然也考虑了性能,然而这些内存管理算法的通用版本
为了应付更复杂、更广泛的情况,需要做更多的额外工作。而对于某一个具体的应
用程序来说,适合自身特定的内存分配释放模式的自定义内存池则可以获得更好的
性能。
6.1.2 内存池的定义和分类
自定义内存池的思想通过这个"池"字表露无疑,应用程序可以通过系统的内存分配
调用预先一次性申请适当大小的内存作为一个内存池,之后应用程序自己对内存的
分配和释放则可以通过这个内存池来完成。只有当内存池大小需要动态扩展时,才
需要再调用系统的内存分配函数,其他时间对内存的一切操作都在应用程序的掌控
之中。
应用程序自定义的内存池根据不同的适用场景又有不同的类型。
从线程安全的角度来分,内存池可以分为单线程内存池和多线程内存池。单线程内
存池整个生命周期只被一个线程使用,因而不需要考虑互斥访问的问题;多线程内
存池有可能被多个线程共享,因此则需要在每次分配和释放内存时加锁。相对而言,
单线程内存池性能更高,而多线程内存池适用范围更广。
从内存池可分配内存单元大小来分,可以分为固定内存池和可变内存池。所谓固定
内存池是指应用程序每次从内存池中分配出来的内存单元大小事先已经确定,是固
定不变的;而可变内存池则每次分配的内存单元大小可以按需变化,应用范围更广,
而性能比固定内存池要低。
6.1.3 内存池工作原理示例
下面以固定内存池为例说明内存池的工作原理,如图 6-1 所示。
图 6-1 固定内存池
固定内存池由一系列固定大小的内存块组成,每一个内存块又包含了固定数量和大
小的内存单元。
如图 6-1 所示,该内存池一共包含 4 个内存块。在内存池初次生成时,只向系统申
请了一个内存块,返回的指针作为整个内存池的头指针。之后随着应用程序对内存
的不断需求,内存池判断需要动态扩大时,才再次向系统申请新的内存块,并把所
有这些内存块通过指针链接起来。对于操作系统来说,它已经为该应用程序分配了
4 个等大小的内存块。由于是大小固定的,所以分配的速度比较快;而对于应用程
序来说,其内存池开辟了一定大小,内存池内部却还有剩余的空间。
例如放大来看第 4 个内存块,其中包含一部分内存池块头信息和 3 个大小相等的内
存池单元。单元 1 和单元 3 是空闲的,单元 2 已经分配。当应用程序需要通过该内
存池分配一个单元大小的内存时,只需要简单遍历所有的内存池块头信息,快速定
位到还有空闲单元的那个内存池块。然后根据该块的块头信息直接定位到第 1 个空
闲的单元地址,把这个地址返回,并且标记下一个空闲单元即可;当应用程序释放
某一个内存池单元时,直接在对应的内存池块头信息中标记该内存单元为空闲单元
即可。
可见与系统管理内存相比,内存池的操作非常迅速,它在性能优化方面的优点主要
如下。
(1)针对特殊情况,例如需要频繁分配释放固定大小的内存对象时,不需要复杂的
分配算法和多线程保护。也不需要维护内存空闲表的额外开销,从而获得较高的性
能。
(2)由于开辟一定数量的连续内存空间作为内存池块,因而一定程度上提高了程序
局部性,提升了程序性能。
(3)比较容易控制页边界对齐和内存字节对齐,没有内存碎片的问题。
回页首
6.2 一个内存池的实现实例
本节分析在某个大型应用程序实际应用到的一个内存池实现,并详细讲解其使用方
法与工作原理。这是一个应用于单线程环境且分配单元大小固定的内存池,一般用
来为执行时会动态频繁地创建且可能会被多次创建的类对象或者结构体分配内存。
本节首先讲解该内存池的数据结构声明及图示,接着描述其原理及行为特征。然后
逐一讲解实现细节,最后介绍如何在实际程序中应用此内存池,并与使用普通内存
函数申请内存的程序性能作比较。
6.2.1 内部构造
内存池类 MemoryPool 的声明如下:
class MemoryPool
{
private:
MemoryBlock* pBlock;
USHORT nUnitSize;
USHORT nInitSize;
USHORT nGrowSize;
public:
MemoryPool( USHORT nUnitSize,
USHORT nInitSize = 1024,
USHORT nGrowSize = 256 );
~MemoryPool();
void* Alloc();
void Free( void* p );
};
MemoryBlock 为内存池中附着在真正用来为内存请求分配内存的内存块头部的结构
体,它描述了与之联系的内存块的使用信息:
struct MemoryBlock
{
USHORT nSize;
USHORT nFree;
USHORT nFirst;
USHORT nDummyAlign1;
MemoryBlock* pNext;
char aData[1];
剩余19页未读,继续阅读
资源评论
- jyj07102019-07-12还可以吧,有得用
conangjm
- 粉丝: 3
- 资源: 3
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功