C++STL内存池

本文探讨了C++ STL中的内存池实现,详细介绍了SGI-STL的内存池策略,比较了使用内存池与系统调用new/delete在性能上的差异。内存池在小对象分配时提供高效分配和释放,减少了系统调用并降低了碎片影响。文章还分析了SGI STL的两级配置器,包括__malloc_alloc_template和__default_alloc_template的工作原理。
摘要由CSDN通过智能技术生成

一、New delete和std::alloc

内存池就是预先分配好,放到进程空间的内存块,用户申请与释放内存其实都是在进程内进行,SGI-STL的alloc遇到小对象时就是基于内存池的。只有当内存池空间不够时,才会再从系统找一块很大的内存。

提到new和delete首先会转调用到malloc和free,这个大家应该很熟识了。很多人认为malloc是一个很简单的操作,其实巨复杂,它会执行一个系统调用(当然不是每一次,windows上是按页算),该系统调用会锁住内存硬件,然后通过链表的方式查找空闲内存,如果找到大小合适的,就把用户的进程地址映射到内存硬件地址中,然后释放锁,返回给进程。new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起。因为在这种情况下,我们几乎肯定知道对象应有什么值。当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象的创建操作(同时付出一定开销)。一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费。

 

二、New,delete调用与Allocate调用的效率对比

#include <QCoreApplication>
#include <QDebug>
#include <time.h>
#define RUNTIMES  (10000)

struct CCC
{
    CCC() {}
    char data[10];
};

char test_new_delete()
{
    char re = 0;
    for (unsigned int i = 0; i < RUNTIMES; ++i)
    {
        for (unsigned int j = 0; j < RUNTIMES; ++j)
        {
            CCC* pc = new CCC();
            CCC* pd = new CCC();
            re += pc->data[2];
            re += pd->data[2];
            delete pc;
            pc = NULL;
            delete pd;
            pd = NULL;
        }
    }

    return re;
}

char test_allocator()
{
    char re = 0;
    std::allocator<CCC> alloc;
    CCC* pStart = alloc.allocate(2);

    for (unsigned int i = 0; i < RUNTIMES; ++i)
    {
        for (unsigned int j = 0; j < RUNTIMES; ++j)
        {
            CCC* pEnd = pStart;

            alloc.construct(pEnd++, CCC());
            re += pEnd->data[2];
            alloc.construct(pEnd++, CCC());
            re += pEnd->data[2];

            while(pEnd != pStart)
            {
                alloc.destroy(--pEnd);
            }
        }
    }

    alloc.deallocate(pStart, 2);

    return re;
}
#include<iostream>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    double re1 = 0;
    double re2 = 0;

    // 运行内存池测试时,基本上对我机器其它进程没什么影响
    time_t begin = time(0);
    re1 = test_allocator(); // 使用内存池
    time_t seporator = time(0);

    // 运行到系统调用测试时,感觉机器明显变慢,
    // 如果再加上内存碎片的考虑,对其它进程的影响会更大。
    std::cout << long(seporator - begin) << std::endl;
    re2 = test_new_delete();           // 直接系统调用
    std::cout << long(time(0) - seporator) << std::endl;
    std::cout << re1 << re2 << std::endl;

    std::cout << "Test End!" << std::endl;

    return a.exec();
}

在一个100000000次的循环中,使用内存池是3秒,使用系统调用是100秒。而且以上测试还没有考虑到碎片的影响,以及运行该程序时对其它程序的影响。而且还有一点,就是机器的内存硬件容量越大,内存分配时,需要搜索的时间就可能越长,如果内存是多条共同工作的,影响就再进一步。当然,有些情况是不可说用什么就用什么的,但如果可选的话,优先使用栈上的对象,其次考虑内存池,然后再考虑系统调用。

在stl-sgi中函数alloc::allocate() 和 alloc::deallocate()源代码在 <stl_alloc.h>中,SGI STL 设计了双层级配置器。第一层配置器直接使用malloc() 和 free().第二层配置器则视情况采用不同的策略:但配置区块超过 128 bytes时,调用第一级配置器。当配置区块小于 128 bytes时,采用复杂的 memory pool 方式。下面我们分别简单的介绍一下第一级和第二级配置器。

 

三、第一级__malloc_alloc_template

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值