一次 malloc,半个 GB:硬核解构 llm.c 如何用纯 C 管理 1.24 亿参数
打开train_gpt2.c的第 580 行,你会看到一个让任何写过 C 的人都会停下来多看两眼的函数——malloc_and_point_parameters()。这个函数只有 19 行,却一次性分配了 497MB 的连续内存,然后用一个指针数组把 16 个结构体字段全部"投影"到这块巨型内存的不同偏移位置上。
没有std::vector,没有Tensor对象,没有引用计数,没有垃圾回收。一次malloc,一个for循环,1.24 亿个浮点参数就安排得明明白白。
如果你用 PyTorch 训练过 GPT-2,你大概知道每个nn.Parameter背后有一整套 autograd 元数据、stride 信息、device 属性、dtype 标记。这些元数据在 Python 层提供了极大的灵活性,但它们也有代价——每个 Tensor 对象本身就要占几百字节的堆内存,50257×768 的 embedding 矩阵和一个标量 bias 享受着完全相同的对象开销。
llm.c 的做法是把这一切全部砍掉。它的核心设计哲学可以用一句话概括:用编译期已知的结构体布局,替代运行时的张量元数据管理。这不是偷懒,这是一种经过深思熟虑的工程权衡——当你的模型架构在编译时就完全确定(GPT-2 的层数、维度、头数全部硬编码在权重文件的 header 里),运行时的元数据管理就变成了纯粹的开销。
这篇文章要做的事情,就是把 llm.c 这套"零依赖、零碎片、零泄漏"的内存管理体系彻底拆开,让你看清
