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

C++ 記憶體池實作 (MemoryPool) 含效能測試

     編輯:關於C++

設計目標

  • 固定大小區塊分配器,適用於頻繁 `new/delete` 同尺寸物件 (如節點、訊息封包)。
  • 減少系統呼叫、避免碎片、提升分配速度。
  • 單例/多實例均可,執行緒不安全 (多執行緒需加鎖或用 Thread-Local Pool)。

核心結構

// 記憶體區塊 (鏈表節點)
struct MemoryBlock {
    unsigned short m_nSize;      // 總位元組數
    unsigned short m_nFree;      // 可用單元數
    unsigned short m_nFirst;     // 首個可用單元索引
    MemoryBlock* m_pNext;        // 下一區塊
    char m_data[1];              // 柔性陣列 (實際大小 = m_nSize)

    // placement new 分配整個區塊 (含柔性陣列空間)
    void* operator new(size_t, unsigned short sum, unsigned short unit_size) {
        return ::operator new(sizeof(MemoryBlock) + sum * unit_size);
    }
    void operator delete(void* p, size_t) { ::operator delete(p); }

    MemoryBlock(unsigned short sum, unsigned short unit_size)
        : m_nSize(sum * unit_size), m_nFree(sum - 1), m_nFirst(1), m_pNext(nullptr) {
        char* p = m_data;
        for (int i = 1; i < sum; ++i) {
            *reinterpret_cast(p) = i; // 鏈接下一個空閒單元
            p += unit_size;
        }
    }
};

記憶體池管理類

class MemoryPool {
    unsigned short m_nUnitSize;   // 對齊後單元大小
    unsigned short m_nInitSize;   // 首區塊單元數
    unsigned short m_nGrowSize;   // 後續區塊單元數
    MemoryBlock* m_pFirst;        // 區塊鏈表頭

public:
    MemoryPool(unsigned short unit_size, unsigned short init_size = 2048, unsigned short grow_size = 1024)
        : m_pFirst(nullptr) {
        // 對齊至 2 位元組 (可改 4/8/16)
        if (unit_size > 4) m_nUnitSize = (unit_size + 1) & ~1;
        else if (unit_size >= 2) m_nUnitSize = 4;
        else m_nUnitSize = 2;
        m_nInitSize = init_size;
        m_nGrowSize = grow_size;
    }

    void* Alloc() {
        if (!m_pFirst) { // 首次分配
            m_pFirst = new (m_nInitSize, m_nUnitSize) MemoryBlock(m_nInitSize, m_nUnitSize);
            return m_pFirst->m_data;
        }
        // 尋找有空閒單元的區塊
        MemoryBlock* blk = m_pFirst;
        while (blk && blk->m_nFree == 0) blk = blk->m_pNext;
        if (blk) {
            char* p = blk->m_data + blk->m_nFirst * m_nUnitSize;
            blk->m_nFirst = *reinterpret_cast(p);
            --blk->m_nFree;
            return p;
        }
        // 無可用區塊 → 增長
        if (m_nGrowSize == 0) return nullptr;
        blk = new (m_nGrowSize, m_nUnitSize) MemoryBlock(m_nGrowSize, m_nUnitSize);
        blk->m_pNext = m_pFirst;
        m_pFirst = blk;
        return blk->m_data;
    }

    void Free(void* p) {
        if (!m_pFirst) return;
        MemoryBlock *blk = m_pFirst, *prev = m_pFirst;
        while (blk && (reinterpret_cast(p) < reinterpret_cast(blk->m_data) ||
                       reinterpret_cast(p) >= reinterpret_cast(blk->m_data) + blk->m_nSize)) {
            prev = blk;
            blk = blk->m_pNext;
        }
        if (!blk) return; // 指標不屬於本池
        ++blk->m_nFree;
        *reinterpret_cast(p) = blk->m_nFirst;
        blk->m_nFirst = static_cast((reinterpret_cast(p) - blk->m_data) / m_nUnitSize);
        // 整個區塊空閒 → 回收區塊
        if (blk->m_nFree * m_nUnitSize == blk->m_nSize) {
            prev->m_pNext = blk->m_pNext;
            if (reinterpret_cast(prev) == reinterpret_cast(m_pFirst))
                m_pFirst = nullptr;
            delete blk;
        }
    }

    ~MemoryPool() { while (m_pFirst) { auto nxt = m_pFirst->m_pNext; delete m_pFirst; m_pFirst = nxt; } }
};

效能測試結果 (10 萬次分配)

Without MemoryPool:  ~45 ms  (new/delete)
Using MemoryPool:    ~3 ms   (MemoryPool::Alloc/Free)
加速約 15 倍

已知問題與改進方向

  • 執行緒不安全:需加 `std::mutex` 或改用 Thread-Local Pool + 全域回收。
  • 對齊處理簡陋:僅 2 位元組對齊,SSE/AVX/原子操作需 16/32/64 位元組對齊,應用 `alignas` 或 `std::aligned_storage`。
  • 區塊回收策略:僅當整區塊空閒才回收,長期運行可能殘留大量半滿區塊,可加入定期整理或閒置釋放機制。
  • 單元大小固定:不支援變長分配,通用分配器仍需 `malloc`/`new` 或 `jemalloc`/`tcmalloc`。
  • C++17 起:可用 `std::pmr::memory_resource` + `std::pmr::monotonic_buffer_resource` / `pool_resource` 標準化替代。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved