Skip to content

llama.cpp 对比基准方法论

本页面定义 tiny-llm 与 llama.cpp 在同一硬件/模型上对比的可复现方法。 原则:所有数字必须能用本文档中的命令原样复现;任何数字必须同时记录 硬件、commit、编译选项、命令(与 DEVELOPMENT_PLAN.md 风险区要求一致)。

1. 环境记录模板

每次跑对比前,把以下信息填进结果表(docs/performance/results/ 下归档):

硬件

值(示例)
GPUNVIDIA GeForce RTX 3060 Laptop GPU
显存6144 MiB
驱动591.44
CUDA Toolkit12.0(nvcc --version 确认)
CPU / 内存(可选记录)

软件

值(示例)
tiny-llm commitgit rev-parse HEAD
tiny-llm 编译Release + -DBUILD_TESTS=ON(默认 nvcc 目标架构 native
llama.cpp commitgit -C /path/to/llama.cpp rev-parse HEAD
llama.cpp 编译cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j(默认 GPU 后端)
对比日期YYYY-MM-DD

2. 模型与量化差异(必须声明)

  • 模型:Qwen2.5-0.5B-Instruct,GGUF Q4_K_M
  • llama.cpp 直接在 Q4_K_M 上计算(GGML 内核原生支持 K 系列量化)。
  • tiny-llm 的路径是:反量化 → 转置 → 重新量化为 W8A16(INT8 权重 + FP16 scale,group_size=128),加载后以 W8A16 权重推理。

因此两者并非同一种量化格式,延迟对比反映的是“原生 Q4_K_M 计算” vs “W8A16 重量化后计算”两个完整路径的差距,不能解读为纯 GEMM 实现差距。

3. 对比边界(先区分两类实验)

  1. 合成 decode 吞吐llama-bench -p/-n 使用指定数量的合成 token,不接收 文本 prompt。它适合对比 tg64 等吞吐,不是同 prompt 端到端测试。
  2. 同 prompt 行为/墙钟:使用 llama-cli -p "你好" --temp 0 与 tiny-llm do_sample=false-t 1 在 llama.cpp 中表示 CPU 线程数,不控制采样;llama.cpp 当前默认 temperature 为 0.8,不能省略 --temp 0 后声称 greedy。
  3. llama.cpp 使用 -ngl 99(全层 GPU),确保 不会因 CPU 卸载把数字拖低。
  4. 预热与迭代次数保持一致:
    • tiny-llm:--warmup 3 --iters 10
    • llama.cpp:llama-bench-r(repeat)与 -n 对齐,或用 llama-cli -n 连续多次取稳定值。
  5. 同一块 GPU、同一时段交错运行 A/B,记录温度、功耗与是否锁频。
  6. tiny-llm 内置字段是“加载前 vs 运行后的常驻显存差值”,不是峰值。只有双方都用 同一个外部采样器、同一采样频率时,才比较峰值显存。

4. 可复制命令

tiny-llm

bash
# 在 tiny-llm 仓库根目录
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build -j$(nproc)

./build/tiny_llm_bench /path/to/qwen2.5-0.5b-instruct-q4_k_m.gguf \
    --prompt "你好" --max-tokens 64 --warmup 3 --iters 10 --json --graphs
# 配对运行把 --graphs 改为 --no-graphs;JSON 会记录实际 enabled/captured 状态。
# 正式 A/B 至少做 5 个独立进程对,并交错 on/off 先后顺序。

llama.cpp(合成 decode 吞吐)

bash
# 在 llama.cpp 仓库 build 目录
./build/bin/llama-bench \
    -m /path/to/qwen2.5-0.5b-instruct-q4_k_m.gguf \
    -ngl 99 \
    -p 1 \
    -n 64 \
    -t 1 \
    -r 3

llama-bench 输出 prompt processing (pp*) 与 token generation (tg*);tg64 可换算 decode TPOT。pp1 只是一个合成 prompt token 的处理时间,不包含用户可观察的 排队、首 token 采样与输出,不能标成 TTFT。-t 1 只固定 CPU 线程数。

llama.cpp(llama-cli,端到端兜底)

bash
./build/bin/llama-cli \
    -m /path/to/qwen2.5-0.5b-instruct-q4_k_m.gguf \
    -ngl 99 -t 1 -n 64 \
    -p "你好" --temp 0 --seed 1 --no-display-prompt

注意:llama-cli 的输出带装饰信息,建议用 llama-bench 取数字, 用 llama-cli 做同 prompt greedy 行为抽查。由于 Q4_K_M 与 W8A16 量化不同, 输出可能在 argmax 边界分叉;只能按实测报告完整一致、公共前缀或 EOS,不能预设逐 token 一致。 参数语义以 llama.cpp CLI 官方说明 为准。

5. 结果表模板

归档位置:docs/performance/results/<date>-<gpu>.md(模板见 docs/performance/results/TEMPLATE.md)。实测快照(2026-08-18,RTX 3060 Laptop,详见 2026-08-18-rtx30602026-08-18-decode-optimization

指标tiny-llmllama.cpp比值 (tiny/llama)
TTFT (ms)10.6(1-token prompt,含首次 logits)未按同口径测量;pp1=4.9ms 不是 TTFT不可比
TPOT (ms/token)6.13.7(tg64: 272 t/s)1.65
decode tok/s164.3272.20.60
常驻显存差值 (MB)3368(加载前 vs 运行后,含 M==1 转置权重副本)未测(同口径)

每张表下方必须附:第 1、2 节的环境快照 + 实际执行的完整命令 + 原始日志路径。

C1 前(转置快路径落地前)tiny-llm TPOT ≈ 24.3 ms / 41 tok/s(比值 ~6.6); C1/C2 后 TPOT ≈ 6.1 ms / 164 tok/s(比值 ~1.65)。kernel 级证据见 2026-08-18-decode-optimization 第 4.2 节。

6. 预期差距来源分析(先写假设,实测后回填)

对 0.5B 模型 + decode 场景,预期差距主要来自(按影响排序的假设):

  1. GEMM 实现差距:llama.cpp 的 Q4_K_M 内核针对 K 量化格式做了 SIMD 向量化与分块;tiny-llm 的 W8A16 m1 kernel 是每 warp 一列、32 lane 归约 K 的简单实现(C1 起对 M==1 使用 [N,K] 转置布局 + coalesced 快路径, lm_head 由 ~10ms 降到 ~0.98ms)。
  2. attention 实现差距:tiny-llm 的 decode attention 未做 KV 缓存 L2/共享内存复用与页式布局优化。
  3. 运行时/launch 开销:24 层 × 每层多个 kernel;CUDA Graphs 已默认开启 消解 launch 串行(见 cuda-graphs.md)。
  4. continuous batching 缺失:tiny-llm 单序列;llama.cpp 即使单序列 也有更紧凑的调度。

实测后:把每一行差距归因到具体 kernel(用仓库内 tiny_llm_kernel_bench 数据,见第 8 节),而不是笼统写“实现差距”。

7. 不在对比范围内的事项

  • 多请求并发 / batch > 1 吞吐(tiny-llm 无 continuous batching)。
  • 采样配置差异(temperature/top-p)——对比固定 greedy。
  • 非本机、非同一时段的数据。

8. 本机 profiler 限制与替代方案

2026-08-23 在本机 WSL2 / 驱动 610.88 复核:nsys/ncu 命令存在,但完整分析仍受限:

  • ncu:采样返回 ERR_NVGPUCTRPERM,没有 kernel 指标可收集;
  • nsys profile:能生成 .qdstrm,但本机安装缺 importer,不能转换为报告并执行 stats。

因此 kernel 级瓶颈分析改用仓库内微基准 tiny_llm_kernel_bench

bash
cmake --build build -j$(nproc)
./build/tiny_llm_kernel_bench
# 输出 CSV:<name>,<shape>,<ms>
# 每项先 warmup 20 次,再测 200 次(lm_head 100 次),
# 循环前后各一次 cudaDeviceSynchronize,std::chrono::steady_clock 均值。

测量对象为 decode 路径真实 shape(Qwen2.5-0.5B):W8A16 GEMM(M=1, K=896, N∈{128,896,4864} 与 down M=1,K=4864,N=896)、FP16 lm_head(M=1,K=896, N=151936)、attention_decode(S∈{8,32,64,128})、rmsnorm、RoPE、add、 silu_mul。C1 起该工具测量的是 M==1 转置快路径(与推理引擎 decode 一致的 公开接口)。

基于 MIT 许可证发布