跳至主要内容

生命週期與記憶體模型

本頁是 TinyLoop C++ 呼叫端的權威記憶體/所有權契約。

生命週期進入點

Model* load_model(const std::string& path, int max_seq_len = 2048, int prefill_chunk = 0);
void free_model(Model* model);
size_t vram_usage_bytes(Model* model);

內部配置階段(load_model)

load_model 不是輕量建構子。內部執行以下步驟:

  1. header 解析 + 格式驗證(MAGICVERSION_MAX、維度)
  2. 量化權重上傳(QuantWeight:打包 bytes + FP16 scales/zeros)
  3. 針對熱路徑 GEMM 的可選 FP16 cache(由 env 控制)
  4. BufferPool 配置:
    • main(FP32 residual,永遠是全序列寬)
    • normscratch1attn_down(FP16 暫存;全序列寬或 chunk 寬)
    • 延遲配置的 logit_row
  5. h-mode 路徑的可選重建 scratch

任何配置失敗都會使模型建立致命錯誤。呼叫端收到 nullptr,不得繼續使用該模型指標。

模型擁有的記憶體

Model 擁有長期存活的 GPU 記憶體:

  • 量化權重 + metadata
  • 輸出/head 張量
  • BufferPool 可重用活化
  • 可選的 FP16 權重 cache

Model 永久擁有每個請求的 decode cache。那些由 generation/prefix/resume API 建立。

每請求 / handle 擁有的記憶體

RuntimeKVCache

由 generation 系列呼叫與 prefix/warm-start builder 配置。層儲存模式依 env/runtime flag 而定:

  • FP16 K/V
  • INT8 K/V
  • store-h FP16
  • store-h INT8
  • store-h INT4

PrefixCache / ResumeHandle / PrefixPool

跨呼叫擁有 cache 狀態的不透明 handle。

  • PrefixCache:固定 loop 深度下預填充的 prefix 狀態。
  • ResumeHandle:residual 快照 + 過量配置的 cache,用於深度升級。
  • PrefixPool:多個 prefix cache + LRU 行為。

可變狀態與 thread-safety 含義

Model 有可變工作 buffer(buffers.mainbuffers.norm、scratch buffer)。每一次 forward 都會修改它們。

後果:

  • 在同一 Model* 上並行呼叫 forward 是不安全的,除非外部序列化。
  • 綁定到單一模型的 handle(尤其是 ResumeHandle)假設沒有外來 forward 污染其預期的 residual/cache 演化。
  • 較安全的並行模式:
    • 每個模型一個鎖
    • 每個 worker 一個模型(較高 VRAM)
    • 每個 replica 一個 process

prefill_chunk 與 scratch 駐留

prefill_chunk > 0 改變記憶體行為:

  • main:仍然是 max_seq_len × D × sizeof(float)
  • scratch buffer(normscratch1attn_down):依 chunk 列數配置,而非全序列列數

取捨:

  • 長上下文下 VRAM 佔用較低
  • 因 chunked 執行,prefill 期間的啟動/driver overhead 較高
  • score 類 API 受 chunk 列預算限制

為什麼這樣設計記憶體

TinyLoop 優先考量:

  • 單次模型載入後的穩定高吞吐
  • 避免熱路徑重複配置的波動
  • 透過 runtime mode flag 明確控制 VRAM-vs-延遲取捨

相較於完全無狀態 API,這產生更高的持續效能,但要求呼叫端對所有權與並行有更強的紀律。

實務整合檢查清單

在 production C++ 整合前:

  1. 選定一種並行模型(鎖 vs 每 worker 一模型)。
  2. 由 VRAM 預算決定 max sequence 預算 + chunk 策略。
  3. 選定 cache 模式(FP16/INT8/store-h 變體)並記錄品質/延遲政策。
  4. 把所有不透明 handle 當作持有 VRAM 的物件;確定性釋放。
  5. load_model 失敗與 OOM 路徑周圍接入健康探針。