mold 项目内嵌 oneTBB 的 fixed_pool 详解:基于固定缓冲区的可扩展内存池

发布时间:2026/9/14 9:43:11
mold 项目内嵌 oneTBB 的 fixed_pool 详解:基于固定缓冲区的可扩展内存池 mold 项目内嵌 oneTBB 的 fixed_pool 详解基于固定缓冲区的可扩展内存池【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/moldfixed_pool是 oneTBBThreading Building Blocks提供的可扩展内存池类它从一个由用户预先提供、大小固定的内存缓冲区中执行 malloc/free/realloc 语义的内存分配且分配行为可随处理器数量扩展。本文以 mold 仓库内嵌的 oneTBB 源码为蓝本完整讲解fixed_pool的启用方式、类接口、构造函数语义、成员函数行为并结合 memory_pool.h 的底层实现剖析其工作原理帮助读者在自己的并发程序中安全、高效地使用固定缓冲区内存池。一、什么是 fixed_pool固定缓冲区的可扩展分配fixed_pool是一个类class用于从一块固定大小的缓冲区中进行可扩展的内存分配。它的核心特征包括所有可供分配的内存在构造时通过构造参数一次性传入即用户自备缓冲区池本身不向系统申请额外内存分配与释放操作是线程安全的并且扩展性随处理器数量提升scales with the number of processors它满足 oneTBB 定义的 Memory Pool 命名要求即提供recycle()、malloc()、free()、realloc()这一组标准接口。与同为内存池的memory_pool模板类不同fixed_pool不依赖任何底层分配器——它管理的全部内存都来自构造时传入的那块缓冲区。这意味着它特别适合以下场景预分配一块大内存区域之后所有小对象分配都从中取避免对操作系统的反复调用对内存使用总量有硬性上限、不允许向系统超额申请的场景高并发下需要低开销、可扩展分配且对象生命周期由池统一管理的场景。注意fixed_pool属于 oneTBB 的预览preview特性要使用它必须先定义宏TBB_PREVIEW_MEMORY_POOL并将其置为 1。二、启用预览特性TBB_PREVIEW_MEMORY_POOL 宏oneTBB 对尚未定稿的新特性采用“预览宏”机制进行门控。fixed_pool、memory_pool、memory_pool_allocator均被TBB_PREVIEW_MEMORY_POOL宏保护。这一点在源码中有强制校验在 oneapi/tbb/memory_pool.h 头部#if !TBB_PREVIEW_MEMORY_POOL #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h #endif也就是说如果在未定义该宏的情况下直接#include oneapi/tbb/memory_pool.h编译会直接报错。同样聚合头文件 oneapi/tbb.h 也只在宏开启时才包含 memory_pool.h#if TBB_PREVIEW_MEMORY_POOL #include oneapi/tbb/memory_pool.h #endif正确的启用方式是在任何包含头文件之前先定义宏#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h官方示例 fixed_pool_example.cpp 也是采用这一顺序第一行定义宏第二行包含头文件。如果希望在整个翻译单元统一启用也可以在编译命令行中通过-DTBB_PREVIEW_MEMORY_POOL1GCC/Clang或/DTBB_PREVIEW_MEMORY_POOL1MSVC传入。三、类接口总览fixed_pool 的 Synopsisfixed_pool位于命名空间oneapi::tbb中头文件为oneapi/tbb/memory_pool.h。其完整类声明如下namespace oneapi { namespace tbb { class fixed_pool { public: fixed_pool(void *buffer, size_t size); fixed_pool(const fixed_pool other) delete; fixed_pool operator(const fixed_pool other) delete; ~fixed_pool(); void recycle(); void* malloc(size_t size); void free(void* ptr); void* realloc(void* ptr, size_t size); }; } // namespace tbb } // namespace oneapi几个值得注意的接口设计点不可拷贝拷贝构造函数与拷贝赋值运算符被显式delete。这是合理的——内存池内部维护着底层的池状态rml::MemoryPool 指针与缓冲区归属关系不允许复制因此也没有no_copy语义问题recycle()一次性回收与memory_pool一样fixed_pool通过recycle()一次性释放池中所有已分配对象而不是逐个 free与 C 运行时同名函数对应malloc/free/realloc三个成员函数分别对应 C 标准库同名函数的内存分配语义只是内存来源从系统堆换成了池管理的缓冲区。四、构造函数fixed_pool(void *buffer, size_t size)构造函数是fixed_pool的唯一入口其声明与语义如下fixed_pool(void *buffer, size_t size);Effects文档语义构造一个内存池管理由buffer指向的、大小为size字节的内存区域。文档同时声明如果库无法构造该类实例将抛出bad_alloc异常。结合源码可以更精确地了解实际行为。在 memory_pool.h 中构造函数实现为inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) { if (!buf || !size) // TODO: improve support for mode with exceptions disabled 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)); }从源码可以看到实际抛出的异常类型比文档描述更细致触发条件异常类型说明buffer为空指针或size为 0std::invalid_argument参数非法Zero in parameter is invalid二者之一为 0 即拒绝构造底层 rml 池创建失败pool_create_v1返回非POOL_OKstd::runtime_error抛出 Cant create pool通过throw_exception包装取决于TBB_USE_EXCEPTIONS配置禁用异常时行为有所不同由此可见传入的缓冲区既不能为空指针大小也不能为 0这是使用fixed_pool的第一条硬性约束。从源码还可以看到底层池的创建参数rml::MemPoolPolicy args(allocate_request, nullptr, size, /*fixedPool*/true);MemPoolPolicy的四个参数依次为内存请求回调、释放回调固定池为nullptr即不回调、池的大小上限此处为传入的size、以及fixedPooltrue标志——这明确告知 rmlruntime memory library这是一个固定池所有内存均来自单一外部缓冲区。五、成员函数详解recycle / malloc / free / reallocfixed_pool的四个核心成员函数语义如下成员函数语义void recycle()释放池中所有已分配的内存将池重置为可重新使用的状态void* malloc(size_t size)从内存池中分配size字节返回指向该内存的指针void free(void* ptr)释放由ptr指向的内存对象void* realloc(void* ptr, size_t size)将ptr指向的内存对象重新分配为size字节可能移动内存位置这些接口并非fixed_pool独自实现而是继承自公共基类pool_base。在 memory_pool.h 中pool_base将这些操作全部委托给 oneTBB 底层的 rml 内存分配库class pool_base : no_copy { public: void recycle() { rml::pool_reset(my_pool); } void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); } void free(void* ptr) { rml::pool_free(my_pool, ptr); } void *realloc(void* ptr, size_t size) { return rml::pool_realloc(my_pool, ptr, size); } protected: void destroy() { rml::pool_destroy(my_pool); } rml::MemoryPool *my_pool; };pool_base被设计为no_copy不可拷贝内部仅持有一个rml::MemoryPool* my_pool句柄。四个成员函数分别对应 rml 的pool_reset、pool_malloc、pool_free、pool_realloc。realloc的存在还允许底层实现做一些低级优化例如原地扩容避免数据搬移。析构时则通过destroy()调用rml::pool_destroy释放整个池。六、Memory Pool 命名要求所有内存池的公共契约fixed_pool满足 oneTBB 定义的Memory Pool 命名要求Named Requirement这是理解它与其他内存池组件关系的钥匙。根据 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字节在 oneTBB 中同时满足该命名要求的两个模型类型是memory_pool模板类从底层分配器如std::allocator申请大块内存再切分给用户fixed_pool类从固定大小的用户缓冲区中分配本文的主角。两者共享同一套接口但内存来源完全不同前者“可增长”底层分配器按需供块后者“固定”缓冲区用尽即失败。它们的公共基类正是上一节的pool_base——这从 memory_pool.h 的类继承关系可以印证memory_pool与fixed_pool都public pool_base。七、源码级原理fixed_pool 如何“消费”一块缓冲区理解fixed_pool底层原理的关键是它的内存请求回调allocate_request。在 memory_pool.h 中inline 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; }这段代码揭示了固定池的工作机制rml 在需要为池补充内存时会回调allocate_request该回调一次性把整个缓冲区交出返回my_buffer同时把要交付的字节数bytes设为my_size随后将my_size清零my_size被清零起到“一次性”标记作用——断言0 ! self.my_size表明缓冲区只能被取用一次重复取用属于编程错误在 debug 构建下会触发断言。也就是说固定池的“固定”体现在底层 rml 一次性获得整块用户缓冲区之后所有 malloc/free/realloc 请求都在 rml 内部从这块缓冲区中切分与回收。正因如此recycle()即rml::pool_reset可以把这块缓冲区内的已分配对象全部回收、供后续重新分配而缓冲区一旦被 rml 取用并耗尽无法再向池补充新的内存——这是固定池与可增长的memory_pool的本质区别。八、完整可运行示例从固定缓冲区分配与释放官方示例 fixed_pool_example.cpp 给出了最简洁的用法#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h int main() { char buf[1024]; oneapi::tbb::fixed_pool my_pool(buf, 1024); void* my_ptr my_pool.malloc(10); my_pool.free(my_ptr); }逐步解读char buf[1024];——栈上或任意位置预先准备一块 1024 字节的内存fixed_pool my_pool(buf, 1024);——用该缓冲区构造固定池。注意这里传入的是裸void*与字节数缓冲区只需大小足够不需要特定对齐要求之外的特殊属性对齐由 rml 内部根据请求大小处理my_pool.malloc(10);——从池中分配 10 字节my_pool.free(my_ptr);——释放该块内存归还给池可供后续分配复用。在实际项目中可以在构造阶段一次性malloc多块内存或者配合recycle()在“一轮任务结束”时整体回收。由于分配来自用户自备的缓冲区整个池的峰值内存占用是可预期、有上限的这在实时性敏感或内存受限的环境中是显著优势。九、与 memory_pool、memory_pool_allocator 的关系三种组件的协作fixed_pool不是孤立存在的。oneTBB 的 Scalable Memory Pools 体系由三个组件构成对应 scalable_memory_pools.rst 的 toctree组件类型内存来源定位memory_pool类模板typename Alloc底层分配器按需提供的大块内存可增长的线程安全内存池fixed_pool本文普通类构造时传入的固定缓冲区固定容量、可扩展分配的内存池memory_pool_allocator类模板typename T绑定某个memory_pool或fixed_pool实例把内存池包装成标准 C 分配器供 STL 容器使用其中memory_pool_allocator是让fixed_pool走进标准库容器的桥梁。其构造函数分别重载了绑定memory_pool与fixed_pool两个版本见 memory_pool_allocator_cls.rst底层实现则是在 memory_pool.h 中把allocate/deallocate转发给池的malloc/freeallocate失败时抛出std::bad_alloc。一个典型的组合用法参考官方示例 memory_pool_allocator_example.cpp 的变体#define TBB_PREVIEW_MEMORY_POOL 1 #include oneapi/tbb/memory_pool.h #include list int main() { char buf[65536]; oneapi::tbb::fixed_pool my_pool(buf, sizeof(buf)); using pool_allocator_t oneapi::tbb::memory_pool_allocatorint; std::listint, pool_allocator_t my_list(pool_allocator_t{my_pool}); my_list.emplace_back(1); // 节点内存来自固定池 }这里std::list的节点内存不再来自系统堆而是来自 64KB 的固定缓冲区——容器的峰值内存因此变得完全可控。memory_pool_allocator同时满足 ISO C 标准 [allocator.requirements] 的要求并提供rebind与operator/operator!比较是否绑定同一池实例可无缝用于std::vector、std::list、std::map等标准容器。十、使用注意事项与限制综合文档与源码使用fixed_pool时需注意以下几点先定义宏再包含头文件TBB_PREVIEW_MEMORY_POOL必须置 1否则编译报错#error强制校验缓冲区必须非空且大小非 0否则构造函数抛出std::invalid_argument池容量固定不可增长缓冲区一经 rml 取用池的可用内存总量即固定分配超出剩余容量会失败返回nullptr不会向系统申请更多内存构造失败异常文档声明失败时抛bad_alloc但从源码看参数非法抛invalid_argument、底层池创建失败抛runtime_error编程时应按实际实现处理不可拷贝fixed_pool拷贝构造与赋值被删除无法按值传递应通过引用或指针共享嵌套池的生命周期来自 memory_pool_cls.rst 的 caution如果底层分配器引用另一个可扩展内存池内层池必须在外层池被销毁或 recycle 之前销毁缓冲区不得提前释放池的生命周期内其管理的缓冲区必须保持有效池析构~fixed_pool→destroy()→rml::pool_destroy之后该缓冲区才可被安全地另行处置。结语fixed_pool以“用户自备一块固定缓冲区”的极简模型换来了线程安全、可随处理器数量扩展、且峰值内存完全可控的内存分配能力。配合memory_pool_allocator它能无缝融入 STL 容器体系为高并发程序的中间数据结构提供确定性的内存行为。理解其“缓冲区一次性整块交给 rml、池内自由切分回收”的实现机制有助于在真正需要时做出正确的容量规划与生命周期管理。本文对应的完整类声明与实现可直接在 oneapi/tbb/memory_pool.h 中查阅官方示例位于 fixed_pool_example.cpp更多内存池家族成员可参见 Scalable Memory Pools 参考页。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询