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

使用unsynchronized_pool_resource分配pmr::vector的内存复用问题

问题:pmr::vector与unsynchronized_pool_resource组合时内存无法复用的原因

我之前尝试结合monotonic_buffer_resource和unsynchronized_pool_resource实现pmr::vector的内存高效分配,避免堆内存调用。但实际运行时,内存并未按预期返回池复用,每次迭代都向monotonic buffer申请内存,很快耗尽;而手动调用allocate/deallocate的示例却能正常复用内存,想知道两者差异和我遗漏的点。

代码示例1(pmr::vector场景)

#include <iostream>
#include <memory_resource>
#include <vector>
class A{
    char array[64];
};
typename std::aligned_storage<32000>::type storage;
thread_local std::pmr::monotonic_buffer_resource bufferPool{&storage,sizeof(storage),std::pmr::null_memory_resource()};
std::pmr::unsynchronized_pool_resource pool{{},&bufferPool};

int main()
{

    for(int i =0; i < 10000;++i){
        std::pmr::vector<A> vec (&pool);
        vec.reserve(1000);
        for(int j =0; j < 1000;++j){
            vec.emplace_back();
        }
    }
}

代码示例2(手动allocate/deallocate场景)

#include <iostream>
#include <memory_resource>

int main() {
    char buffer[1024];
    std::pmr::monotonic_buffer_resource monotonicResource(buffer, sizeof(buffer));

    std::pmr::unsynchronized_pool_resource poolResource(&monotonicResource);

    for (int i = 0; i < 5; ++i) {
        std::cout << "Iteration " << i << ":\n";

        void* ptr = poolResource.allocate(200);
        std::cout << "Allocated at: " << ptr << "\n";

        poolResource.deallocate(ptr, 200);

        std::cout << "Deallocated.\n\n";
    }

    return 0;
}

差异分析与问题根源

1. 内存申请大小触发的pool行为差异

unsynchronized_pool_resource有一个关键特性:当申请的内存大小超过其默认的「最大池化块阈值」(实现定义,通常为256/512字节)时,会直接向上游内存资源(这里是monotonic_buffer_resource)申请内存,而不会将这块内存纳入自身的池化管理。

  • 代码示例2中,每次申请200字节,小于默认阈值,因此pool会从monotonic buffer申请一块大内存块,切割为多个200字节的小块管理。释放时,小块会被放回pool的空闲列表,下一次申请直接复用,无需再向monotonic buffer申请。
  • 代码示例1中,vec.reserve(1000)需要申请1000 * sizeof(A) = 64000字节,远大于默认阈值。因此每次迭代时,pool都会直接向monotonic buffer申请64000字节的大块内存,完全不经过自身的池化复用逻辑。

2. monotonic_buffer_resource的不可回收特性

monotonic_buffer_resource的设计是单向分配:它只能向前分配内存,不支持释放单个内存块(deallocate操作是无效的),只有调用reset()才能重置整个缓冲区的分配位置。

在代码示例1中,每次vector销毁时释放的64000字节大块内存,monotonic buffer无法回收复用——因为它无法将已分配的块标记为空闲。因此每次迭代都会在monotonic buffer的当前位置继续分配新的大块内存,很快耗尽32000字节的缓冲区(甚至第一次申请64000字节就会超过缓冲区大小,触发bad_alloc,除非你的实际缓冲区更大)。

3. 你遗漏的关键配置

要让大内存申请也能被pool池化管理,需要在构造unsynchronized_pool_resource时,通过pool_options手动设置largest_required_pool_block阈值,将你的大申请大小纳入池化范围:

// 调整pool配置,让64000字节的申请也能被池化
std::pmr::pool_options opts;
opts.largest_required_pool_block = 64000; // 设置为你需要的最大申请大小
std::pmr::unsynchronized_pool_resource pool{opts, &bufferPool};

这样,pool会为64000字节的申请创建对应的池,释放时将内存块放回池,下一次迭代就能直接复用,无需再向monotonic buffer申请。

另外注意:monotonic_buffer_resource作为pool的上游,当pool需要向其申请chunk时,monotonic会分配一块足够大的内存给pool,pool再切割成对应大小的块管理。即使如此,monotonic本身还是无法回收已分配给pool的chunk,除非调用pool.release()或monotonic.reset(),但只要pool内部的空闲块足够,就不会再向monotonic申请新内存。


内容的提问来源于stack exchange,提问作者getsoubl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:25:06