Skip to content

閱讀心得

從 Python 到 C:一個 LLM 的逆向旅程

閱讀 llama2.c 最震撼的感受是:一個現代 LLM 的核心推理,竟然可以用不到一千行的 C 程式碼完整實作。這顛覆了我對「大型語言模型必須依賴龐大框架」的認知。

對 Karpathy 風格的觀察

Karpathy 的程式碼有一種 signature style:極簡但不簡陋

  • run.c 沒有使用任何第三方庫,連 #include 都只有標準 C 標頭檔
  • 註解恰到好處,解釋「為什麼」而非「是什麼」
  • 變數命名直觀(wq = weights for queries, xb = residual branch buffer)
  • 錯誤處理簡單直接(失敗就 exit(),不搞複雜的錯誤回傳)

對 C 語言實作 LLM 的反思

記憶體管理

C 語言要求開發者手動管理每個 byte。run.c 使用 calloc 分配緩衝區,使用 mmap 映射權重檔。這種底層控制讓權重佈局一目了然——你可以確切知道每個權重陣列在記憶體中的位置和大小。

效能

沒有 Python 的 interpreter overhead、沒有 PyTorch 的 tensor dispatch 開銷,純粹的 for 迴圈矩陣乘法。加上 OpenMP 平行化後,即使是 CPU 也能達到可用的推理速度。

可讀性

原始碼本身就是文件。forward() 函數的結構完全對應 Transformer 的數學定義,閱讀程式碼的同時就在學習架構。

學到的重要概念

  1. Weight Tying 的實務意義model.pytok_embeddings.weight = output.weight 這一行,在 C 中對應 wcls = shared_weights ? token_embedding_table : ptr——真正理解了「共享權重」在記憶體中的呈現方式。

  2. GQA 的實作巧妙:在 C 中,GQA 透過 kv_dim = (dim * n_kv_heads) / n_headskv_mul = n_heads / n_kv_heads 來實作,比 PyTorch 版的 repeat_kv() 更加直覺。

  3. RoPE 的 On-the-fly 計算:C 版不像 PyTorch 版預先計算 cos/sin 表格,而是在 forward 時即時計算。這是空間 vs 時間的權衡。

改進空間

  • CUDA 支援仍在 todo list 中(run.cu
  • 13B+ 模型的指標運算溢位問題尚未完全解決
  • Chat 模式的實作被標註為「proof of concept」,不夠穩健