对于C++开发而言,内存分配优化几乎每个项目优化的必须课题.其实现方式也是五花八门.本文重点总结下这方面的经验.
1.通用分配/释放的优化
对于windows应用程序的内存分配
,从上层往下,以此是malloc/new-->HeapAlloc-->VirtualAlloc.通用的内存分配优化,一般会选择基于VirtualAlloc重新实现malloc.以期替代c标准函数malloc.
现在开源的这样的实现,效率比较好的有tcmalloc和nedmalloc.前者是google的众多基本库之一.后者是历史悠久的一个malloc开源实现.他们的实现原理基本一致,TLS+struct MemPool{ struct MemPool* next;}这样的结构实现.其中实现的大部分代码在于如何构建内存地址,以便在free时通过地址获得它的大小.就性能而言,它们也基本一致.
不过tcmalloc有个好处,如果链接了它的dll,会自动hook所有分配函数,并替换之.能够非 常方面的集成到程序中.性能来说,它比默认的分配快很多.在我们开发的游戏,集成后在某些机器上能带来30%以上的fps提升.
2.Free带大小的分配和释放
虽然tcmalloc有很好的表现,但是在某些情况下,比如stl容器,和每次只分配一个的对象,他们在free时,会带上释放内存的大小.
这样我们可以简化tcmalloc实现,直接每个分配大小映射一个Mem链表,多线程依然采用TLS解决.典型的逻辑大概如下:
Void * fast_malloc(size_t sz)
{
// g_mainList采用TLS存储.对于过大尺寸的内存分配采用默认分配.
MemPool*& pHead = g_mainList[sz];
If(!pHead )
{
// 初始化链表
}
MemPool* pRet = pHead;
pHead = pHead->next;
Return pRet;
}
Void fast_free(void* p, size_t sz)
{
MemPool* pNew = (MemPool*)p;
MemPool*& pHead = g_mainList[sz];
pNew->next = pHead;
pHead = pNew;
}
这样实现后,在内存分配/释放分别在不同线程的情况下,会造成内存泄漏.可以针对每个大小的链表添加一个计数,超过一定个数就统一释放.一般来说这个会比tcmalloc还快20%.
3.Std::map/list等特殊容器的适配器优化
map/list这样的容器,总是一次分配一个结点.对于他们的适配器,可以更特殊的处理.
这样大概有两个成员变量.
template<class Ty>
class FastOneAllocator
{
MemPool* m_poolHead;
// 记录分配的内存,方便最终释放
MemPool* m_ListHead;
// 内存链.分配/释放原理跟上文类似
};
释放/分配的逻辑如下:
pointer allocate(size_type _Count)
{
if(m_ListHead == NULL)
{
//有个默认条件:sizeof(Ty)必须大于等于sizeof(MemPool)
void* pNew = malloc(COUNT_PER*sizeof(Ty)+sizeof(MemPool))
//todo: 把内存通过链表连起来.
MemPool* pNewHead = (MemPool*)pNew;
pNewHead->next = m_poolHead;
m_poolHead = pNewHead;
}
// 同fast_malloc逻辑.
}
//析构函数内.
~FastOneAllocator()
{
MemPool* lastPtr = NULL;
for(; m_poolHead; m_poolHead = lastPtr)
{
lastPtr = m_poolHead->_next;
free(m_poolHead);
}
}
如果list以FastOneAllocator作为适配器.list::swap就会问题.
典型的调用如下:
list<int> t2;
{
list<int> t1;
t1.push_back(1);
t2.swap(t1);
//t1的适配器析构 把交换过去的t2内存都释放了
}
类似list这样的数据结构,都是通过一个head来把所有容器内数据串联起来的.list::swap主要是通过直接交换head来达到swap整个数据结构的目的.
为解决这个问题,可以统一把list::swap用std::swap实现.这里效率相对来说有所降低.但是考虑到这样的调用实际中并不常见,也是可以接受的。
实际测试的结果, FastOneAllocator相对于以fast_malloc为基础构建的适配器,快15%左右.