1. 内存池分配器

任务目标

实现一个固定大小对象的内存池分配器,用于替代频繁的 new/delete 动态内存分配。通过预分配连续内存块并复用已释放对象槽位,减少堆分配的系统调用开销与内存碎片。最终通过一个基准测试对比内存池与标准 new/delete 在批量创建/销毁对象时的耗时差异。

核心实现

#include <cstdint>
#include <cstddef>
#include <vector>
#include <chrono>
#include <iostream>

// 固定大小对象内存池(线程非安全)
template <typename T>
class MemoryPool {
public:
    explicit MemoryPool(size_t capacity) : capacity_(capacity) {
        storage_ = static_cast<T*>(::operator new(capacity_ * sizeof(T)));
        free_list_.reserve(capacity_);
        // 初始化空闲槽位索引(从后往前压入,使分配时优先使用低地址)
        for (size_t i = capacity_; i > 0; --i) {
            free_list_.push_back(i - 1);
        }
    }

    ~MemoryPool() {
        ::operator delete(storage_);
    }

    // 禁止拷贝
    MemoryPool(const MemoryPool&) = delete;
    MemoryPool& operator=(const MemoryPool&) = delete;

    template <typename... Args>
    T* alloc(Args&&... args) {
        if (free_list_.empty()) return nullptr;
        size_t index = free_list_.back();
        free_list_.pop_back();
        // 在预分配内存上构造对象
        T* ptr = new (storage_ + index) T(std::forward<Args>(args)...);
        return ptr;
    }

    void dealloc(T* ptr) {
        if (ptr == nullptr) return;
        ptr->~T();  // 显式调用析构
        size_t index = static_cast<size_t>(ptr - storage_);
        free_list_.push_back(index);
    }

private:
    T* storage_;               // 预分配内存块
    size_t capacity_;          // 最大对象数量
    std::vector<size_t> free_list_;  // 空闲槽位索引栈
};

// 测试对象:模拟有一定内存开销的实体
struct TestObject {
    int id;
    double data[8];
    TestObject(int i = 0) : id(i) {
        for (auto& d : data) d = static_cast<double>(i);
    }
};

// 基准测试:对比内存池与 new/delete
void benchmark() {
    constexpr size_t OBJ_COUNT = 100000;
    constexpr size_t POOL_SIZE = OBJ_COUNT;

    // ---- 使用内存池 ----
    MemoryPool<TestObject> pool(POOL_SIZE);
    std::vector<TestObject*> pool_ptrs;
    pool_ptrs.reserve(OBJ_COUNT);

    auto pool_start = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < OBJ_COUNT; ++i) {
        pool_ptrs.push_back(pool.alloc(i));
    }
    for (auto* p : pool_ptrs) {
        pool.dealloc(p);
    }
    auto pool_end = std::chrono::high_resolution_clock::now();
    auto pool_ms = std::chrono::duration_cast<std::chrono::milliseconds>(pool_end - pool_start).count();

    // ---- 使用 new/delete ----
    std::vector<TestObject*> heap_ptrs;
    heap_ptrs.reserve(OBJ_COUNT);

    auto heap_start = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < OBJ_COUNT; ++i) {
        heap_ptrs.push_back(new TestObject(static_cast<int>(i)));
    }
    for (auto* p : heap_ptrs) {
        delete p;
    }
    auto heap_end = std::chrono::high_resolution_clock::now();
    auto heap_ms = std::chrono::duration_cast<std::chrono::milliseconds>(heap_end - heap_start).count();

    std::cout << "MemoryPool time: " << pool_ms << " ms\n";
    std::cout << "new/delete time: " << heap_ms << " ms\n";
    std::cout << "Speedup: " << (heap_ms > 0 ? static_cast<double>(heap_ms) / pool_ms : 0.0) << "x\n";
}

int main() {
    benchmark();
    return 0;
}

编译与运行

g++ -O2 -std=c++17 memory_pool.cpp -o memory_pool
./memory_pool

验证方法

程序执行后输出两种分配方式的总耗时(毫秒)及加速比。判定通过条件:

  • 程序能正常编译并无运行时错误;
  • 输出中 MemoryPool time 明显小于 new/delete time(通常快 2~10 倍,具体取决于系统与优化级别);
  • 加速比大于 1,证明内存池确实减少了动态分配开销。

关键说明

  • 内存池在构造时一次性分配 capacity_ * sizeof(T) 字节,所有对象生命周期内不产生堆分配调用;
  • alloc 使用 placement new 在预分配内存上构造对象,dealloc 显式调用析构后将槽位索引压回空闲栈;
  • 空闲槽位用栈管理,分配与释放均为 O(1) 操作;
  • 本实现为教学演示,未处理线程安全与内存对齐扩展需求,实际工程中需按需补充。