01 · 用户态内存分配器竞品分析框架与评分标准
适用场景:终端设备(手机 / 平板 / 手表 / 车机等内存受限、无 swap、系统按内存压力杀进程的客户端设备) 版本:v1.0 配套文档:02-analysis-doc.html(被评对象)、03-iteration-log.html(过程记录)
1. 目的与范围
1.1 目的
为"终端设备上选型 / 替换用户态内存分配器"这一决策,提供可复现、可审计、有实验数据支撑的竞品分析。本框架定义三件事:
- 分析什么:竞品集、分析维度、实验协议(§2–§4);
- 产出长什么样:说明文档的结构模板(§5);
- 好坏怎么判:文档评分标准与迭代协议(§6–§7)。
1.2 场景定义:终端设备的特殊约束
终端设备与服务端的核心差异,是本框架一切设计取舍的出发点:
| # | 终端约束 | 对分配器评测的含义 |
|---|---|---|
| C1 | 无 swap / 受 swap | RSS 即生死;phys_footprint(jetsam/LMK 判杀依据)是第一公民指标 |
| C2 | 系统按内存压力杀进程 | free 之后能否及时归还、归还速度,比吞吐更影响存活 |
| C3 | 能耗与热预算敏感 | 分配路径的指令数 / 缓存 footprint 间接影响功耗(本环境无法直测功耗,见 §9) |
| C4 | arm64、16KB 页 | 页粒度归还的成本收益与 4KB 页不同;结论不可从 x86_64 直接外推 |
| C5 | 动态注入受限(iOS 无 DYLD_INSERT 通道) | 替换通道(链接期 vs 注入期)本身是评测维度 |
| C6 | 包体积敏感 | 分配器二进制体积计入评测 |
| C7 | 峰值内存比稳态更致命 | 峰值 footprint、burst 场景必须覆盖 |
1.3 评测代理平台声明(重要)
本分析在本机 Apple M3(8 核 / 8GB / arm64 / 16KB 页 / macOS 26.4) 上执行全部实验。它与目标终端设备同 ISA 家族、同页大小、同内核家族(XNU),内存容量同为受限级别(8GB)。所有结论的适用边界限于"Apple Silicon 类终端平台",向 iOS 真机外推的每一条都必须显式标注外推依据与风险(对应评分项 E3)。iOS 真机验证列为扩展轨道 T3(本环境具备越狱 iPhone 实验通道,见 §9.3),仅在迭代需要时启用。
2. 竞品集定义
2.1 分档原则
按"能否在本环境获得并实测"分三档,档位差异必须在文档中显式声明,禁止将设计级结论冒充实测结论(对应评分项 E2)。
| 档位 | 定义 | 竞品 |
|---|---|---|
| 实测集(S 级数据) | 本机可运行、可注入/链接、可测 | 系统 libmalloc(基线)、jemalloc、mimalloc、tcmalloc |
| 尽力集 | 尝试实测,失败则降档并记录失败原因 | (本轮无:tcmalloc 已入实测集;snmalloc 因获取通道受限直接入设计级) |
| 设计级集(D 级数据,低置信度) | 无法本地实测,仅源码/文档级分析 | snmalloc、Scudo、PartitionAlloc、hardened_malloc |
2.2 实测集版本与接入通道(必须 pin 并记录)
| 竞品 | 版本 | 获取通道 | 接入机制 | 备注 |
|---|---|---|---|---|
| libmalloc(基线) | 随 macOS 26.4 | 系统 | 默认 | nano/magazine 分配器 |
| jemalloc | 5.3.1 | Homebrew bottle | DYLD_INSERT_LIBRARIES 注入(dylib 导出 _malloc) | 进程级替换 |
| mimalloc | 3.5.0 | Homebrew bottle | 链接期覆盖(官方 mimalloc.o override 目标) | 可执行文件级替换;与注入的差异见 §4.7 |
| tcmalloc | gperftools(版本以安装记录为准) | Homebrew | DYLD_INSERT_LIBRARIES 注入 | 进程级替换 |
2.3 网络受限说明
本环境 GitHub 直连不可达(超时),Homebrew CDN / PyPI 可达。因此以 GitHub 为唯一分发渠道的 snmalloc、hardened_malloc 无法本地构建,列入设计级集。此为环境约束,必须在文档中披露(对应 C1/E2 评分项),不得静默忽略。
3. 分析维度体系
五个维度,权重与数据等级要求如下。终端场景约束(§1.2)是维度选取的依据,文档必须给出 C1–C7 到维度的映射关系(对应评分项 B1)。
| 维度 | 内容 | 数据等级要求 | 映射的终端约束 |
|---|---|---|---|
| V1 性能 | 单线程吞吐、多线程扩展性、分配/释放延迟分位 | S 级(实测) | C3、C7 |
| V2 内存效率 | 稳态 footprint、有效内存比、峰值、free 后归还率与归还速度 | S 级(实测) | C1、C2、C7 |
| V3 安全加固 | 元数据布局、freelist 保护、隔离、quarantine、随机化 | D 级(设计级)+ 关键项接口验证 | C5 关联 |
| V4 可观测性 | 统计 API、heap profiling、与平台工具联动 | S+D 混合 | C1/C2 的问题定位效率 |
| V5 工程集成 | 构建可行性(实测记录)、二进制体积、接入通道、平台支持 | S 级(体积/构建实测)+ D 级 | C5、C6 |
4. 实验协议
4.1 公共协议(所有实验必须遵守,对应评分项 C3)
- 进程隔离:每次测量独立进程,消灭跨轮污染;
- 预热:正式测量前先执行 ≥100 万次分配释放预热(覆盖 tier/page 初始化);
- 重复:每个配置 ≥5 次独立重复,报告中位数与 IQR,禁止只报最好值;
- 环境固定:电源接通、无其他重负载任务、同一机器同一 OS 版本;
- 数据落盘:原始数据以 JSON/CSV 存入
experiments/data/round{N}/,命名规范{实验ID}_{竞品}_{工作负载}_{rep}.json,分析文档中每个数字都能回溯到具体文件(对应评分项 D1); - 诚实性:失败的实验、异常值、不利数据一律保留并说明,禁止选择性删除(对应评分项 E4)。
4.2 工作负载定义(W 系列)
| ID | 负载 | 参数 | 模拟的终端场景 |
|---|---|---|---|
| W1 | 小对象均匀 | 尺寸 U(16,128)B | 高频 UI/消息对象 |
| W2 | 混合分布 | 70% U(16,128)B / 20% U(129,1024)B / 9% U(1K,64K)B / 1% U(64K,1M)B | 真实 App 混合负载形态 |
| W3 | 稳态集 + 淤积(churn) | 维持固定 live 集(如 64MB 对象),持续换入换出 | 长期运行 App 的碎片累积 |
4.3 实验矩阵(E 系列)
| ID | 实验 | 方法 | 输出指标 | 服务的维度 |
|---|---|---|---|---|
| E1 | 单线程吞吐 | W1/W2,N 次 alloc/free 计时 | ops/s | V1 |
| E2 | 多线程扩展 | W1 × {1,2,4,8} 线程 | ops/s、加速比 | V1 |
| E3 | 延迟分位 | 逐次计时直方图(mach_absolute_time) | p50/p99/p999 分配与释放 | V1 |
| E4 | 稳态内存 | W3 固定 live 集,采样 phys_footprint | footprint、有效内存比 EMR | V2 |
| E5 | 归还曲线 | 分配至峰值后全量 free,30s 内周期采样 | 归还率 RR、归还时间常数 | V2 |
| E6 | 宏观负载 | 真实应用级工作负载(候选:自建 sqlite/JSON 处理管线) | 端到端时间、footprint | V1+V2 交叉验证 |
4.4 指标严格定义(文档不得出现口径漂移,对应评分项 C2/A4)
- ops/s:完成一次"分配并写入首字节 + 释放"的循环次数 / 耗时,按线程数分列;
- phys_footprint:
task_info(TASK_VM_INFO)返回值,与footprint(1)、jetsam 判杀口径一致; - 有效内存比 EMR =
live_logical_bytes / (phys_footprint_steady − phys_footprint_baseline),分母含分配器元数据与已提交未用页(有意如此:这是设备真正付出的代价); - 归还率 RR =
(phys_footprint_peak − phys_footprint_t) / (phys_footprint_peak − phys_footprint_baseline),t 取 free 完成后 30s; - 碎片(外部):
1 − live/committed,committed 无法直接从第三方分配器内部读取时,以 EMR 作为含碎片+元数据的综合代理指标并明示。
4.5 分配器活性验证(防"假注入")
每个竞品在测量前必须通过活性验证,证明分配确实由目标分配器服务:
- jemalloc:
mallctl("version")可解析 +malloc_stats_print统计量与工作负载吻合; - mimalloc:
mi_version()返回 3.5.x +mi_stats_print统计量吻合; - tcmalloc:
tc_version()可解析; - 系统:
malloc_zone_from_ptr()非 NULL。
未通过活性验证的数据不得进入报告,验证日志随数据落盘。
4.6 环境边界
- 本机 8GB 内存:工作负载 live 集上限 ≤ 512MB,避免内存压力干扰测量(记录于每轮数据);
- 关闭 benchmark 期间的终端重负载任务;不使用
taskset类绑核(macOS 受限),以重复测量控制方差。
4.7 接入机制差异披露
mimalloc 采用链接期覆盖(可执行文件内符号替换),jemalloc/tcmalloc 采用 DYLD 注入(进程级替换,影响所有动态库的 malloc 调用)。两者对本 harness 的自环测量等效,但对"进程级替换"语义不等效,文档必须在 V5 维度披露此差异。
5. 说明文档结构模板(02 文档的必备章节)
- 执行摘要:结论总表(竞品 × 五维得分/评级 + 一句话推荐);
- 范围与场景:终端约束 → 评测维度映射(引 §1.2);
- 竞品概览:架构对比表 + 版本/通道/数据等级声明;
- 评测方法学:环境、指标定义、协议、可复现性说明;
- 实验结果:按 V1–V5 分节,每个实验小节含设置、数据表/图、观察;
- 综合对比与机制归因:不止数字,要解释"为什么"(架构机制 → 数据形态);
- 场景化选型建议:按业务场景差异化推荐 + 适用条件;
- 风险与局限:平台外推、版本局限、未覆盖项;
- 附录:原始数据索引、脚本使用说明。
6. 文档评分标准(rubric)
评分对象:02-analysis-doc.html 的当前版本。总分 100。
6.1 维度与子项
A · 结构与可读性(15 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| A1 执行摘要 | 3 | 开篇 ≤1 页,含竞品×维度结论总表与明确推荐 |
| A2 结构导航 | 3 | 章节编号清晰、目录有效、交叉引用可达 |
| A3 图表规范 | 3 | 每个图表有编号、标题、单位、数据来源(实验 ID),无裸数字堆砌 |
| A4 术语一致 | 3 | 关键指标全文唯一定义、无同义漂移 |
| A5 篇幅纪律 | 3 | 正文聚焦结论,细节入附录,无重复段落 |
B · 场景与竞品覆盖(15 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| B1 场景刻画 | 5 | 终端约束 C1–C7 完整刻画并映射到维度与实验 |
| B2 竞品覆盖 | 6 | 实测集 4 个全覆盖(缺 1 扣 1.5);设计级 ≥2 个(缺 1 扣 1);缺失有原因说明 |
| B3 维度覆盖 | 4 | V1–V5 均有 S 或 D 级内容(缺 1 扣 1) |
C · 方法论严谨性(15 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| C1 环境披露 | 3 | 硬件 / OS / 编译器 / 各竞品版本与编译选项完整 |
| C2 指标定义 | 3 | 每个指标有严格定义与测量方法 |
| C3 实验协议 | 5 | 预热、≥5 次重复、中位数+IQR、进程隔离均落实 |
| C4 可复现性 | 4 | 脚本与数据入库、有一键重跑说明、抽查可复算 |
D · 数据支撑(25 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| D1 结论溯源 | 10 | 每条关键结论标注实验 ID + 数据文件,抽查 3 条全部可溯源满分,1 条不可溯源扣 3 |
| D2 数据完整性 | 8 | 计划矩阵(4 竞品 × E1–E6)实测填充率 ≥90%(缺失格须标注原因),每降 10% 扣 2 |
| D3 统计质量 | 4 | 报告离散度(IQR/极差)、异常值处理说明 |
| D4 宏观验证 | 3 | ≥1 个真实应用级负载(E6)数据支撑结论 |
E · 结论有效性(20 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| E1 推理链 | 6 | 结论由数据直接支撑、无逻辑跳跃;关键结论有机制归因 |
| E2 不确定性表达 | 4 | S/D 级数据区分声明;设计级结论未被表述为实测事实 |
| E3 平台外推控制 | 4 | macOS 代理结论向终端外推时逐条标注依据与风险 |
| E4 对比公平性 | 6 | 不利数据同样呈现;接入机制差异、版本差异对结论的影响被讨论 |
F · 决策可用性(10 分)
| 子项 | 分值 | 判据 |
|---|---|---|
| F1 场景化建议 | 4 | 至少 2 类场景给出差异化选型 + 适用条件 |
| F2 集成风险清单 | 3 | 替换通道、兼容性、体积、维护成本 |
| F3 后续行动 | 3 | 落地前的验证清单(含真机验证项) |
6.2 通过门槛
- 总分 ≥ 90;
- 每个维度得分率 ≥ 80%(A≥12, B≥12, C≥12, D≥20, E≥16, F≥8);
- D、E 两维不得低于 80%(数据支撑与结论有效性是一票否决区)。
6.3 版本目标
| 版本 | 目标分 | 说明 |
|---|---|---|
| v1 | ≥ 65 | 首轮:核心实验 + 文档骨架,预期短板在 D4/E3/F |
| v2 | ≥ 80 | 补延迟分位、归还曲线、统计质量、设计级竞品 |
| v3+ | ≥ 90(达标线) | 补宏基准、公平性讨论、机制归因 |
7. 评分与迭代协议
7.1 评委
- 两名独立评委:均为无历史上下文的独立代理,仅获得本 rubric、被评文档路径、数据目录路径;
- 评委任务:逐子项打分(0 至满分,允许 0.5 粒度)+ 每个扣分项给出"扣分理由 + 修复建议";抽查 3 条关键结论溯源到数据文件;
- 最终得分 = 两评委逐子项平均,写入
03-iteration-log.html; - 评委不可评分自己参与编写的内容(独立性由无上下文机制保证)。
7.2 迭代循环
文档 vN
→ 双评委独立打分(rubric 逐项 + 扣分理由)
→ Gap 分析(按扣分排序,锁定 Top 修复项)
→ 设计补充实验 / 文档修订(实验优先于修辞:数据缺失类扣分必须用实验修复,
禁止用改写措辞来"修复"数据类扣分)
→ 文档 vN+1 → 重新打分
7.3 退出条件
满足其一:
- 达标:通过 §6.2 全部门槛;
- 收益递减:连续两轮总分增量 < 3 且剩余扣分均源于本环境不可消除的限制(须逐条列出并说明为何不可消除),此时以当前版本定稿,并在评分记录中显著声明未达标项。
7.4 评分记录规范
每轮记录:版本号、日期、两评委逐项分数、平均分、Top-5 扣分与修复动作、本轮新增实验清单。评分只升不降不是目标——若新数据推翻旧结论,允许维度分下降,但必须记录原因(对应 E4)。
8. 工程与可复现性约定
malloc_/
├── docs/
│ ├── 01-framework.html ← 本文档
│ ├── 02-analysis-doc.html ← 被评对象(迭代演进)
│ └── 03-iteration-log.html ← 评分与迭代过程
├── experiments/
│ ├── bench/bench.c ← 统一 benchmark harness
│ ├── scripts/ ← 构建/运行/汇总脚本
│ └── data/round{N}/ ← 原始数据(JSON/CSV)
└── index.html ← 索引与一键重跑说明
约定:脚本是数据的唯一生成路径;改文档不改数据属违规(评分项 C4/D1)。
9. 局限与风险
- 平台代理:macOS ≠ iOS 真机(jetsam 行为、无 DYLD 通道、A 系列 vs M 系列内存带宽差异),E3 评分项强制逐条标注外推风险;
- 设计级竞品(snmalloc/Scudo/PartitionAlloc/hardened_malloc)无本地数据,其结论置信度天花板低一档;
- 能耗不可直测:C3 约束只能以延迟/吞吐间接代理,文档不得声称功耗结论;
- iOS 真机扩展轨道(T3):本环境具备越狱 iPhone(12 Pro / iOS 14.8)实验通道,如迭代中"平台外推"成为持续短板且时间允许,可启用真机补充实验;
- 8GB 本机内存限制工作负载规模(§4.6),超大规模场景(>1GB live 集)不在本轮范围。
10. 本框架的自检清单(写作前回顾)
- [ ] 五维是否都有对应实验或设计级分析?
- [ ] 每个数字是否有实验 ID 与数据文件?
- [ ] 每条结论是否讨论了反例与边界?
- [ ] S/D 级数据是否全程区分标注?
- [ ] 平台外推是否逐条标注?
- [ ] 不利数据是否保留?