首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 高性能內存池實現:從原理到基準測試超越 std::allocator 與 vector

     編輯:關於C++

頻繁的 new/delete 或 std::allocator 調用系統分配器會產生內存碎片、鎖競爭與調用開銷。本文實現一個定長塊 + 自由鏈表的高性能內存池 MemoryPool<T, BlockSize>,符合 STL 分配器概念,並通過鏈式棧壓測證明其分配速度遠超 std::allocator 與 std::vector。

一、內存池核心設計

  • 塊:向系統申請固定大小(默認 4KB)大塊內存,作為基本管理單元。
  • 槽:塊內劃分為大小為 sizeof(T)(對齊後)的槽,每槽存一個對象或作為自由鏈表節點。
  • 自由鏈表:freeSlots_ 串聯所有空閒槽,分配/釋放為 O(1) 指針操作。
  • 指針管理:currentBlock_ 當前塊,currentSlot_ 當前塊下一可用槽,lastSlot_ 當前塊末尾。

二、關鍵數據結構

template<typename T, size_t BlockSize = 4096>
class MemoryPool {
    // 聯合體:槽要么存對象,要么存下一空閒槽指針
    union Slot_ {
        T element;
        Slot_* next;
    };
    using Slot = Slot_;
    using SlotPtr = Slot*;
    using DataPtr = char*;

    SlotPtr currentBlock_ = nullptr; // 塊鏈表頭
    SlotPtr currentSlot_ = nullptr;  // 當前塊下一可用槽
    SlotPtr lastSlot_ = nullptr;     // 當前塊末尾+1
    SlotPtr freeSlots_ = nullptr;    // 自由鏈表頭

    static_assert(BlockSize >= 2 * sizeof(Slot), "BlockSize too small");
    // ...
};

三、核心接口實現(符合 Allocator 概念)

1. 分配 allocate

T* allocate(size_t n = 1, const T* = 0) {
    if (n != 1) throw std::bad_alloc(); // 僅支持單對象分配

    if (freeSlots_) { // 優先復用自由鏈表槽
        SlotPtr slot = freeSlots_;
        freeSlots_ = slot->next;
        return reinterpret_cast<T*>(slot);
    }

    if (currentSlot_ >= lastSlot_) { // 當前塊耗盡,申請新塊
        DataPtr newBlock = reinterpret_cast<DataPtr>(::operator new(BlockSize));
        reinterpret_cast<SlotPtr>(newBlock)->next = currentBlock_; // 塊鏈表串聯
        currentBlock_ = reinterpret_cast<SlotPtr>(newBlock);

        // 對齊調整:跳過塊頭 SlotPtr,按 alignof(Slot) 對齊
        DataPtr body = newBlock + sizeof(SlotPtr);
        uintptr_t addr = reinterpret_cast<uintptr_t>(body);
        size_t padding = (alignof(Slot) - addr % alignof(Slot)) % alignof(Slot);
        currentSlot_ = reinterpret_cast<SlotPtr>(body + padding);
        lastSlot_ = reinterpret_cast<SlotPtr>(newBlock + BlockSize - sizeof(Slot) + 1);
    }
    return reinterpret_cast<T*>(currentSlot_++);
}

2. 釋放 deallocate

void deallocate(T* p, size_t = 1) {
    if (p) {
        SlotPtr slot = reinterpret_cast<SlotPtr>(p);
        slot->next = freeSlots_; // 頭插自由鏈表
        freeSlots_ = slot;
    }
}

3. 構造/析構轉發

template<typename U, typename... Args>
void construct(U* p, Args&&... args) {
    ::new (p) U(std::forward<Args>(args)...); // 定位 new
}
template<typename U>
void destroy(U* p) { p->~U(); }

4. 銷毀池 ~MemoryPool

~MemoryPool() {
    SlotPtr cur = currentBlock_;
    while (cur) {
        SlotPtr next = cur->next;
        ::operator delete(cur); // 歸還系統
        cur = next;
    }
}

5. rebind 支持

template<typename U> struct rebind { using other = MemoryPool<U, BlockSize>; };

四、測試框架:鏈式棧 StackAlloc<T, Alloc>

template<typename T, typename Alloc = std::allocator<T>>
class StackAlloc {
    struct Node { T data; Node* prev; };
    using AllocTraits = std::allocator_traits<Alloc>;
    using NodeAlloc = typename AllocTraits::template rebind_alloc<Node>;
    NodeAlloc alloc_;
    Node* head_ = nullptr;
public:
    void push(const T& v) {
        Node* n = AllocTraits::allocate(alloc_, 1);
        AllocTraits::construct(alloc_, n, Node{v, head_});
        head_ = n;
    }
    T pop() {
        Node* n = head_;
        T v = n->data;
        head_ = n->prev;
        AllocTraits::destroy(alloc_, n);
        AllocTraits::deallocate(alloc_, n, 1);
        return v;
    }
    bool empty() const { return !head_; }
};

五、基準測試代碼與結果分析

#define ELEMS 10'000'000
#define REPS  100

int main() {
    clock_t start;
    // 1. std::allocator
    StackAlloc<int, std::allocator<int>> stkStd;
    start = clock();
    for (int r=0; r vec;
    start = clock();
    for (int r=0; r

典型輸出(示例)

std::allocator: 58.23s
std::vector:    12.45s
MemoryPool:      3.17s

結論:MemoryPool 比 std::allocator 快 ~18x,比 std::vector 快 ~4x。優勢來源:無系統調用開銷、無鎖、無內存碎片、緊湊內存佈局利於缓存。

六、適用場景與局限

適用不適用
高頻小對象分配/釋放(節點、消息、任務) 大對象、生命週期極長對象
實時系統、遊戲引擎、高頻交易 需要釋放內存給系統的場景(池析構前不歸還)
STL 容器自定義分配器 多線程共享池需自行加鎖(本實現非線程安全)

七、擴展方向

  • 線程安全版:freeSlots_ 改用無鎖棧或每線程本地緩存(TLAB)。
  • 可變塊大小:按請求大小分級管理多個池。
  • 統計監控:記錄分配次數、峰值使用量、碎片率。

內存池是系統級性能優化的利器,理解其原理並能正確實現 STL 兼容分配器,是 C++ 高性能開發的核心技能之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved