假设你在做一个 AI infra 系统设计题:训练数据在 A 区域,GPU 空闲窗口在 B 区域,平台同时收到三类任务:一个 30 天主训练、几十个 2 小时调参实验、还有一批随时会被取消的评估 run。你不能简单回答"先把数据复制到 B 区域再开跑",也不能简单回答"全部远程读,靠缓存顶住"。
更工程化的答案应该是:先做任务分型,再决定数据位置策略。
本文只讨论"训练数据预搬全量 vs 按需水合/分层缓存的任务分型"。事实边界先说清:Meta 在 2026-07-01 官方公开资料中披露,其 AI storage blueprint 涉及 Tectonic、BLOB、统一元数据 schema、O(1) chunk 地址解析、胖客户端、区域 BLOB、GPU 主机内存/闪存 L1/L2、区域闪存 BLOB L3、全局 HDD BLOB、deep prefetch、TTL/LRU、容量与配额感知驱逐、hedged reads 和动态并发控制。Meta 同时报告了分布式数据缓存平均 80% 命中率、read-plan 元数据缓存访问 1-2ms、旧路径部分元数据查找累计可达数百毫秒、训练数据 ingestion 可能等待数小时。这些全部限定为 Meta 2026-07-01 官方公开场景,不代表行业基准、通用 SLA 或其他团队可复现结果。本文所有公式、阈值、门禁、TypeScript 示例、L3 配额和停止水合条件,均为面试演算占位,不是 Meta 公开阈值,也不是生产建议;真实系统应由压测、历史 workload、GPU 成本和区域带宽预算决定。
1. 任务分型表:先决定任务,不要先决定缓存
| 任务类型 | 典型特征 | 推荐初始策略 | 主要风险 | 退出/调整条件 |
|---|---|---|---|---|
| 长主训练 | 数周/月级运行,多 epoch,高复用 | 全量预搬或强预热 | 启动慢,但运行期稳定 | 复制等待远小于总运行收益 |
| 短实验 | 分钟/小时级运行,低复用,频繁取消 | 按需水合 + deep prefetch | cold miss 被同步放大 | GPU stall 或 barrier miss 超预算 |
| 多变体调参 | 同一快照,多 run,部分共享 shard | 混合策略:热 shard 预热,长尾水合 | L3 被单租户吃满 | tenant quota 或 eviction rate 超预算 |
| barrier 敏感训练 | worker 同步等待明显 | 更偏预搬或扩大 L3 | 少量 miss 放大全组等待 | cold miss near barrier 升高 |
| checkpoint 密集任务 | 周期性大流量写出/读回 | 限制水合并发,动态降速 | checkpoint egress spike | 出口峰值压过正常读取 |
| 不稳定探索 run | 经常 cancel/retry | 小窗口水合,强取消清理 | prefetch waste bytes 高 | waste bytes 或取消残留超预算 |
这张表的核心是:策略跟着任务生命周期走。长训练追求稳定摊销,短实验追求快速进入第一批数据,多变体任务要考虑复用,barrier 敏感任务要关注 miss 分布,checkpoint 密集任务要防出口峰值。
2. 一个够用的预算式
可以从这个预算式开始:
text
pre_copy_cost =
T_copy * waiting_cost
- reuse_benefit(T_run, epochs, reuse_rate)
hydration_cost =
time_to_first_batch
+ cold_miss_penalty * miss_count_weighted
+ checkpoint_egress_penalty
+ prefetch_waste
+ eviction_risk
choose_hydration_if:
hydration_cost + risk_buffer < pre_copy_cost
实际面试里不需要把公式写得很复杂,但变量必须解释清楚:
T_copy:复制完整训练快照的墙钟时间,来自数据量、effective_bandwidth、元数据准备、校验和队列。T_run:任务预计运行时长。epochs:同一份数据被重复扫描的轮数。reuse_rate:这次数据搬运能否服务后续 run。cold_miss_penalty:miss 导致的 GPU 等待,不是存储请求慢多少,而是训练整体被拖住多少。GPU_hour_cost:GPU 空等或低利用率的成本。effective_bandwidth:真实有效带宽,包含跨区、限流、拥塞和多租户干扰。prefetch_window:系统能提前几分钟知道并水合未来数据。
其中最容易被低估的是 miss_count_weighted。一次普通 miss 可能只让单个 worker 慢一下;一次 barrier 前 miss 可能让整组 worker 等最慢者。权重不同,策略就不同。
3. Cache tier:不要把"缓存"说成一个桶
一个更清楚的分层可以这样表达:
| Tier | 位置 | 作用 | 典型策略 |
|---|---|---|---|
| Source | 全局 HDD BLOB / 远端事实源 | 保留完整数据事实 | 强一致快照、低成本容量 |
| L3 | 区域闪存 BLOB | 跨 run、跨节点共享热数据 | TTL、LRU、容量/配额感知驱逐 |
| L2 | GPU 主机本地闪存 | run 内高频读取 | active window、顺序预取 |
| L1 | GPU 主机内存 | 当前 batch / 下一批 | dataloader prefetch |
| Metadata cache | read-plan / chunk 地址 | 降低元数据路径开销 | warm_metadata、取消后可回收的预取块标记 |
Meta 2026-07-01 官方公开场景里提到 GPU 主机内存和闪存作为 L1/L2、区域 flash BLOB 作为 L3、全局 HDD BLOB 作为最终事实源,并提到 deep prefetch 会水合未来几分钟数据并预热 metadata cache。这里的"未来几分钟"不是通用 SLA,只是官方公开描述的场景范围。
4. 运行阶段检查表
运行时不要把设计讲成一条抽象流程,而是把每个阶段写成"触发、观察、退出动作"。这更接近工程面试里真正要讲清的东西:什么时候继续水合,什么时候缩小窗口,什么时候停止水合并做 active-window 预搬。
| 阶段 | 触发 | 观察 | 退出动作 |
|---|---|---|---|
submitted |
任务进入队列 | dataset、region、预计运行时长、epoch、GPU 数、优先级是否齐全 | 进入任务分型;缺字段则要求补齐约束 |
classify |
基础约束齐全 | T_copy、T_run、epochs、reuse_rate、L3 quota、历史 miss 分布 |
选择 pre-copy、hydration 或 hybrid 初始策略 |
snapshot/check |
已选初始策略 | snapshot 是否可读,shard 列表是否完整,目标区域是否可达 | 校验失败则不启动训练;校验通过才进入预热 |
deep_prefetch |
短实验或 hybrid 任务准备启动 | 首批数据、未来窗口、预取速率、L3 剩余配额 | 首批数据到位后允许进入 running;配额不足则缩小窗口 |
warm_metadata |
shard 读取计划已确定 | read-plan / chunk 地址是否在启动前预热 | 元数据抖动下降后进入 running;击穿风险高则限速 |
running |
第一批数据已到 | GPU stall、cold miss 位置、L3 eviction、待水合数据量 | 指标稳定则继续;越界则缩小窗口或进入 active-window 预搬 |
checkpoint_spike |
checkpoint 或恢复流量升高 | checkpoint egress、正常读取延迟、预取并发 | 动态降低水合并发,保护训练读取 |
cancel/evict |
run 被取消或重试 | 已预取块是否仍被其他 run 使用,是否占用热缓存 | 用预取块回收标记释放无用块,保留仍有复用价值的热块 |
active-window pre-copy |
停止水合条件被触发 | barrier miss、GPU stall、L3 驱逐、出口峰值是否继续恶化 | 复制 active window 或暂停新水合任务,避免继续拖 GPU |
关键点是,active-window 预搬不是失败,而是系统保护。工程系统最怕的是水合已经不适合当前任务,却因为没有停止条件继续拖 GPU。
5. TypeScript 风格成本估算
下面所有阈值只用于面试演算占位,不是 Meta 公开阈值,也不是生产建议;真实系统应由压测、历史 workload、GPU 成本和区域带宽预算决定。代码只做成本估算与预算比较,不解释运行阶段。
ts
type Strategy = "PRE_COPY" | "HYDRATE" | "HYBRID";
type RunInput = {
dataTb: number;
estimatedRunHours: number;
epochs: number;
reuseRate: number;
gpuCount: number;
gpuHourCost: number;
effectiveBandwidthTbPerHour: number;
l3QuotaTb: number;
activeWindowTb: number;
barrierSensitive: boolean;
checkpointHeavy: boolean;
};
type RuntimeBudget = {
maxGpuStallMinutes: number;
maxColdMissNearBarrierRatio: number;
maxL3EvictionRate: number;
maxPrefetchWasteTb: number;
maxCheckpointEgressGbps: number;
maxUnhydratedActiveWindowTb: number;
};
type RuntimeMetrics = {
gpuStallMinutes: number;
coldMissNearBarrierRatio: number;
l3EvictionRate: number;
prefetchWasteTb: number;
checkpointEgressGbps: number;
unhydratedActiveWindowTb: number;
};
function estimateCopyHours(input: RunInput): number {
return input.dataTb / input.effectiveBandwidthTbPerHour;
}
function chooseInitialStrategy(input: RunInput): Strategy {
const tCopy = estimateCopyHours(input);
const runWeight = input.estimatedRunHours * input.epochs;
const copyIsSmallRelative = tCopy < runWeight * 0.05;
const shortLowReuse = input.estimatedRunHours < tCopy && input.reuseRate < 0.4;
const l3TooSmall = input.activeWindowTb > input.l3QuotaTb * 0.8;
if (copyIsSmallRelative && input.epochs >= 3 && input.reuseRate >= 0.6) {
return "PRE_COPY";
}
if (input.barrierSensitive && l3TooSmall) {
return "PRE_COPY";
}
if (shortLowReuse && !l3TooSmall && !input.checkpointHeavy) {
return "HYDRATE";
}
return "HYBRID";
}
function shouldStopHydrating(metrics: RuntimeMetrics, budget: RuntimeBudget): boolean {
return (
metrics.gpuStallMinutes > budget.maxGpuStallMinutes ||
metrics.coldMissNearBarrierRatio > budget.maxColdMissNearBarrierRatio ||
metrics.l3EvictionRate > budget.maxL3EvictionRate ||
metrics.prefetchWasteTb > budget.maxPrefetchWasteTb ||
metrics.checkpointEgressGbps > budget.maxCheckpointEgressGbps ||
metrics.unhydratedActiveWindowTb > budget.maxUnhydratedActiveWindowTb
);
}
这段代码要表达的不是"写一个万能分类器",而是让面试官看到你会把训练数据位置选择拆成可讨论的输入、成本和预算。真实系统里,这些预算来自压测、历史 workload、GPU 稀缺度、区域带宽和业务优先级。
6. 三个本文示例
示例 A:短实验不要等全量复制
- 数据:24TB。
effective_bandwidth = 4TB/h。T_copy = 6h。T_run = 1.5h。epochs = 1。reuse_rate = 0.2。prefetch_window = 10min。- L3 quota 足够覆盖 active window。
选择:HYDRATE。
理由:全量复制等待远大于运行时间,复用率低,且 L3 能覆盖热窗口。保护条件是限制单 run 预取速率,观察 time-to-first-batch 和 cold miss near barrier。如果 GPU stall 连续超过本文演算预算,就切到 hybrid 或 active-window 预搬。
示例 B:主训练不要盲目水合
- 数据:600TB。
T_copy = 12h。T_run = 30d。epochs = 8。reuse_rate = 0.9。- checkpoint 恢复稳定性要求高。
选择:PRE_COPY。
这是第一个明确反例:长训练、多 epoch、高复用更适合预搬。12 小时等待被 30 天和 8 个 epoch 摊薄,稳定数据路径比启动快几个小时更有价值。按需水合在这里可能带来持续 cold miss、checkpoint 恢复不确定和更复杂的容量竞争。
示例 C:L3 不足 + barrier 敏感,水合会反噬
- active working set:90TB。
- L3 quota:40TB。
prefetch_window = 5min。- 每个 epoch 末尾有同步 barrier。
- cold miss 主要落在长尾 shard。
- checkpoint 周期与预取窗口重叠。
选择:PRE_COPY 或先扩 L3 / 改 shard 计划后再 HYBRID。
这是第二个明确反例:barrier 敏感且 L3 容量不足时,水合可能比预搬更差。平均命中率看起来不错也没用,因为最贵的 miss 出现在最不能 miss 的位置。
7. 保护预算:水合不是无限后台任务
按需水合要能上线,至少需要这些保护:
| 控制项 | 解决什么问题 | 设计要点 |
|---|---|---|
| 令牌桶 | 防止短实验同时抢带宽 | 按租户、区域、run class 分桶 |
| L3 配额 | 防止单租户吃满区域缓存 | hard quota + borrow quota + 回收优先级 |
| 预取块回收标记 | 防止已取消任务继续占热缓存 | 记录 cache block 属于哪个预取窗口、是否被其他 run 复用、何时可回收 |
| hedged read cap | 防慢节点但不放大后端负载 | 限制额外请求比例和触发条件 |
| dynamic concurrency | checkpoint 峰值时自动降并发 | 用应用层拥塞信号调 parallelism |
| cancellation cleanup | 取消后释放预取数据 | cancel 后立即停止 deep prefetch,延迟回收热共享数据 |
| 停止水合条件 | 防止错误策略拖垮 GPU | stall、miss、eviction、egress 超预算时转 active-window 预搬 |
这里可以引用 gRPC 中文文档对 request hedging 的说明:对冲请求能降低慢请求影响,但会增加后端负载,所以必须有上限、延迟触发和重试预算。InfoQ 中文文章也可以作为中文参考,提醒对冲机制的收益依赖场景和边界,不能把特定模拟结果套到训练存储系统。NVIDIA 中文 AI 存储层级资料则适合支撑"GPU、主机内存、本地闪存、共享存储和网络要一起看"的背景。
8. 故障注入表
| 故障注入 | 观察指标 | 预期行为 | 失败信号 |
|---|---|---|---|
| L3 冷启动 | time-to-first-batch、待水合数据量 | 首批小窗口水合,逐步扩大 | 启动后 GPU 长时间空等 |
| 单租户 miss 放大 | per-tenant 待水合数据量 | 令牌桶限速,保护其他租户 | 一个租户拖慢整个区域 |
| checkpoint egress spike | checkpoint_egress_peak | 动态降低 prefetch 并发 | checkpoint 与水合互相抢出口 |
| 慢存储节点 | read latency tail、hedged count | hedged read 有上限触发 | 对冲请求导致后端负载飙升 |
| cancel 后残留 | prefetch_waste_bytes | cleanup 按预取块回收标记清理 | 旧 run 数据挤掉新 run 热数据 |
| 元数据缓存击穿 | metadata cache miss | warm_metadata 提前预热 | 大量 run 同时卡在 metadata |
| L3 容量不足 | eviction rate、reuse hit | 降低水合窗口或转预搬 | 刚水合的数据反复被驱逐 |
| barrier 前 cold miss | cold_miss_near_barrier | 预热长尾 shard 或预搬 | 平均 hit 高但 GPU 同步等待 |
故障注入不是为了证明系统永远没问题,而是为了让策略选择可观测。只要无法观测,就无法知道应该继续水合还是退出。
9. 指标门禁
面试演算时可以先用这些示例门禁占位,方便说明该观察什么;它们不是生产建议,也不是 Meta 公开指标。
text
time_to_first_batch_minutes < 15
gpu_stall_ratio < 3%
cold_miss_near_barrier < 1%
l3_eviction_rate < 10%
prefetch_waste_bytes / hydrated_bytes < 15%
checkpoint_egress_peak < region_budget * 0.7
unhydrated_active_window within token_bucket_budget
stop_hydration_events reviewed per run class
真正的门禁应该由平台成本、GPU 稀缺度、区域带宽、训练框架和业务优先级决定。
更重要的是门禁的方向:
time_to_first_batch看启动速度。gpu_stall_ratio看 GPU 有没有被数据拖住。cold_miss_near_barrier看最贵 miss 是否集中。l3_eviction_rate看缓存是否在自我打架。prefetch_waste_bytes看取消和误判成本。checkpoint_egress_peak看水合是否和 checkpoint 抢资源。unhydrated_active_window看后台水合是否追得上计算。
10. 面试里怎么收束
一个工程化回答可以这样结束:
先根据 T_copy / T_run / epochs / reuse_rate / cold_miss_penalty / GPU_hour_cost / effective_bandwidth / prefetch_window 做任务分型。长训练、多 epoch、高复用时,预搬仍然合理;短实验、低复用、启动等待极贵时,按需水合可以把等待拆到后台。然后用 L1/L2/L3、deep prefetch、metadata warming 和动态并发来执行,用令牌桶、L3 quota、hedged read cap、预取块回收标记和 cancellation cleanup 控制风险。最后设置停止水合条件,发现 GPU stall、barrier miss、eviction 或 checkpoint egress 超预算,就转 active-window 预搬或缩小水合窗口。
把 T_copy、T_run、reuse_rate、cold miss 位置、L3 配额 填成一张预算表,再用 offer.cc 追问每个数字从哪里来。
参考资料:
- Meta Engineering:Meta's AI storage blueprint at scale
engineering.fb.com/2026/07/01/... - At Scale Conference:AI Storage Blueprint
atscaleconference.com/videos/ai-s... - gRPC 中文文档:请求对冲
grpc.org.cn/docs/guides... - NVIDIA 中文技术博客:AI 训练与推理的存储层级
developer.nvidia.cn/blog/tips-o... - InfoQ 中文文章:自适应请求对冲的边界
www.infoq.cn/article/htL...