读者:项目开发者(本仓内部工作文档)。本分册定位见
README.md;历史归档在../history.md。 状态:现行。
文档状态:L1 激活重计算(梯度检查点)已实施 / L2 内存池整块归还 + 统计已实施(L3 远期)。 关联文档:
../internal/02-operator-fusion.md(算子融合已完成部分,消除 seq² 物化)。
L1 激活重计算(梯度检查点)
- 接口:
Layer的set_checkpoint_mode / recompute_supported / forward_recompute / set_checkpoint_every契约;Model::set_checkpoint_every逐层分发;GPTModeloverride 实现块级检查点(每 N 个 GPTBlock 存一次输入,backward 重算)。 - 实现:
AttentionBase/FeedForward/GPTBlock/TransformerEncoderLayer传播 checkpoint 模式;Linear/ReLU/GeLU/SwiGLU/LayerNorm/RMSNorm/Softmax/AttentionBase在 checkpoint 模式下跳过中间激活缓存写入(forward 改用局部变量承载计算,避免空缓存读取)。 - 验收:
src/gpt_checkpoint_test.cpp(gpt_checkpoint_test)stride∈{1,2} 与全存基线逐位一致(max_abs=0);gpt_gradcheck/rmsnorm/swiglu/softmax/attn_gradcheck、matmul_fusion_test、ce_fusion_test全绿;text_train --checkpoint-every NCPU 端到端训练正常(loss 正常下降、exit=0)。
L1:RLA/RAPT 接入
RAPTBlock提供forward_recompute+activation_cache();RAPTModel提供块级检查点/offload/flush 管线(set_checkpoint_every/set_activation_offload/set_flush_interval,与 GPT 同档)。ReLULinearAttention在 checkpoint 模式下不写全部 7 项 backward 缓存(Qp/Kp/V_re/Q_normed/K_normed/Q_rms_inv/K_rms_inv),并在backward缺缓存时硬报错而非用空/陈旧张量算垃圾梯度;activation_cache()列出本层全部缓存 + 4 个子层缓存(offload 完整覆盖)。- 坑:复合层重写
forward_recompute必须调用虚函数set_checkpoint_mode(基类默认实现只改自身标志位,子层不重建缓存;stride>1 时会被上一步的陈旧缓存掩盖,表现为"某些 stride 能过、某些过不了")。 - 验收:
src/rapt_checkpoint_test.cpp并入rapt_test,stride∈{1,2} 与全存基线逐位一致(max_abs=0);GPU 侧rapt_offload_test与全存基线逐位一致。
L2 内存池整块归还 + 统计
MemoryPool:PoolStats+pool_debug_stats()(块数/占用/空闲/碎片)、release_idle_blocks()(整块vkFreeMemory+ 从blocks_移除,带retain_free_bytes_保留阈值防抖动)、set_retain_free_bytes()。GpuBackend::release_idle_pool_blocks()(先flush_pending_destroys()再归还);ComputeEngine提供release_idle_pool_blocks()/pool_stats();GpuEngineoverride。- 现行口径(M5):
pool_stats()不再有"CPU 返回空串"的空档——无池引擎(CPU)默认返回宿主直配账本direct{ blocks=… live_bytes=… peak_bytes=… total_blocks=… total_bytes=… }(nn::host_alloc_ledger(),在algebra_matrix.hpp的MatrixT分配/释放路径记账,relaxed 原子、元素热循环零参与);release_idle_pool_blocks()对 CPU 是语义成立的 no-op(直配无整块可归还)。两引擎同一动词、都非空,训练日志一套代码读两个引擎(17 §4.5)。
- 现行口径(M5):
- 接入:
text_train的--checkpoint-every N与每 step 末尾release_idle_pool_blocks()(end_batch 提交完成、延迟销毁已 flush 后调用,安全)。 - 回归:仅 GPU 内存管理路径,不影响 CPU 数值;L1 相关 gradcheck 全绿。
L3(自动融合 + 优化器降内存)— 待立项(2026-10-14 登记为 18-roadmap.md P4-3)
以实测配置为基准(滑动窗口,seq_len=1024,stride=1024,样本数 435736):
词表 32782 · 模型维度 1024 · 注意力头 16 · Transformer 层数 16
FFN 维度 4096 · 序列长度 1024 · 优化器 adamw · 批大小 6 · GPU 启用
该配置下 训练峰值显存 ≈ 27 GB(已含 M5/M6 融合收益)。
- 融合(M4/M5/M6)后,非线性象限(
seq²注意力、vocab×seq全 softmax)不再物化,≈ 0。 - 剩余 27 GB 的构成与融合无关,集中在激活缓存、优化器状态、内存池碎片化三处。
| # | 项目 | 计算 | 约占 27 GB |
|---|---|---|---|
| ① | 参数 + 梯度 + Adam(m/v) | ~236M × 4B的4× ≈ 3.7 | ~14% |
| ② | 逐层激活缓存(16 层) | 每层 ~0.4 ≈ 6.4 | ~24% |
| ③ | seq² 注意力分数/p | 融合已清除,≈ 0 | — |
| ④ | logits(LM head 输出) | 6×1024×32782×4B ≈ 0.8 | ~3% |
| ⑤ | 反向临时 + 内存池碎片(step 内不归还) | 其余 | ~59% |
注:①≈3.7、②≈6.4、④≈0.8 为可推导的硬性下限;⑤ 为反算残差,需实测采样确认真实构成(见"验证与回归")。
| 级别 | 手段 | 目标降幅(相对 27G) | 触及架构红线? |
|---|---|---|---|
| L1 白拿 | 激活重计算(梯度检查点) | ② 6.4→0.6,最大省 ~6G | 否(Layer/Model 级) |
| L2 回收 | 内存池复用/归还 + 生命周期边界 | ⑤ 中碎片部分,省 ~2-4G | 否(后端内存管理) |
| L3 算法 | 自动融合优化(替代手写注意力)+ 优化器降内存 | ③+① 二次缩减 | 否(复用现有自动融合路径) |
优先级按收益/风险排序:L1/L2 不影响数值正确性与分层铁律,风险最低、收益可立即验证。
core_config.hpp 中 using Scalar = float,本文档数字均为纯 fp32 基线(v1.2.0 起另有
f16 混合精度——Precision/PrecisionProfile,见 05-mixed-precision;本文档的显存账不适用于 f16 路径)。
fp32 基线下激活、梯度、权重、优化器态全部 4B。
- 层内 forward 为 backward 保留中间结果:如
compute_layer.hpp的input_cache_(Linear)、sigmoid_cache_(SiLU)、注意力保留的 Q/K/V 与 norm 输入等。 Model::forward→Model::backward(model_container.hpp)是逐层顺序执行,层间不丢弃激活。- 每层驻留约 8 个
B·seq·d与 2 个B·seq·d_ff的 fp32 副本(d_ff=4096 每张 100MB 是重要大头),16 层累加 ~6.4G。本节是"未启用梯度检查点"时的口径——启用 L1 checkpoint 后仅存块边界输入(见下文 L1)。
backend/compute_memory_pool.hpp:first-fit 子分配 + 相邻 free region 自动前后合并(O(log n)、O(1) 合并)。- 整块回收:空闲 Block 可经
release_idle_blocks()整块vkFreeMemory并移出blocks_(retain_free_bytes_阈值防抖动);text_train每 step 末尾调用release_idle_pool_blocks()。block 底材按需 128MB(或超尺寸单块)申请。 - 残余风险:碎片与闲置块在 step 内仍会累积(归还只发生在 step 边界),峰值生命周期仍是"一步之内"。
注意力 forward 为单 fold kernel(FoldSpec 分块流式:QKᵀ/掩码/online softmax/ΣwV 逐块完成,S 在 kernel 内逐块存在、从不落显存,见算子融合文档"关键算法"章状态横幅),backward 为 R/X 表达式 + batched_matmul。该路径已消除 BH·seq² 物化,剩余驻留为逐层 Q/K/V 激活集(属 L1 激活重计算可覆盖范围)。不手写 flash-attention 类融合 kernel:沿用现有自动融合/算子生成路径(fold 生成器即 AOT 结构驱动生成,非手写 kernel),由 L3 的自动融合优化统一推进。
无张量/序列并行、无 FSDP/ZeRO 跨设备分片。真正进入几十万 token 时只能靠压缩 batch/seq。
按"不动分层、不动精度、收益先验证"排序。
- 目标:把逐层激活从"全存"降到"只存 block 边界 input(或隔层存)",backward 重算中间,使② 从 ~6.4G 降到 ~0.6G。
- 归属层:
Model编排 +Layer契约新增可选方法(默认走缓存;支持者重算)。不动 ComputeEngine、不动精度。 - 接口:
// Model 层 void set_checkpoint_every(std::size_t /* layer_stride */); // Layer 可选 virtual bool recompute_supported() const { return false; } virtual Result<Tensor> forward_recompute( ComputeEngine&, const Tensor& /* saved_input */) { /* 重算 forward,只存必要 O(1) 状态 */ }
- 实现方式:
Model::backward遇到 checkpoint 层时,先forward_recompute(saved_input)重建局部中间,再按层回传梯度。 - 验证时序:逐层数 1/2/4/8 层梯度与现有全存结果数值一致(复用
gpt_gradcheck.cpp渠道)。 - 风险:约 +1 次前向 FLOPs(训练可接受);需保证重算路径与原始 forward 数值一致。
- 目标:回收 ⑤ 中碎片与闲置底材。
- 方案:
MemoryPool支持整块释放:allocation_count==0且全 region 空闲的 Block,调用vkFreeMemory并从blocks_移除(阈值控制,避免抖动)。- 统计上报:
pool_debug_stats()(总占用/空闲/block 数/碎片比),供采样确认真实构成。 - 评估按"训练-step 生命周期池 + 长生命周期参数池"分池,避免 step 间临时张量污染参数驻留区;或由
staging/Tensor::destructor返回池而非直接vkFreeMemory。
- 范围:仅
backend/compute_memory_pool.hpp与 Tensor 分配/销毁路径,不涉及算法。 - 风险:低;需 benchmark 分配总时长与碎片比变化。
内存池复用安全约定(新增原语必须遵守):中间 Tensor 的归还路径由以下两点保证正确性:
- batch 模式下
GpuBuffer::~GpuBuffer立即pool_->free()(内存可复用)+ 延迟vkDestroyBuffer(经pending_destroys_到end_batch提交并同步等待后统一销毁),满足VUID-vkDestroyBuffer-buffer-00922。- 安全性由两点保证:① 每个原语 op 录制时都包裹全内存屏障(input:
MEMORY_WRITE→SHADER_READ+ output:SHADER_WRITE→MEMORY_READ,src/dstStage 含 COMPUTE),保证 batch 命令缓冲内前序 op 的写入先完成并对后续可见;② 复用只发生在命令缓冲中更靠后的 op,且被复用的 scratch 在读取前总会先被该 op 写满。- 该安全性依赖每个 op 都插入输出屏障——新增原语若漏写 output barrier 会重新引入风险;新增 op 时须照此约定。
- 内存池复用不保证清零,任何"读取前期望为 0"的 op 必须显式
zero()(现有模式)。
- 自动融合(替代手写注意力 kernel):不手写任何融合算子;基于既有自动融合/算子生成路径,将注意力与 softmax 相关子图自动合并为更省内存的单一计算,消除 Q/K/V 激活集物化。
- 优化器降内存:采用 Adafactor 类无 m/v 优化器,去除每参数两份 moment 状态(对应总账①中约一半),不引入 fp16/fp8 低精度 moment。
| 级别 | 手段 | 预期省显存 | 累计(估) |
|---|---|---|---|
| L1 | 激活重计算 | ~6G | 27 → ~21G |
| L2 | 池归还+复用(碎片部分) | ~2-4G | ~21 → ~17-19G |
| L3 | 自动融合 + 优化器降内存 | ~2-4G | ~17 → ~13-15G |
说明:数值为估算;⑤ 的真实构成需采样后修正。理想状态下(L1+L2+L3)可降至 ~13-15G,接近成熟框架同量级。
- 显存采样:在训练 step 间记录
pool_stats(块数/已分配/空闲/碎片比)与 GPU 总占用曲线,标出 step 内峰值出现阶段(forward/backward/optimizer)。 - 逐项归因:跑一次 step,分别关闭激活缓存(假想)、池归还、自动融合,量化每项独立贡献——修正总账的⑤残差。
- 数值回归:
gpt_gradcheck、rmsnorm_gradcheck、swiglu_gradcheck、softmax_gradcheck、matmul_fusion_test、ce_fusion_test保持全绿;训练 loss 曲线与参考一致。 - 性能回归:确认"省显存"未以显著耗时退化为代价(重计算倍率、池复用消耗),用训练 step 耗时采样衡量。
| 项 | 说明 | 应对 |
|---|---|---|
| 重计算精度一致性 | 重算路径需与 forward 一致 | 先小层数回归、复用 gradcheck |
| 池归还抖动 | 频繁整块释放/重建开销 | 阈值 + 池分级,实测调参 |
| 自动融合子图边界 | 融合子图切分需与自动融合器对齐 | 复用现有自动融合回归渠道 |
| 优化器降内存 | 无 m/v 优化器收敛特性差异 | 独立评测 loss 曲线 |
| 分布式分片 | 超出当前后端范围 | 远期另立 |
- L1(激活重计算)与 L2(内存池归还/复用)已就位:验证渠道固定为
text_train+gpt_gradcheck/rapt_test+pool_stats采样。 - 下一步 = 立项 L3(自动融合 + 优化器降内存) 独立里程碑。
- 分布式按需远期推进。
- 不破坏分层铁律:L1/L2 只在
Model/Layer契约与backend内存管理内实现,ComputeEngine原语与expr_dsl不动。 - 不破坏注册表/AOT:L1/L2 不引入新 shader、不删改
expr_registry;不破坏主路径 JIT 兜底。 - 不违背确定性:全部路径保持定点、可复现;L1/L2 与精度设置正交(f16 混合精度另行记录在 05-mixed-precision,本文档账目为 fp32 口径)。