SGI STL的内存池

转载:http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html
stl中各种容器都有一个可选的模板参数:allocator,也就是一个负责内存分配的组件。STL标准规定的allcator
被定义在memory文件中。STL标准规定的allocator只是单纯地封装operator new,效率上有点过意不去。

SGI实现的STL里,所有的容器都使用SGI自己定义的allocator。这个allocator实现了一个small object的内存池。
Loki里为了处理小对象的内存分配,也实现了类似的内存管理机制。

该内存池大致上,就是一大块一大块地从系统获取内存,然后将其分成很多小块以链表的形式链接起来。其内部
有很多不同类型的链表,不同的链表维护不同大小的内存块。每一次客户端要求分配内存时,allcator就根据请求
的大小找到相应的链表(最接近的尺寸),然后从链表里取出内存。当客户端归还内存时,allocator就将这块内存
放回到对应的链表里。

我简单地画了幅图表示整个结构:
SGI STL的内存池
allocator内部维护一个链表数组,数组元素全部是链表头指针。链表A每一个节点维护一个8bytes的内存块,链表
B每一个节点维护一个16bytes的内存块。

当客户端请求分配10bytes的内存时,allocator将10调整为最接近的16bytes(只能大于10bytes),然后发现16bytes
这个链表(链表B)里有可用内存块,于是从B里取出一块内存返回。当客户端归还时,allocator找到对应的链表,将
内存重新放回链表B即可。

大致过程就这么简单,也许有人要说用链表维护一块内存,链表本身就会浪费一些内存(在我很早前接触内存池时,
总会看到类似的论点= =|),其实通过一些简单的技巧是完全可以避免的。例如,这里allocator维护了很多内存块,
反正这些内存本身就是闲置的,因此我们就可以直接在这些内存里记录链表的信息(下一个元素)。
还是写点代码详细说下这个小技巧:

struct Obj
    {
        Obj *next;
    }; 

    void *mem = malloc( 100 );
    Obj *header = (Obj*) mem;
    Obj *cur_obj = header;
    Obj *next_obj = cur_obj;
    for( int i = 0; ; ++ i )
    {
        cur_obj = next_obj;
        next_obj = (Obj*)((char*)next_obj + 10 );
        if( i == 9 )
        {
            cur_obj->next = 0;
            break;
        }
        else
        {
            cur_obj->next = next_obj;
        }
    }
    free( mem );

这样,通过header指针和next域,就可以逐块(这里是10byts)地访问mem所指向的内存,而这些链表的节点,都
是直接保存在这块内存里的,所以完全没有额外消耗。

我用C模仿着SGI的这个allocator写了个可配置的内存池,在其上按照STL的标准包装了一个allocator,可以直接
用于VC自带的STL里。测试代码稍微测试了下,发现在不同的机器上有明显的差距。

上一篇:leetcode 算法题157 (简单037) 用 Read4 读取 N 个字符


下一篇:157 亿美元收购Tableau,Salesforce在下一盘怎样的棋?