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) 操作;
- 本实现为教学演示,未处理线程安全与内存对齐扩展需求,实际工程中需按需补充。