對C++程序內(nèi)存管理的精雕細(xì)琢

字號:

應(yīng)用程序分配內(nèi)存的方法,對程序的執(zhí)行性能有著深刻的影響。目前,通用的內(nèi)存分配方法本質(zhì)上已非常高效,但仍有改進(jìn)的空間。
    內(nèi)存分配,不可一層不變
    今天,對絕大多數(shù)程序來說,通用的內(nèi)存分配方法--此處指代分配算符(Allocator:即malloc或new),已達(dá)到了理想的速度及滿足了低碎片率的要求,然而,在內(nèi)存分配領(lǐng)域,一丁點(diǎn)的信息都值得探討很久,某些特定程序關(guān)于分配模式的信息,將有助于實(shí)現(xiàn)專門的分配算符,可顯著地提高大多數(shù)高性能要求程序的性能底線。有時,當(dāng)通用內(nèi)存分配算符平均耗費(fèi)幾百個時鐘周期時,一個良好的自定義內(nèi)存分配算符可能只需要不到半打的周期。
    這就是為什么大多數(shù)高性能、高要求的應(yīng)用程序(如GCC、Apache、Microsoft SQL Server),都有著它們自己的內(nèi)存分配算符。也許,把這些專門的內(nèi)存分配算符歸納起來,放進(jìn)一個庫中,是個不錯的想法,但是,你的程序可能有不同的分配模式,其需要另外的內(nèi)存分配算符,那怎么辦呢?
    等等,還有呢,如果我們設(shè)計了一種特殊用途的內(nèi)存分配算符,就可以不斷發(fā)展下去,由此可從中篩選出一些,來組成一個通用目的的內(nèi)存分配算符,如果此通用分配算符優(yōu)于現(xiàn)有的通用分配算符,那么此項(xiàng)設(shè)計就是有效及實(shí)用的。
    下面的示例使用了Emery小組的庫--HeapLayers(http://heaplayers.org/),為了定義可配置的分配算符,其中使用了mixins(在C++社區(qū)中,也被稱為Coplien遞歸模式):通過參數(shù)化的基來定義類,每一層中只定義兩個成員函數(shù),malloc和free:
    template
    struct Allocator : public T {
    void * malloc(size_t sz);
    void free(void* p);
    //系統(tǒng)相關(guān)的值
    enum { Alignment = sizeof(double) };
    //可選接口e
    size_t getSize(const void* p);
    };
    在每一層的實(shí)現(xiàn)中,都有可能向它的基類請求內(nèi)存,一般來說,一個不依賴于外界的內(nèi)存分配算符,都會處在層次的頂層--直接向前請求系統(tǒng)的new和delete操作符、malloc和free函數(shù)。在HeapLayers的術(shù)語中,沒有頂層堆,以下是示例:
    struct MallocHeap {
    void * malloc(size_t sz) {
    return std::malloc(sz);
    }
    void free(void* p) {
    return std::free(p);
    }
    };
    為獲取內(nèi)存,頂層堆也能通過系統(tǒng)調(diào)用來實(shí)現(xiàn),如Unix的sbrk或mmap。getSize函數(shù)的情況就比較特殊,不是每個人都需要它,定義它只是一個可選項(xiàng)。但如果定義了它,你所需做的只是插入一個存儲內(nèi)存塊大小的層,并提供getSize函數(shù),見例1:
    例1:
    template
    class SizeHeap {
    union freeObject {
    size_t sz;
    double _dummy; //對齊所需
    };
    public:
    void * malloc(const size_t sz) {
    //添加必要的空間
    freeObject * ptr = (freeObject *)SuperHeap::malloc(sz + sizeof(freeObject));
    //存儲請求的大小
    ptr->sz = sz;
    return ptr + 1;
    }
    void free(void * ptr) {
    SuperHeap::free((freeObject *) ptr - 1);
    }
    static size_t getSize (const void * ptr) {
    return ((freeObject *)ptr - 1)->sz;
    }
    };
    SizeHeap是怎樣實(shí)現(xiàn)一個實(shí)用的層,并掛鉤于它基類的malloc與free函數(shù)的示例,它在完成一些額外的工作之后,把修改好的結(jié)果返回給使用者。SizeHeap為存儲內(nèi)存塊大小,分配了額外的內(nèi)存,再加上適當(dāng)?shù)男⌒恼{(diào)整(指union),盡可能地避免了內(nèi)存數(shù)據(jù)對齊問題。不難想像,我們可構(gòu)建一個debug堆,其通過特定模式在內(nèi)存塊之前或之后填充了一些字節(jié),通過檢查是否模式已被保留,來確認(rèn)內(nèi)存的溢出。事實(shí)上,這正是HeapLayers的DebugHeap層所做的,非常的簡潔。