为何C++中MemoryPool分配小尺寸数据时速度更慢?
内存池分配小尺寸数据时速度更慢的原因分析
我编写了如下MemoryPool测试代码,发现仅当fieldSize设置为10000及以上时,MemoryPool的内存分配速度更快。为何分配小尺寸数据时速度反而更慢?
#include <iostream> #include <stack> #include <mutex> #include <memory> const int fieldSize = 10000; // 当该值大于10000时,MemoryPool分配速度更快 const int numThreads = 1; const int numObjectInterval = 1000000; struct MemoryPoolTest { int temp{}; char data[fieldSize]{}; MemoryPoolTest(int i = 0) { temp = i; } }; template<typename T> class SimpleMemoryPool { public: SimpleMemoryPool() = default; SimpleMemoryPool(const SimpleMemoryPool &) = delete; SimpleMemoryPool &operator=(const SimpleMemoryPool &) = delete; template<typename... Args> T *allocate(Args &&... args) { std::lock_guard<std::recursive_mutex> lock(mutex_); if (!pool_.empty()) { T *obj = pool_.top(); pool_.pop(); return obj; } return new T(std::forward<Args>(args)...); } void deallocate(T *obj) { std::lock_guard<std::recursive_mutex> lock(mutex_); pool_.push(obj); } private: std::stack<T *> pool_; std::recursive_mutex mutex_; }; void TestAllocateWithoutPool(int numObjects) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < numObjects; ++i) { MemoryPoolTest *obj = new MemoryPoolTest(i); obj->temp = i; delete obj; } auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> duration = end - start; std::cout << "numObjects Count = " << numObjects << ", 无内存池耗时: " << duration.count() << " 秒\n"; } SimpleMemoryPool<MemoryPoolTest> pool; void TestAllocateWithMemoryPool(int numObjects) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < numObjects; ++i) { MemoryPoolTest *obj = pool.allocate(i); obj->temp = i; pool.deallocate(obj); } auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> duration = end - start; std::cout << "numObjects Count = " << numObjects << ", 内存池耗时: " << duration.count() << " 秒\n"; } void TestAllocateWithoutPoolByThread() { std::vector<std::thread> threads; std::cout << "\n测试无内存池:\n"; for (int i = 0; i < numThreads; i++) { threads.emplace_back(TestAllocateWithoutPool, numObjectInterval * (i + 1)); } for (int i = 0; i < numThreads; i++) { threads[i].join(); } } void TestAllocateWithPoolByThread() { std::vector<std::thread> threads; std::cout << "\n测试内存池:\n"; for (int i = 0; i < numThreads; i++) { threads.emplace_back(TestAllocateWithMemoryPool, numObjectInterval * (i + 1)); } for (int i = 0; i < numThreads; i++) { threads[i].join(); } threads.clear(); } int main() { TestAllocateWithoutPoolByThread(); // 首次测试MemoryPool(内存池为空) TestAllocateWithPoolByThread(); // 再次测试MemoryPool(内存池已填满,应比无池更快) TestAllocateWithPoolByThread(); TestAllocateWithoutPoolByThread(); return 0; }
测试结果
当fieldSize设为10000时
测试无内存池: numObjects Count = 1000000, 无内存池耗时: 0.0655205 秒 测试内存池: numObjects Count = 1000000, 内存池耗时: 0.0595625 秒 测试内存池: numObjects Count = 1000000, 内存池耗时: 0.0636256 秒 测试无内存池: numObjects Count = 1000000, 无内存池耗时: 0.0674414 秒
当fieldSize设为1000时
测试无内存池: numObjects Count = 1000000, 无内存池耗时: 0.0225407 秒 测试内存池: numObjects Count = 1000000, 内存池耗时: 0.0604793 秒 测试内存池: numObjects Count = 1000000, 内存池耗时: 0.0600341 秒 测试无内存池: numObjects Count = 1000000, 无内存池耗时: 0.0246731 秒
原因分析
- 锁开销占比过高:你的内存池使用
std::recursive_mutex,每次allocate和deallocate都要执行加锁解锁操作。当对象尺寸较小时,标准库new/delete的原生开销极低(标准库针对小对象有内置线程局部池优化),此时锁的开销会成为整体耗时的主要部分,直接拉高内存池的总耗时;而大对象的new/delete需要向操作系统申请内存,本身开销较大,锁的开销占比被稀释,内存池复用对象的优势就凸显出来。 - 内存池未真正节省初始化开销:你的
allocate逻辑中,复用对象时并没有跳过构造函数——虽然从池中取出了指针,但构造函数已经在首次分配时执行过,而你在allocate后又手动给obj->temp = i,等于做了两次赋值操作;直接用new的话,构造函数一次就完成了初始化,这部分额外操作在小对象场景下会进一步放大耗时差距。 - 标准库的内置优化碾压自定义实现:C++标准库的
new/delete已经针对小对象做了成熟的优化(比如线程局部缓存池),你的自定义内存池不仅没有超越这个优化,反而额外增加了锁和栈操作的开销,自然在小对象场景下表现更差。
优化建议
- 移除不必要的锁:你的测试场景是单线程(
numThreads=1),完全不需要std::recursive_mutex,去掉锁后能大幅降低开销。 - 调整对象复用逻辑:采用
placement new,首次分配内存时构造对象,复用对象时直接返回内存块,避免重复执行构造和额外赋值操作。 - 改用线程局部内存池:针对多线程场景,为每个线程分配独立的内存池,避免跨线程锁竞争,对齐标准库的优化思路。
内容的提问来源于stack exchange,提问作者DoyoHntr
相关产品推荐
相关产品推荐

