效能與記憶體
TinyLoop 有多種 runtime 模式,不同模式的數字不能互相直接套用。本頁目的就是讓 benchmark 主張保持嚴謹。
Benchmark 形狀很重要
目前公開 benchmark 數字指的是:
seq_len=128loops=8- 不物化 logits
- H100 驗證環境
不要把這些數字泛化到:
- 任意序列長度
- tokenizer-backed 服務工作負載
- batched decode 吞吐
目前 benchmark 狀態
| 路徑 | 結果 | 解讀 |
|---|---|---|
| 預設低位元 benchmark | 30.48 ms | 主低位元 runtime 路徑 |
| FP16-body benchmark | 2.82 ms | 更快,但 VRAM 顯著更高 |
| 完整 prefill attention | 0.143 ms -> 0.087 ms | 較安全 tiled prefill kernel 對比參考路徑 |
FP16 body cache — 407M INT4 上 prefill 快 30-65×
2026-04-17 於 H100 / 407M INT4 / L=8 實測(開啟
TINYLOOP_EXPERIMENTAL_FP16_BODY=1):
| phase | INT4 scalar(預設) | FP16 body + cuBLAS TC | speedup |
|---|---|---|---|
| prefill seq=128 | 183 ms | 2.8 ms | 65× |
| prefill seq=256 | 357 ms | 5.2 ms | 69× |
| prefill seq=512 | 712 ms | 13.8 ms | 51× |
| prefill seq=1024 | 1431 ms | 47.5 ms | 30× |
| 128-token decode | 1241 ms | 514 ms | 2.4× |
權重 VRAM 放大約 2.9×(407M:216 → 619 MB)。輸出在幾個 token 後與 INT4 scalar 路徑分歧,是 FP32 vs FP16 tensor-core 累加器 rounding 差異, 不是正確性 bug。
KV cache INT8 — 同 VRAM 下 2× context
2026-04-17 於 H100 / 407M INT4 / L=8 實測(開啟 TINYLOOP_KV_INT8=1)。
per-token per-head 對稱 INT8 量化,每 (token, head) 一個 FP16 scale:
| 指標 | FP16 KV | INT8 KV | 差異 |
|---|---|---|---|
| KV 記憶體(max_seq=2048, 10 layers) | 167.8 MB | 85.2 MB | −49.2% |
| 同 VRAM 下有效 context | 2048 tok | 4032 tok | +97% |
| decode 時間 seq=256 | 2652 ms | 2748 ms | +3.6% |
| decode 時間 seq=1024 | 15262 ms | 16653 ms | +9.1% |
| decode 時間 seq=2048 | 42668 ms | 48462 ms | +13.6% |
Greedy 輸出前 ~40 tokens 完全相同,之後會隨著 rounding 累積而分歧 —
兩邊都是量化模型的合法取樣。可與 TINYLOOP_EXPERIMENTAL_FP16_BODY=1
疊加。剩餘的 decode regression 來自 per-element int8→float 轉換,要
再進一步需要 Hopper 上的 INT8 tensor cores(目前未排期)。
Cross-iteration stochastic rounding — 在 L ≥ 16 贏過 naïve INT4
Looped transformer 每次 forward 都把同一個 weight 用 L 次,這是唯一 可以做 per-iteration stochastic rounding 抵銷 quantization bias 的 regime。§16.9 出三個操作點:
| 變種 | 儲存成本 | slope | 407M PPL vs naïve INT4 |
|---|---|---|---|
| naïve INT4(預設) | 4 bits/weight | +1.00 | ± 0(baseline) |
| INT4 + 1-bit sign-of-frac | +25% | +0.80 | +2.18 at L=16(gap 縮小 46%) |
| INT4 + 4-bit signed frac | loop block +100%(整模型約 +15%) | +0.50 | −0.32 at L=16, −0.67 at L=32 |
1-bit 便宜版只縮 gap 但沒贏 naïve。4-bit frac 版在 L=16 跟 L=32 實測 贏 naïve INT4,符合 paper §O.2 Bernoulli oracle simulation 的方向。
Runtime 開啟:先用 tools/tinyloop_add_sign_bit.py 升級 .tinyloop
檔,然後 TINYLOOP_CROSS_ITER_ROUND=1:
python3 tools/tinyloop_add_sign_bit.py \
model_int4.tinyloop model_int4_v5.tinyloop \
--from-pt model.pt --loop-only --frac-bits 4
--loop-only 只在 loop block 加 sign-bit / frac channel(pre-blocks、
embed、head 保持 plain INT4 — 每 iteration 加 noise 對這些只跑一次
的權重反而傷質)。
Prefix cache — 10-user 同 prefix 吞吐量 3.03×
2026-04-17 於 H100 / 407M INT4 / L=8 透過 tests/bench_prefix_cache.py
實測:
| 後端 | prefix | N | NO_CACHE | SHARED | speedup |
|---|---|---|---|---|---|
| INT4 scalar(預設) | 500 tok | 10 | 12 847 ms | 5 415 ms | 2.37× |
| INT4 scalar(預設) | 1024 tok | 10 | 23 852 ms | 8 329 ms | 2.86× |
| INT4 scalar(預設) | 1024 tok | 50 | 104 605 ms | 34 531 ms | 3.03× |
| FP16 body + TC | 1024 tok | 10 | 5 123 ms | 4 732 ms | 1.08× |
論文主張(「prefix cache 得以乘上 L」)的前提是 prefill 為 compute-bound。 INT4-scalar body 下看到完整 2.4-3.0× 增益;tensor-core 路徑下 prefill 本身已經夠快,prefix 共享只貢獻 5-8%。
主要 runtime 開關
TINYLOOP_EXPERIMENTAL_FP16_BODY=1
作用:
- 在載入時將 transformer-body 權重反量化為常駐 FP16 cache
- body GEMM 走 FP16/cuBLAS
取捨:
- 更快
- 需要更多 VRAM
TINYLOOP_DISABLE_FLASH2_PREFILL=1
作用:
- 停用較新的、較安全 tiled prefill attention 路徑
- 強制回到參考 prefill kernel
適用於:
- 與舊路徑做 parity 比較
- 隔離 prefill attention 變更影響
TINYLOOP_DISABLE_KV_CACHE=1
作用:
- 關閉 cached generation
- 強制 uncached 參考生成路徑
適用於:
- 執行 parity 檢查
- 偵錯 decode 行為
TINYLOOP_CUDA_PROFILE=1
作用:
- 在
benchmark期間輸出分段時間
適用於:
- 找出主導耗時階段
- 判斷加速來源是 prefill、MLP、attention 還是 logits
記憶體組成
TinyLoop runtime 記憶體占用大致為:
- 權重
- 常駐 buffer
- 可選 cache(如 FP16 body cache)
- 隨序列增長的 KV cache
目前重要記憶體控制桿:
max_seq_lencache_window- 預設低位元路徑 vs FP16 body cache 模式
Sliding-Window Cache
cache_window 控制 decode 歷史在 KV cache 中保留多少。
語義:
0:完整快取>0:只保留最後W個快取 token
適用於:
- 不需要長上下文保留
- 有界 KV 記憶體比完整歷史更重要
Prefix Cache 重用
Prefix cache 是針對重複共享 prompt 的記憶體與延遲策略。
有幫助的情境:
- 多請求共享共同前綴
- 想攤提前綴 prefill 成本
它不能取代 batching 或 paged attention。
如何負責任地發布數字
發布 TinyLoop 數字時請附上:
- GPU 型號
- loop 次數
- 序列長度
- 是否物化 logits
- 是否啟用 KV cache
- 是否啟用
TINYLOOP_EXPERIMENTAL_FP16_BODY=1 - 是否啟用較新的 tiled prefill attention 路徑
沒有這些上下文,數字很容易被誤讀。