memory pool 的高效实现
memory pool
malloc可以分配任意大小的内存,因此,在malloc内部,保存了一些簿记信息(至少有一个包含内存块尺寸的信息)。调用free时,可以正确释放。
为了减少这些簿记开销,可以使用memory pool。
根据使用情境,可以分为两种:
1. 只分配固定大小的内存块,速度最快(normal path约10条机器指令)。
2. 可分配不同大小的内存块,速度稍慢,但比malloc快得多,也无簿记开销。
以下将分别说明
mpool
mpool可分配不同尺寸的内存。大多数时刻,都在内部分配。
销毁mpool时,会自动释放在mpool中未释放的内存。
mpool内部有一个包含多个不同尺寸fixed_mpool的array,根据请求分配的内存大小,直接索引到相应的fixed_mpool来分配一个单元(cell)。
通过定义宏FEBIRD_MPOOL_ALLOW_BIG_BLOCK,就允许大于max_cell_size的内存分配。在这种情况下,使用标准的malloc分配内存,分配出去的内存有额外簿记(用双向链表串起来),以便在销毁mpool时自动释放。
fixed_mpool
尺寸固定的内存池,一旦创建,该内存池只能分配固定尺寸的内存单元(cell)。这在很多情况下都适用,例如链表结点、树节点、图结点、自定义的结构、等等。
用于stl的map/set/list再适合不过了——但是不能用于vector/deque等需要分配可变尺寸的容器。
fixed_mpool内部的多个chunk使用数组,类似std::vector,iNextChunk相当于vector.size,nChunks相当于vector.capacity,每次空间不够时扩张一倍。使用数组,而不是链表,有以下好处:
1. 有助于对齐——如果chunk_allocator是对齐(对齐>=32时)分配的,而chunk使用链表组织,就会在chunk开始处预留一个chunk头部,这会导致不对齐。
2. 如果cell_size也刚好较大且整除chunk_size,使用链表就会浪费将近一个cell(cell_size – chunk_header_size)。
3. 如果不把连接信息保存在chunk header,就需要另外分配chunk结点,而分配chunk结点又需要其它内存分配函数。
空闲表使用单链表,因此,理论上每个cell最小必须能容纳一个指针,32位系统式4字节,64位系统式8字节,实现中使用8字节作为最小值。