NanoEuler:一个人用纯C/CUDA手搓GPT-2

开发者justvugg在GitHub开源NanoEuler,用纯C和CUDA从零实现GPT-2规模模型的训练与推理,不依赖任何深度学习框架,是Karpathy llm.c之后又一个值得细读的极简实现。
又一个从零手搓GPT-2的项目,这次叫NanoEuler
6月底,一位GitHub ID为justvugg的开发者把自己的副业项目NanoEuler挂到了Hacker News上:用纯C和CUDA,从零实现一个GPT-2规模的语言模型,不依赖PyTorch,不依赖任何Python深度学习框架,连训练循环都是手写的。
这事听起来不新鲜——Karpathy的llm.c早就把这条路走通了,nanoGPT更是几乎成了入门LLM的标配教材。但NanoEuler值得拿出来说一句,原因有两个:第一,它不是llm.c的简单复刻,作者在kernel组织和反向传播实现上有自己的写法;第二,在2026年这个TensorRT-LLM、vLLM、SGLang满天飞的时间点,还有人愿意花精力把GPT-2的每一个算子用CUDA手写一遍,本身就说明社区对"理解底层"的需求一直没消失。
这个项目到底做了什么
打开仓库扫一眼,结构非常直白。没有setup.py,没有requirements.txt,只有几个.c、.cu和.h文件。整个项目把GPT-2的训练拆成了几块:
- Tokenizer:BPE的最小实现,能加载GPT-2原版的vocab和merges
- 前向传播:Embedding、LayerNorm、Causal Self-Attention、MLP(GELU激活)、最后的lm_head
- 反向传播:每一个forward kernel都对应手写的backward kernel
- 优化器:AdamW,自己实现m/v的更新
- 数据加载:直接读二进制token文件,按batch切
做过PyTorch的人看到这里大概会愣一下:在PyTorch里F.layer_norm一行就完事,loss.backward()自动反传。但在C/CUDA里,LayerNorm的forward要算mean、variance、normalize、scale+shift,backward要对输入x、对gamma、对beta分别求梯度,每一步都涉及reduction,每一个reduction都要考虑warp级别的shuffle、shared memory的bank conflict。一个LayerNorm前后向加起来几十行kernel代码,attention更是要小心处理causal mask和softmax的数值稳定性。
NanoEuler把这些全做了一遍。模型规模对标GPT-2 small(124M),也支持配置到medium、large。
跟llm.c比,差在哪、强在哪
绕不开的对比是Karpathy的llm.c。客观说,llm.c在工程完成度和性能优化上是当之无愧的标杆——它做了mixed precision、做了多GPU训练、做了FlashAttention的集成,甚至复现了GPT-2训练全过程的wall-clock time。NanoEuler目前还没到这个程度,单卡FP32训练,没有用cuBLAS之外的高级优化库。
但NanoEuler的代码组织更轻。llm.c为了追求性能,单文件代码量已经膨胀到让初学者望而却步的程度,宏定义和条件编译穿插,新手读起来需要先消化一遍。NanoEuler把kernel按功能拆成多个文件,每个kernel周围的代码量更少,作为"读懂GPT-2底层到底在算什么"的教学材料,反而更友好。
这种取舍其实挺典型:llm.c是"我要证明这条路可以做到极致",NanoEuler是"我要让自己彻底搞懂每一行在干嘛"。两个目标不冲突,但成品形态不一样。
为什么2026年还有人做这个
有人可能会问:现在推理有vLLM和TensorRT-LLM,训练有Megatron和DeepSpeed,连研究用的小模型都有nanoGPT这种Python实现,再写一遍C/CUDA到底图什么?
几个现实的理由:
理解抽象层之下到底发生了什么。 PyTorch把太多东西藏起来了。当你调一个scaled_dot_product_attention,你知道它在内部走的是FlashAttention-2还是cuDNN的fused kernel吗?知道它的内存访问模式吗?大多数业务工程师不需要知道,但做推理加速、做训练框架、做新硬件适配的人必须知道。
面试和招聘的硬通货。 这几年AI infra岗位的面试越来越偏底层,能讲清楚一个CUDA kernel的launch config怎么调、shared memory怎么用,比刷十道LeetCode都有用。一个能跑通的C/CUDA GPT-2项目,是简历上最有说服力的那种证据。
为下一代硬件做准备。 2026年新出的加速卡越来越多,不只是NVIDIA一家。但凡你想往这些新硬件上移植训练栈,懂底层算子写法就是入场券。
几个值得抠的实现细节
读这种项目,光知道"它实现了GPT-2"是没用的,得看具体怎么处理那些坑。
Attention里的causal mask。 朴素做法是构造一个上三角矩阵加到attention scores上再softmax,但这浪费一半计算。NanoEuler的处理跟llm.c一致:在softmax kernel里直接跳过上三角部分,既省算力也省显存。
LayerNorm的backward。 这是最容易写错的地方之一。dL/dx里包含三项:直接项、通过mean的项、通过variance的项。后两项都是全局reduction,必须用warp shuffle先做warp内规约再做block内规约,否则性能直接掉一个数量级。
AdamW的实现。 Adam的m、v状态是和参数等大的,对一个124M的模型就是接近1.5GB(FP32),加上参数本身和梯度,单卡显存压力立刻上来。NanoEuler目前是直接全FP32,没有做mixed precision的optimizer state,这是后续值得优化的点。
数据pipeline。 项目假设你已经有预处理好的token二进制文件,每个token用uint16存储。这个简化是合理的——tokenization放在训练循环里会成为瓶颈,离线做好再喂进来是工业界的标配。
这个项目适合谁
说几句直白的:
- 如果你是LLM应用开发者,每天调API、写prompt,这个项目对你帮助不大,看一眼当扩展视野就行
- 如果你是想转AI infra的工程师,认真读完这份代码,比刷十篇论文都管用
- 如果你在做推理加速、自研框架、新硬件适配,这种从零实现的代码是绝佳的参考
- 如果你在带AI方向的学生或新人,这个项目可以作为llm.c之外的另一个教学选择,门槛更低
顺带提一句
训练GPT-2这种事情门槛不高,单卡A100几小时就能跑完,但很多人卡在"想理解但没机会从头读一遍"。NanoEuler的价值就在于把这个机会摆到了所有人面前。
至于推理,如果你只是想拿GPT-2级别(或者更强的)模型跑业务,没必要自己训。OpenAI Hub已经聚合了GPT、Claude、Gemini、DeepSeek等主流模型,一个Key全调通,国内直连,OpenAI格式兼容——把精力留给上层产品,底层的事让懂底层的人去卷就好。
参考来源
- NanoEuler GitHub仓库 - 项目源码和README
- Reddit讨论:用C语言编写GPT-2 - 社区对从零实现GPT-2的相关讨论
- 知乎:逐行拆解nanoGPT与llm.c对比 - 推理工程师视角的代码拆解,包含和llm.c的对比



