《C++应用程序性能优化::第六章内存池》学习和理解&&最后的总结
说明:《C++应用程序性能优化》 作者:冯宏华等 2007年版。
内存的申请和释放对一个应用程序的整体性能影响极大,甚至在很多时候成为某个应用程序的瓶颈。消除内存申请和释放引起的方法往往是针对内存使用的实际情况提供一个合适的内存池。内存池之所以能够提供性能,主要是因为它能够利用应用程序的实际内存使用场景中的某项“特性”。比如某项内存申请与释放肯定发生在一个线程中,某项类型的对象生成和销毁与应用程序中其他类型对象要频繁得多,等等。针对这些特性可以为这些特殊的内存使用场景提供量身定做的内存池。这样就能够消除系统提供的缺省内存机制中,对于该实际应用场景中的不必要的操作,从而提升应用程序的整体性能。
2010.11.10~2010.11.11
第一版第六章即第二版第七章
一、概念
内存池,顾名思义就是放了很多“内存单元”的“池”。这些“内存单元”大小可以是固定的也可以是可变的,相应的内存池称为“固定内存池”和“可变内存池”。另外,考虑到多线程安全,内存池也可以分为多线程内存池和单线程内存池。书上的是固定大小、单线程内存池。
当应用程序需要内存单元时,便从内存池中获取。当内存池中内存单元用完时,内存池就一次性向系统申请很多个内存单元。当内存池中内存单元太多了,就一次性把很多个内存单元返还给系统。
二、思路
设计一个内存池,要考虑几个因素。1、每个内存单元的大小;2、内存单元的个数;3、内存单元之间的链接;4、给使用者提供内存单元、回收内存单元。
内存单元之间的链接,书上采用了两层的结构。把内存池分成N个大内存块,每个大内存块里边有M个小的内存单元。N个大内存块直接采用链式连接,而内存单元依据偏移来确定位置。内存池是动态变化的才有更高的效率,所以最开始的时候内存池只有一个大内存块,含有若干的内存单元。这个大内存块是一次性向系统申请的,依据各个内存单元的地址,给大块内存中的每一个内存单元“贴上”编号,在分配出内存池和回收回内存池都要用到这个编号。当内存池中没有内存单元时,一次性向系统申请一大块内存,然后初始化、分配给使用者。当内存池中出现一大块内存都是空闲时,则把大块的内存返还给系统。
内存池给使用者提供的接口是申请内存单元和返回内存单元。申请内存单元函数要到尽快的在内存池中找到一个空闲的内存单元。返回内存单元要做的是把回收的内存单元加入到内存池中。回收时把刚刚回收的那个内存单元所在的内存块放在内存池的最前面,这样可能会减少申请时遍历内存块的次数。
内存池的数据结构是非常重要的,内存池的使用就是对内存池数据结构的维护,内存池结构是整个内存池的核心。确定好的数据结构非常重要。
三、代码
https://files.cnblogs.com/cswuyg/%e5%86%85%e5%ad%98%e6%b1%a0.rar
四、一个比较难理解的地方
代码中使用的MemoryBlock结构体,在最后有一个char aData[1],这个地方如此定义结构体,书中说是为了“在对齐方式不同的各种平台上都可以工作”。这样说很难理解,我觉得可能是下边的这种情况:在书上提供的代码中可以发现,跟对齐相关的代码就是使用了sizeof。如果不是定义了一个char aData[1],那么在确定MemoryBlock中的某一个内存单元位置时,可能就要用到sizeof去获取MemoryBlock结构体的大小。而MemoryBlock结构体和它所关联的很多个内存单元是作为一块整体向系统申请内存空间的,单独对MemoryBlock结构体使用sizeof可能会因为对齐问题而使得返回的结果超过应该有它的大小,这样MemoryBlock所关联的最后一个内存单元就可能会越界。另外,觉得定义了aData[1]会方便编程。
五、如何使用
还没参加工作,暂时还没有遇到需要使用内存池的情况。本章的最后简单的说了如何使用:“声明一个静态内存池对象,同时为了让其所有对象都从这个内存池中开辟内存,需要为类重载一个new运算符。在new运算符中用内存池的Alloc函数满足所有该类对象的内存请求……”。
我觉得不一定是这样子,应该是根据实际情况去做。我尝试着去写这样一个类,但没有继续写下去,觉得写出来会有很多局限性。定义内存池时需要知道类的大小,而内存池又是类的一部分。也许可以用模板来做,修改MemoryPool提供的接口,这样子可能会通用一些。
—————————————————————————————————————————————————————————
—————————————————————————————————————————————————————————
—————————————————————————————————————————————————————————
最后的总结:
2010.11.13
这本书的第三篇是应用程序启动性能优化,第四篇是性能工具。第四篇暂时不想看。
第三篇很多都是纯粹概念的东西。动态链接库部分,linux那块我没看,windows部分,有一些知识说得很简单,如果之前没看过相关的知识是很难理解的,如:DLL的使用、PE文件的相关内容。程序启动性能部分,是讲优化时应该注意的一些地方、应该从哪个地方入手,这部分现在暂时当做概念记下一点。
这本书的前言里说“本书适用于有一定C++开发经验的开发人员,也可以作为高等院校相关专业师生的参考书。”,看到后边时,觉得有编程经验的人看这本书应该会有更多的收获,第三篇和第四篇的内容对有编程经验的人来说,可能会有深刻的理解,在实践中体验性能问题才会有真正的理解。前边的第一二篇的内容比较容易看懂,都是一些可以简单做下实践的知识。
这本书我应该算是看了一半,学习了一些以前没学习的知识点,特别是内存那块的知识。不过,在讲解C++对象模型时,有些地方没有讲清楚:讲创建对象时,没有解释NRV优化,我用书上提供的代码做测试,发现跟它所说的结果不一样,找了很久才发现是返回值优化导致的。另外,对C++对象内存布局讲解不多,可能是不想过多的涉及编译器吧。
9月初买了这本书的第二版,发觉大部分内容跟第一版一样的,连错误也是。第二版的书增加了一章,像是软件工程的知识,另外最后一篇工具使用,有增加一些内容。
前言里说“愿意与读者共同探讨”,但是并没有留下联系方式。第二版书上没有重大错误,发现的都是一些小问题:
P23:“要首先销毁其父类的对象”,应该是“最后销毁”。
P38~39:在讲解C++对象的创建时,我觉得应该说明默认构造函数是否是trivial的,关系到实际上是否会去调用构造函数的问题。
P44:“内建类型成员变量被赋给一个初值”,这个是错的。
P82:测试(VC和GCC)发现,虚函数做不了内联。不可以在类定义体外使用inline关键字。书上例子通过指针去调用虚函数是做不了内联的。
P108:例子程序的倒数第二行应该是 =newblock。
P119:“即0010101000(0x2A8)”,应该是001010101000。
P175:new操作的对象错误,不是FixedMemBlock,而是:MemoryBlock。
另外,还有少数几个地方有错别字、病句。