TL;DR
- C++17 以后 PMR 值得看,因为数据库系统会产生很多短生命周期小对象。
- PMR 和 smart pointer 解决的问题不同。smart pointer 描述 ownership;PMR 描述 allocation locality 和 lifetime grouping。
- 重要边界:我没有在检查的 DuckDB snapshot 里看到大量 PMR 使用。这里讨论的是设计方向,不是当前实现事实。
PMR 问的问题
PMR 是 std::pmr,全称 polymorphic memory resource。它是 C++17 的标准库特性。
它主要问的不是:
谁拥有这个对象?
这个问题 smart pointer 已经能回答。
PMR 问的是:
很多小对象、很多 container、生命周期差不多。
它们应该从哪里分配内存?
这个问题在数据库系统里很自然。
这种模式在哪里出现
数据库系统经常构造短生命周期结构:
- binder 里的临时 expression list
- optimizer 里的 join graph
- rule rewrite 的中间节点
- candidate plan/path
- query-local metadata
- debug/report structure
这些对象通常会在一个 query 或 optimizer phase 结束时一起死亡。
用普通 std::vector、std::unordered_map、std::string 时,每个 container 都走默认 allocator。小分配多了以后,内存会更分散,也更难统计。
PMR 可以让这些 container 共用一个 memory resource:
std::pmr::monotonic_buffer_resource arena;
std::pmr::vector<Node *> nodes{&arena};
std::pmr::unordered_map<int, Cost> costs{&arena};
std::pmr::string label{&arena};
这有点像 PostgreSQL MemoryContext 的 C++ 版本,但它和 STL container 结合得更自然。
PMR vs smart pointer
Smart pointer 描述 ownership:
谁负责销毁这个对象?
PMR 描述 allocation grouping:
这些对象属于同一个 query。
这些对象属于同一个 optimizer phase。
这些对象可以一起释放。
两者解决的是不同问题:
| 问题 | 更合适的工具 |
|---|---|
| 单个对象所有权 | unique_ptr |
| 资源自动释放 | RAII |
| 一组短生命周期小对象 | PMR |
| PostgreSQL C 代码里的区域生命周期 | MemoryContext |
不要到处都用
PMR 不能机械使用。
DuckDB 已经有自己的 execution memory layout:Vector、DataChunk、validity mask、selection vector、string heap、buffer manager state、hash table payload。这些都靠近 hot path。
PMR 在这些地方是否有帮助,要看代码生成、cache locality、memory accounting 和现有 memory manager。
更合理的起点是:
先用于 parser、binder、optimizer、debug structure。
谨慎进入 execution hot path。
不要绕过已有 memory manager 或 buffer manager。
PMR 有价值,是因为它匹配数据库里的生命周期模式,而不是因为它是现代 C++。