首页
/ llmfit 社区基准全解:一条 `llmfit bench --all --share` 把你的硬件实测数据贡献回开源仓库

llmfit 社区基准全解:一条 `llmfit bench --all --share` 把你的硬件实测数据贡献回开源仓库

2026-09-08 17:11:50作者:明树来

导读:llmfit 的核心能力是"告诉我你的硬件,我告诉你什么模型跑得动"——而这背后的底气,来自一个不断壮大的社区基准数据库。本文围绕仓库中的 社区基准说明文档,完整讲解 llmfit 社区基准数据的产生、提交、校验与回流闭环:你会掌握 llmfit bench --share 的完整使用姿势、理解 PR 自动提交的免 gh 授权机制、读懂每一条 JSON 提交的 schema 契约,以及看懂一次 merge 之后你的实测数据是如何"嵌入二进制、进入每一个新安装"的。

一、community/ 目录是什么:一台台真实机器攒出来的测量数据库

在 llmfit 仓库中,llmfit-core/data/community/ 是一个只存放"基准测试结果"的目录:每个子目录以硬件命名(如 nvidia-geforce-rtx-7900-xtapple-m5-proamd-radeon-rx-6800),目录里是若干条 JSON 提交,每条对应一次真实的基准跑分。

这些数据不是维护者手工录入的,而是由全球 llmfit 用户通过同一条命令贡献:

llmfit bench --all --share

--all 表示对所有模型做一次完整基准扫描,--share 表示把这次扫描的结果贡献回上游仓库。用一句话概括这条命令的行为链:跑基准 → 组装成标准的 JSON payload → 让你确认 → 自动 fork 仓库并打开一个 Pull Request,把文件添加进 community/ 目录——整个过程完全不需要安装 gh CLI,也不需要任何自建服务器

核心机制在于认证方式:共享流程使用 GitHub OAuth device flow(也就是 gh auth login 内部采用的同一套机制),llmfit 二进制的客户端 ID 是公开的、非机密,因此可以直接打进二进制里。若环境变量中存在 GITHUB_TOKEN / GH_TOKEN,llmfit 会自动优先使用它,从而在 CI 等无交互环境中也能直接跑通共享流程。

共享不等于丢数据:先本地暂存,随时可补交

README 特别强调一个设计原则:"拒绝共享绝不会丢弃数据"。不带 --share 的基准运行同样会被完整记录到用户机器的本地存储中;之后某次执行 llmfit bench --share(即使此刻已没有新模型可测)也会把本地积压的旧结果一次性打包进一个 PR 提交上去。

从源码看,这个机制落地在 share.rs

  • 每次成功的基准运行先经 store_local 序列化成"待上传 payload"写入本地 pending 存储,文件名与最终上传到仓库的文件名完全一致(时间戳 + 内容哈希);
  • 共享成功后,mark_shared 再把文件从 pending/ 移动到 shared/——文件保留作为本地历史,但永远不会被发送第二次;
  • 本地存储根目录由 dirs 库的 data_local_dir() 决定(Linux 上通常即 ~/.local/share/llmfit/benchmarks),并可通过环境变量 LLMFIT_BENCH_STORE 覆盖,便于测试或将存储放到共享卷上。

需要说明适用前提:这套共享闭环以 GitHub 为上游托管方(上游仓库、main 分支等常量在 share.rs 中定义),因此文档描述的 PR 流程均针对当前上游仓库的 GitHub 托管环境。

二、先预览再提交:--dry-run 让你不碰 GitHub 也能看到完整 payload

在真正联系 GitHub 之前,你完全可以用 dry-run 模式预览将要提交的内容:

llmfit bench --all --share --dry-run

dry-run 模式的行为在 share.rs 的 share_all_pending 中实现:

  1. 列出本地 pending 存储中全部待贡献结果(一行一条,格式为 model via provider — N tok/s);
  2. 逐条打印将要上传的完整 JSON payload(对应源码中的 --- <本地文件路径> --- 分隔块);
  3. 明确提示 --dry-run: nothing was submitted. 后退出,全程不发任何网络请求。

CLI 层把 --dry-run 解析为 ShareOptions.dry_run(对应结构体定义见 share.rs,命令行组装见 llmfit-tui/src/main.rs);另有 assume_yes 字段用于跳过交互式确认。值得注意的是,dry-run 与正常的 --share 相比还省去了预检:真实共享时程序会在基准开跑之前先做凭证预检(preflight_auth),把"token 缺失/过期"这类问题提前暴露,而不是等跑完几分钟基准才报错。

--share 的提示、进度等所有人可读的输出统一走 stderr,绝不会污染 bench --json 写到 stdout 的结构化结果。

三、目录布局:为什么文件能被安全地并发提交

community/ 的布局是一套约定,而非随意堆放:

community/
  <hardware-slug>/
    <unix-timestamp>-<hash>.json

其中:

  • <hardware-slug>:硬件标识的规范化 slug。payload 中 GPU 机取 GPU/加速器名、纯 CPU 机取 CPU 名,转为小写并只保留字母数字、其余字符替换为 -(实现见 share.rs 的 payload_slug)。例如真实目录 amd-radeon-rx-7900-xtapple-m5-prointel-arc-graphics-130v-140v-integrated。校验器要求 slug 匹配 ^[a-z0-9]+(-[a-z0-9]+)*$
  • <unix-timestamp>-<hash>.json:提交时间戳 + 8 位十六进制内容哈希。哈希基于 FNV-1a 64 位算法混入墙钟时间生成(short_hash),即使内容相同的重复运行也会得到不同文件名,同时保持稳定可复现。

这套命名有两个直接收益:

  1. 并发提交永不冲突:文件按硬件命名空间 + 内容哈希隔离,不同用户、不同批次的结果天然不重叠;
  2. 幂等性:仓库内文件名与贡献者本地存储条目一一对应。当贡献者已有打开的 benchmark PR 时,新结果会被追加进同一个 PR 而不是再开一个;部分失败后重试时,已经落地(landed)的文件会被跳过而非重复提交。

共享失败与恢复路径在 submit_stored 中有清晰实现:先检查该用户是否已有打开的 bench PR,有则追加;否则以 bench/<slug>-<hash> 为分支名新建分支并开新 PR。若所有文件都已在远端(例如此前 PR 已合并但本地 mark_shared 失败),则返回"无需开 PR"的结果,避免 GitHub 拒绝空 diff。上传后的文件若硬件身份不可识别(如 ROCm/libdrm 探测不到 GPU 名而显示为 N/A)会被过滤并保留在本地——原因正如代码注释所言:社区排行榜按硬件身份分组,占位符身份只会制造无意义的桶。

四、合并之后会发生什么:一次提交 = 下个版本里所有人的"默认校准"

这是整个社区基准闭环的"回报"设计。README 明确指出:

Submissions are aggregated by llmfit-core/build.rs and embedded into the binary, so every merged submission ships in the next release.

构建期聚合逻辑见 llmfit-core/build.rs:它递归扫描 data/community/ 下所有 <slug>/*.json,按确定性顺序排序后聚合成单一 JSON 数组,写入 OUT_DIR/community_benchmarks.json,并通过 include_str! 嵌入二进制(对应 benchmarks.rs 中被 build.rs 聚合的注释与 cargo:rerun-if-changed=data/community 的重建声明)。一个坏文件只会产生 cargo:warning 并跳过,绝不会破坏整个构建——因为 CI 在 PR 阶段就已经校验过了。

只要用户跑在相同硬件(同一 CPU + GPU)上,你的提交就会带来三类可见收益:

收益 说明
你的实测进入对方 benchmark 页面 数据来源标注为 llmfit community
measured ✓ tok/s 出现在对方 fit 表中 出现在用户自己的本地实测之下localmaxxing 中位数与公式估算之上——你测过的那些模型将优先显示真实测速而非估算
其余每个模型获得校准后的估算 以你的实测为锚点修正估算曲线;一台全新安装的机器在用户还没跑过任何基准之前,就已经因为你的提交而受益

数据消费侧的排序逻辑,可以在 benchmarks.rs 中看到对应类型:MeasuredSource 区分本地实测、嵌入式社区提交等来源;community_submissions() 读取构建期嵌入的社区提交;community_results_for_specs(specs) 会先按硬件(CPU/GPU)过滤出与当前机器匹配的提交(过滤逻辑同时被本地存储复用,见 StoredBenchmark::matches_hardware);LocalBenchIndex 则负责索引用户自己在本机跑过的实测,供 fit 表做本地优先命中。测试 benchmarks.rscommunity_embed_parses_and_is_seeded 断言嵌入聚合非空、community_results_filter_by_hardware 断言结果确实按硬件匹配过滤。

五、合并前的守门人:每条 PR 都要过的校验关卡

README 明确:每一条触及该目录的 PR 都会运行 Community Benchmarks workflow,调用 scripts/validate_community_benchmarks.py。本地也可以直接运行同一脚本:

python3 scripts/validate_community_benchmarks.py            # 校验全部文件
python3 scripts/validate_community_benchmarks.py FILE...    # 只校验指定文件

脚本需要 jsonschemapip install jsonschema),退出码 0 表示全部通过,1 表示有问题。手写(hand-crafted)的提交同样欢迎,前提是能通过与自动生成提交相同的全部检查。具体检查分四层:

1. 路径与命名约定:文件必须直接位于 community/<hardware-slug>/ 之下;slug 必须是小写字母数字加连字符;文件名必须匹配 ^\d{9,12}-[0-9a-f]{8}(-\d+)?\.json$(其中 (-\d+)? 兼容早期构建产生的后缀,仓库中确有这样的文件,例如 intel-arc-graphics-130v-140v-integrated/1783676393-792a24b6-0.json)。

2. 体积上限:单文件不得超过 64 KB、单文件结果数不得超过 100 条——一条正常提交只有几 KB,体积异常大就意味着它不是基准数据。

3. schema 符合性:逐条用 schema.json(draft-07)校验,错误会精确到 JSON path 并逐条输出。

4. 跨字段合理性(schema 表达不了的部分):

  • minTps <= avgTps <= maxTps,且 avgTps > 0
  • 硬件物理边界:ramGb ≤ 8192vramGb ≤ 2048cpuCores ≤ 1024gpuCount ≤ 64(刻意留得很宽,只拦"4 TB 显卡 / 百万核 CPU"这类无意义数据,不拦真实存在的冷门硬件);
  • 提交时间合理性:submittedAtUnix 既不能早于共享功能上线的 2026-07-01(1782864000),也不能超过当前时间 + 24 小时容差。

六、提交格式:从顶层到字段级的完整契约

每个文件都符合 schema.json。README 给出的标准示例:

{
  "schemaVersion": 1,
  "submittedAtUnix": 1752127200,
  "tool": { "name": "llmfit", "version": "1.0.0" },
  "hardware": {
    "hwClass": "DISCRETE_GPU",
    "hardwareName": "NVIDIA GeForce RTX 4090",
    "memTierGb": 24,
    "vramGb": 24.0,
    "gpuCount": 1,
    "unifiedMemory": false,
    "cpu": "AMD Ryzen 9 7950X",
    "cpuCores": 32,
    "ramGb": 64.0,
    "os": "linux"
  },
  "results": [
    {
      "model": "llama3.1:8b",
      "provider": "ollama",
      "numRuns": 3,
      "avgTps": 128.4,
      "minTps": 121.0,
      "maxTps": 133.7,
      "avgTtftMs": 41.2,
      "avgTotalMs": 812.5,
      "avgOutputTokens": 104.0
    }
  ]
}

schema 设置 additionalProperties: false,意味着每个对象都不允许出现未列出的多余字段。逐层拆解如下。

6.1 顶层字段(五个全必填)

字段 类型 约束 说明
schemaVersion integer const: 1 契约版本,当前唯一合法值
submittedAtUnix integer ≥ 0 提交的 Unix 时间戳,由 CLI 在上传前重新盖章(见后文)
tool object 必填 nameversion 生成工具标识
hardware object 见 6.2 运行硬件描述
results array minItems: 1 本次扫描的模型结果列表

6.2 tool 对象

字段 类型 约束
name string 当前即 "llmfit"
version string 取自 CARGO_PKG_VERSION(构建时版本)

6.3 hardware 对象

必填字段为 hwClassgpuCountunifiedMemorycpucpuCoresramGbos;其余可空。

字段 类型 约束 语义
hwClass string 枚举 DISCRETE_GPU / UNIFIED / CPU_ONLY 硬件类别,由统一内存 / 是否有 GPU 推导(见 share.rs 的 build_submission
hardwareName string / null GPU/加速器型号名;GPU 机与 UNIFIED 机的分组键
memTierGb integer / null ≥ 0 声明式"内存层级":将实测容量就近取整到粗粒度阶梯(declared_mem_tier,当前阶梯为 2/3/4/6/8/12/16/24/32/48/64/80/96/128/192/256 GB),精确相等时向下取整——刻意低估比高估安全,因为它是对机器真实配置的声明;源码注释特别提醒它不要benchmarks.rs 中用途不同的 lookup_mem_tier 混淆
vramGb number / null ≥ 0 GPU 显存总量(统一内存机为空,改走 memTierGb
gpuCount integer ≥ 0,必填 GPU 数量
unifiedMemory boolean 必填 是否统一内存架构
cpu string 必填 CPU 型号名;CPU-only 机以此作为分组键与 slug
cpuCores integer ≥ 1,必填 CPU 总核数
ramGb number ≥ 0,必填 内存总量
os string 枚举 linux / macos / windows,必填 cfg!(target_os) 推导(os_name

所有数值在序列化前都会被 round2 保留两位小数(share.rs)。

6.4 results[] 对象

必填字段为 modelprovidernumRunsavgTpsminTpsmaxTpsavgTotalMsavgOutputTokens

字段 类型 约束 语义
model string minLength: 1 模型标识。llamacpp 场景下会做 GGUF 绝对路径剥离,只保留文件名(见下文隐私处理)
provider string 枚举 ollama / vllm / mlx / llamacpp 后端运行器
numRuns integer ≥ 1 取平均的重复运行次数
avgTps / minTps / maxTps number 0 ~ 100000 生成吞吐(tok/s)的平均 / 最小 / 最大;且跨字段必须满足 min ≤ avg ≤ max
avgTtftMs number / null ≥ 0 平均首 token 延迟(毫秒),部分后端不可测则为 null
avgTotalMs number ≥ 0,必填 平均总耗时(毫秒)
avgOutputTokens number ≥ 0,必填 平均输出 token 数

6.5 仓库里的真实样例

README 的示例是理想化的 ollama 模型标签。仓库里真实存在的提交则能让你看到 llama.cpp / GGUF 场景下的实际形态,例如 amd-radeon-rx-7900-xt/1785258762-ee642f2d.json:一台 i9-10900X + RX 7900 XT 的 Linux 机器,hwClassDISCRETE_GPU,一条提交内包含 40 个模型的实测(Llama-3.2-1B-Instruct-Q4_K_M.gguf 约 375.97 tok/s,逐级递减到 Qwen2.5-Coder-32B-Instruct-Q4_K_M.gguf 的约 31.13 tok/s),numRuns 均为 3、avgTtftMs 为 null——这就是 llamacpp 提交的真实剖面。全仓目前已有覆盖 AMD 独显/核显、Apple M 系列、Intel Arc/Xe、NVIDIA 各代显卡等大量硬件目录,每条文件都是一台真实机器的指纹。

6.6 隐私与数据卫生:路径会被先"清洗"

一个容易被忽略的细节:llamacpp 的 llama-server 在 /v1/models 里报告的模型 id 往往是 -m/--model绝对文件路径,其中可能带着用户名等机器特定信息。为此 share.rs 内置了两层防护:

  1. 新 payload 构建时通过 strip_gguf_path 规范化:只有以 /\ 或盘符开头的绝对路径且以 .gguf 结尾时,才剥离为裸文件名;而 hf.co/org/repo/file.gguf 这类带命名空间的 Hub 引用会原样保留(它们不含机器特定信息);
  2. 旧版二进制写入的本地存储仍可能残留路径,加载时由 sanitize_stored_payload 统一在共享前清洗,保证共享预览、dry-run 与实际上传三方一致,机器特定路径永远不出本机

七、常见运行场景与边界行为

结合 README 与源码,把几个高频场景的行为讲清楚,可以避免踩坑:

1. 本地还没有任何基准结果 share_all_pending 直接报错提示:先运行 llmfit bench <model>llmfit bench --all(错误文案见 share.rs)。

2. 硬件无法被识别 若 GPU 名是 N/A/unknown 之类的占位符,提交会被过滤并给出明确提示:结果保留在本地,不会污染社区排行榜。占位符判定集合与原因见 share.rs

3. token 失效 先查 GITHUB_TOKEN / GH_TOKEN,再查上次 device flow 登录缓存的 token(默认缓存在配置目录的 llmfit/github_token,权限 0600)。环境变量中的 token 无效属于硬错误(用户显式设置过,必须指出来);缓存的 token 失效则被静默丢弃并重新走交互式登录。若不希望任何交互登录发生(如受限 CI),可把 LLMFIT_GH_CLIENT_ID 显式设为空字符串来禁用 device flow(oauth_client_id)。

4. 多轮共享 上传成功后 mark_shared 会把 pending 清空;但已打开的 PR 是"可续写"的——下次共享会检测到已存在 open bench PR 并继续追加,而不是制造一堆碎片 PR。

八、小结:这条数据管线的设计哲学

回看整条链路,llmfit 的社区基准之所以能持续壮大,靠的是三组刻意设计的取舍:

  1. 贡献门槛降到最低:无 gh、无自建服务器,device flow 几分钟授权一次即可;共享失败绝不丢数据,本地暂存 + 幂等文件命名让"事后补交"与"失败重试"都变得廉价;
  2. 数据质量交给自动化守门:CI 校验 + schema 严格封闭(additionalProperties: false)+ 物理边界合理性检查,让"合并即可信"成立,也让手写提交与工具生成提交遵循同一套标准;
  3. 激励闭环内置在产品里:合并即嵌入二进制、随下个版本发布,硬件相同的用户立刻在自己的 benchmark 页面、fit 表和模型估算中受益——贡献者与使用者的利益在数据上合流,而排序优先级(本地实测 > 社区实测 > 中位数 > 估算)保证每个人看到的最准确数据永远来自最近的真实测量。

如果你也想让社区看到你那台机器的真实跑分,随时运行:

llmfit bench --all --share --dry-run   # 先看会提交什么
llmfit bench --all --share              # 确认无误后正式贡献

一次运行,就是给所有同款硬件用户的一份开箱校准。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391