oneTBB fixed_pool 详解:基于固定大小缓冲区的可扩展内存池
并发编程高性能计算【免费下载链接】oneTBBoneAPI Threading Building Blocks (oneTBB)项目地址https://gitcode.com/gh_mirrors/on/oneTBB点击查看免费下载fixed_pool是 oneAPI Threading Building BlocksoneTBB预览特性TBB_PREVIEW_MEMORY_POOL提供的核心组件之一它让开发者能够在一块预先分配好的固定大小缓冲区上执行线程安全、随处理器数量扩展的内存分配与释放操作。本文基于 doc/main/reference/source/uncategorized/scalable_memory_pools/fixed_pool_cls.rst 展开并结合 include/oneapi/tbb/memory_pool.h 的源码实现与 test/tbbmalloc/test_scalable_allocator.cpp 的测试用例进行纵深解读。读完本文你将掌握fixed_pool的完整成员接口、构造约束、底层分配原理以及如何在真实代码中安全地使用它。概览什么是 fixed_poolfixed_pool是 oneTBB 内存池体系中的定长池它不从系统或底层分配器按需申请内存而是将构造函数传入的一块固定大小的缓冲区作为全部可用内存来源在池内部完成对这块内存的切分、分配与回收。其设计目标是可扩展性Scalable分配与释放操作随处理器数量增长而保持高效不会退化为全局锁串行化线程安全多个线程可以同时从同一个池中分配内存无需外部加锁确定性内存来源所有可分配内存均由调用方预先指定便于实现定制化的内存管理策略例如共享内存、固定地址空间、低碎片场景。从源码结构看fixed_pool直接继承自pool_base见 include/oneapi/tbb/memory_pool.h而pool_base是 oneTBB 所有内存池共用的接口基类内部封装了对 scalable allocator 底层 RMLResource Management Layer池接口的调用。开启预览特性与包含头文件memory_pool.h属于 oneTBB 的预览特性preview feature。包含该头文件之前必须先定义宏TBB_PREVIEW_MEMORY_POOL且值为 1否则编译器会直接报错。这一点在源码中有强制校验#include detail/_config.h #if !__TBB_PREVIEW_MEMORY_POOL #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h #endif标准用法如下#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h另外同族的memory_pool模板类与memory_pool_allocator模板类也共用同一个头文件三者合称Scalable Memory Pools详见 doc/main/reference/source/memory_allocation/scalable_memory_pools.rst。类声明与语法fixed_pool的声明形式非常简单class fixed_pool;其完整成员接口位于命名空间oneapi::tbb下namespace oneapi { namespace tbb { class fixed_pool : no_copy { public: fixed_pool(void *buffer, size_t size) throw(std::bad_alloc); ~fixed_pool(); void recycle(); void *malloc(size_t size); void free(void* ptr); void *realloc(void* ptr, size_t size); }; } // namespace tbb } // namespace oneapi注意该类继承自no_copy即不可拷贝、不可赋值这是内存池类保持内部状态一致性的必要设计源码注释也明确指出Pool interface is separate from standard allocator classes because it has to maintain internal state, no copy or assignment。遵循的 Memory Pool 命名要求fixed_pool建模modelsoneTBB 参考手册中定义的Memory Pool 概念Memory Pool Concept。该概念以伪签名形式规定了所有内存池必须满足的语义见 doc/main/reference/source/memory_allocation/scalable_memory_pools.rst下表即该概念的完整定义其中P表示内存池类的一个实例伪签名语义~P() throw();析构函数。释放所有已分配的内存。void P::recycle();释放所有已分配的内存池本身可继续复用。void* P::malloc(size_t n);返回从内存池分配的n字节内存的指针。void P::free(void* ptr);释放ptr指针指定的内存对象。void* P::realloc(void* ptr, size_t n);将ptr指向的内存对象重新分配为n字节。该概念的另一建模者是 memory_pool 模板类memory_pool从底层分配器按需申请大块内存而fixed_pool的所有内存都来自构造函数传入的固定缓冲区这是两者最本质的区别。构造函数绑定固定缓冲区fixed_pool的构造函数是使用该类的关键入口fixed_pool(void *buffer, size_t size) throw(std::bad_alloc);buffer指向由调用方提供的内存缓冲区起始地址size该缓冲区的字节大小异常若运行时无法成功构造池实例抛出std::bad_alloc。参考手册对构造函数的说明是构造一个内存池用于管理 buffer 指向的、大小为 size 的内存。从源码实现看构造函数远比表面更严格include/oneapi/tbb/memory_pool.hinline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) { if (!buf || !size) throw_exception(std::invalid_argument(Zero in parameter is invalid)); rml::MemPoolPolicy args(allocate_request, nullptr, size, /*fixedPool*/true); rml::MemPoolError res rml::pool_create_v1(intptr_t(this), args, my_pool); if (res!rml::POOL_OK) throw_exception(std::runtime_error(Cant create pool)); }可以从中提炼出三条重要的实现事实缓冲区与大小为 0 均非法buffer nullptr或size 0会抛出std::invalid_argument。这一约束在测试中得到了验证——test/tbbmalloc/test_scalable_allocator.cpp 的TestSmallFixedSizePool用例专门断言零大小池仅抛出std::invalid_argument并验证了fixed_pool pool(nullptr, 10*1024*1024)必然抛出该异常。底层通过 RML 池接口创建构造函数向rml::pool_create_v1提交一个MemPoolPolicy其中granularity被设为整个缓冲区大小sizefixedPool标志置为true。MemPoolPolicy与MemPoolError的定义位于 include/oneapi/tbb/scalable_allocator.h后者包含POOL_OK、INVALID_POLICY、UNSUPPORTED_POLICY、NO_MEMORY、NO_EFFECT五类错误码。内存申请回调只触发一次allocate_request回调在首次需要内存时把整块缓冲区交给池并将内部my_size置 0 标记缓冲区已被使用include/oneapi/tbb/memory_pool.hinline void *fixed_pool::allocate_request(intptr_t pool_id, size_t bytes) { fixed_pool self *reinterpret_castfixed_pool*(pool_id); __TBBMALLOC_ASSERT(0 ! self.my_size, The buffer must not be used twice.); bytes self.my_size; self.my_size 0; // remember that buffer has been used return self.my_buffer; }这意味着同一块缓冲区在池的生命周期内只提交一次给底层分配器池内部负责此后所有小块内存的切分与复用。缓冲区大小的实际下限测试代码给出了一条非常实用的经验值TestSmallFixedSizePool以 0、3、9、27……递增的缓冲区大小反复构造池并断言16 字节分配能放进 16KB 的 slab因此池至少需要约 16KB 空间才能工作test/tbbmalloc/test_scalable_allocator.cpp。也就是说缓冲区过小会导致池无法创建或无法完成任何分配——不是构造失败抛出异常就是malloc返回nullptr。测试同时验证了最大缓冲区大小必然足以创建可用的 fixed_pool这一结论。成员函数详解void recycle();释放池中所有已分配的内存对象使池恢复到可继续分配的状态。源码实现为void recycle() { rml::pool_reset(my_pool); }recycle()对应rml::pool_reset语义是一次性释放池内全部对象见 include/oneapi/tbb/scalable_allocator.h。与析构函数相比recycle()不会销毁池本身因此适合分配一批对象 → 处理 → 整批回收的高频复用场景能显著减少重复建池的开销。void *malloc(size_t size);从池中分配size字节返回指向该内存块的指针分配失败时返回nullptr。实现直接转发给rml::pool_mallocvoid *malloc(size_t size) { return rml::pool_malloc(my_pool, size); }注意这与标准operator new的语义不同malloc失败不抛出异常而是返回空指针调用方需要显式检查返回值。void free(void* ptr);释放此前由malloc/realloc返回的内存对象void free(void* ptr) { rml::pool_free(my_pool, ptr); }void *realloc(void* ptr, size_t size);将ptr指向的内存对象重新分配为size字节是pool_malloc的互补接口源码注释指出它允许一些底层优化可能性void *realloc(void* ptr, size_t size) { return rml::pool_realloc(my_pool, ptr, size); }测试用例专门验证了realloc不会破坏既有内容先在池中分配 16 字节写入字符串this is a test再realloc为 15 字节随后断言指针非空且字符串内容完整test/tbbmalloc/test_scalable_allocator.cpp。析构函数~fixed_pool() { destroy(); }析构时调用pool_base::destroy()最终执行rml::pool_destroy(my_pool)include/oneapi/tbb/memory_pool.h释放池内部所有簿记结构与分配出的内存对象。最小可用示例参考手册给出的官方示例完整如下#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h ... char buf[1024*1024]; oneapi::tbb::fixed_pool my_pool(buf, 1024*1024); void* my_ptr my_pool.malloc(10); my_pool.free(my_ptr);这段代码演示了从固定池分配的最小闭环构造 1MB 缓冲区上的池 → 分配 10 字节 → 释放。更贴近实战的写法建议显式检查返回值并包含异常处理#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h #include cstdio int main() { static char buf[1024 * 1024]; // 固定 1MB 缓冲区 try { oneapi::tbb::fixed_pool pool(buf, sizeof(buf)); void* p pool.malloc(10); // 分配 10 字节 if (!p) { std::fprintf(stderr, allocation failed\n); return 1; } // ... 使用 p ... pool.free(p); // 归还给池 pool.recycle(); // 可选一次性回收全部对象 } catch (const std::invalid_argument) { std::fprintf(stderr, invalid buffer/size\n); } catch (const std::bad_alloc) { std::fprintf(stderr, pool creation failed\n); } return 0; }将 fixed_pool 接入 STL 容器fixed_pool自身并不满足 C 标准分配器Allocator要求但 oneTBB 提供了配套的 memory_pool_allocator 模板类它为内存池提供 C 分配器接口主要目的就是让内存池可用于 STL 容器。其构造函数接受memory_pool 或fixed_pool 两种池实例因此可以把fixed_pool无缝接入容器#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h #include list ... typedef oneapi::tbb::memory_pool_allocatorint pool_allocator_t; std::listint, pool_allocator_t my_list(pool_allocator_t(my_pool));上述用法中my_pool既可以是oneapi::tbb::memory_poolAlloc的实例也可以是oneapi::tbb::fixed_pool的实例。从源码看memory_pool_allocatorT, P pool_base保存指向池的指针pool_type *my_pool其allocate调用my_pool-mallocdeallocate调用my_pool-free两个不同元素类型的分配器相等性operator也以是否指向同一个池为判据include/oneapi/tbb/memory_pool.h。注意一个重要的生命周期约束参考手册对memory_pool的 caution 同样适用于嵌套场景若底层分配器引用的是另一个可扩展内存池则内层池必须在外层池被销毁或 recycle 之前销毁否则会留下悬空引用。使用注意事项与边界综合参考文档、源码与测试使用fixed_pool时应牢记以下边界缓冲区生命周期池持有的是调用方缓冲区的指针而非所有权缓冲区必须比池存活得更久且在整个池生命周期内地址保持有效池析构后缓冲区方可复用。缓冲区只会被底层消费一次allocate_request内部通过将my_size置 0 保证缓冲区不得使用两次源码断言信息原文The buffer must not be used twice.。零值与空指针非法size 0或buffer nullptr构造必然抛出std::invalid_argument。过小缓冲区不可用从测试看池需要有足够的内部空间约 16KB 级别才能完成分配否则malloc返回nullptr。不可拷贝fixed_pool继承自no_copy既不能拷贝也不能赋值只能通过引用传递。分配失败语义malloc失败返回nullptr而不抛异常务必检查返回值。总结fixed_pool是 oneTBB Scalable Memory Pools 中面向固定缓冲区场景的内存池实现。它以一块由调用方提供的定长内存为唯一来源通过底层 RML 池接口pool_create_v1/pool_malloc/pool_free/pool_realloc/pool_reset提供线程安全、可扩展的malloc/free/realloc/recycle操作并可通过memory_pool_allocator接入 STL 容器。对需要精确控制内存来源、追求低碎片或复用整块缓冲区的并行程序而言它提供了一个开箱即用的标准答案其约束不可拷贝、缓冲区大小下限、一次性消费语义则保证了实现的简单与确定性。赞分享并发编程高性能计算【免费下载链接】oneTBBoneAPI Threading Building Blocks (oneTBB)项目地址https://gitcode.com/gh_mirrors/on/oneTBB点击查看免费下载相关推荐mold 内嵌 oneTBB 的 fixed_pool 类深度解析在定长缓冲区上构建可扩展内存池mold 内嵌 oneTBB 的 fixed_pool 类深度解析在定长缓冲区上构建可扩展内存池 本文以 oneTBBoneAPI Threading Bu开发工具构建工具系统编程eLabFTW终极指南如何5步搭建免费开源的电子实验记录本系统eLabFTW终极指南如何5步搭建免费开源的电子实验记录本系统 还在为实验室数据管理混乱而烦恼吗每天花大量时间整理实验笔记、追踪试剂库存、协调团队工作让我科研后端F´ 框架 Svc::BufferManager 组件深度解析基于 Bin 池的固定大小内存缓冲管理F´ 框架 Svc::BufferManager 组件深度解析基于 Bin 池的固定大小内存缓冲管理 Svc::BufferManager 是 F´F Pr嵌入式系统编程上一篇3步为Windows注入macOS光标魅力的完整解决方案下一篇FakeLocation终极指南Android应用级虚拟定位的完整技术解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考