You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:05:57