
如果你以前把训练系统理解成“前向、反向、AdamW 更新,再加一个学习率调度器”,那你只看到了算法表面;真正把一个大模型训练程序从单卡推向多卡、从 FP32 推向 BF16、从能跑推向跑得稳,决定成败的往往不是那些写在论文标题里的大词,而是几个你在第一眼读代码时容易忽略的小机制——一个用 FMA 写出来的lerp,一份 FP32 master weights 的影子副本,一段不用atomicAdd的全局范数归约,一个看似朴素却极其关键的ShardInfo{offset, size},以及一条把梯度、优化器状态、参数副本重新织回一致性的ReduceScatter - Local AdamW - AllGather闭环。因为在 llm.c 里,adamw.cuh不是孤立的优化器 kernel,schedulers.h也不是一个可有可无的工具头文件,global_norm.cuh更不只是“梯度裁剪之前算个范数”,它们都嵌在同一条主训练链上:数据被多个 rank 切开,梯度在多个 GPU 上生成,梯度范数在分片状态下被重新定义,学习率按 step 调度,AdamW 在局部 shard 上更新,再通过 NCCL 把参数重新拼回完整模型。下面我们就从单卡 AdamW 开始,一路走到多机多卡 ZeRO-1,把这条链完整拆开。一、先把主线讲清楚:这四个文件在训练闭环里分别扮演什么角色如果你直接打开这些源码文件,很容易产生一