Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NixOS Framework Benchmarks

一个可复现的 NixOS 配置框架性能基准项目,用相同的合成工作负载对比:

  • Snowveil
  • Snowfall Lib
  • flake-parts
  • nixos-unified
  • Den
  • plain flake:无框架基线

项目依据各框架的官方组织方式设计:Snowveil、Snowfall 使用目录约定和自动发现;flake-parts 与 nixos-unified 使用 flake module 装配;Den 使用 aspect;plain flake 直接调用 nixpkgs.lib.nixosSystem。各案例使用自己的真实框架入口,不用模拟实现替代框架。

当前锁文件生成于 2026-09-01。精确 revision、narHash 和传递输入以各案例的 flake.lock 为准。

规模矩阵

规模 主机数 合成 NixOS 模块数 用途
tiny 1 8 烟雾测试与快速回归
small 4 32 个人多主机配置
medium 16 128 团队或同构服务器集群
large 64 512 大型配置仓库与压力测试

每个合成模块向 benchmark.moduleIds 追加唯一整数。所有框架中的每台主机都必须得到相同的:

  • moduleCount
  • checksum
  • hostId
  • networking.hostName
  • system.build.toplevel.drvPath

smoke_test.py 会检查这些不变量,避免“更快”只是因为某个框架漏装模块。

项目结构

.
├── benchmark-matrix.json       # 规模定义
├── cases/
│   ├── tiny/{plain,snowveil,flake-parts,nixos-unified,snowfall,den}/
│   ├── small/...
│   ├── medium/...
│   └── large/...
├── docs/methodology.md         # 公平性、指标和限制
├── scripts/
│   ├── generate_cases.py       # 确定性生成全部案例
│   ├── smoke_test.py           # 求值正确性检查
│   ├── run_benchmarks.py       # 采样器
│   └── render_report.py        # JSON/CSV/Markdown 汇总
├── flake.nix                   # apps、开发环境和 formatter
└── flake.lock

框架案例映射

框架 案例组织方式 模块如何进入主机
plain modules/features/*.nix + 显式列表 nixosSystem.modules
Snowveil modules/*/nixos.nixhosts/*/{meta,default}.nix Snowveil 自动发现并注入
flake-parts flake module 中声明 flake.nixosConfigurations flake-parts 装配 outputs,显式模块列表
nixos-unified flake-parts module 中调用 config.flake.nixos-unified.lib.mkLinuxSystem 封装 nixosSystem,显式模块列表
Snowfall modules/nixos/*/default.nixsystems/<system>/*/default.nix Snowfall 自动发现并注入
Den 每个 feature/host 一个 aspect module den.default.includes + host aspect

这不是“只测模块系统”的微基准,而是测量用户采用各框架惯用组织方式时的端到端求值成本,因此目录扫描、框架抽象和 output 装配都会计入。

为避免 flake-show 被框架附带但与本基准无关的 checks/packages 干扰,各案例统一只暴露共享的 nixosConfigurationsbenchmarks 探针;Snowfall 因其生成配置对 self.pkgs 的内部引用额外保留 pkgs output。

基准结果

以下快照来自一次 --quick 运行(tiny + smalleval-first/eval-all/drv-first 三个核心任务,cold + warm 两种模式,每场景预热 1 次、正式采样 2 次,seed 20260902),完整 72 行数据见 results/20260902T152044Z/report.mdsummary.csv。求值耗时(中位数,单位 s):

框架 tiny eval-all cold small eval-all cold small drv-first cold small eval-all 相对 plain
plain(基线) 1.657 2.924 8.441 1.00×
Snowveil 1.804 2.589 7.628 0.89×
flake-parts 1.691 3.049 8.280 1.04×
nixos-unified 1.757 3.100 7.828 1.06×
Snowfall 2.196 3.584 7.932 1.23×
Den 1.959 3.275 8.145 1.12×

要点:

  • eval-* 任务里框架差异最明显:Snowfall 最慢(约 +10%–38%),Den 次之(约 +12%–30%);Snowveil、flake-parts、nixos-unified 与 plain 接近,其中 Snowveil 在 small/eval-all 甚至快于 plain(约 0.89×),且其 RSS 明显更低(small/eval-all 约 354 MiB,其余约 436–472 MiB)。
  • drv-* 任务中所有框架都收敛到 plain 附近(约 0.90×–1.11×),因为 nixpkgs/NixOS derivation 实例化开销占主导,淹没了框架抽象与目录发现的差异。
  • 该结果仅对应上述受限规模与短采样,正式结论应固定 CPU governor、停止后台负载并增加 runs/seed 后重跑。

快速开始

cd nixos-framework-benchmarks

# 进入包含 nix、GNU time、jq、nixfmt、statix、deadnix 的环境
nix develop

# 验证六种框架的 tiny 案例
nix run .#smoke

# 快速基准:tiny + small,核心任务,cold + warm,各 2 次
nix run .#benchmark -- --quick

# 假设输出目录为 results/20260901T120000Z
nix run .#report -- results/20260901T120000Z

完整基准默认会运行 4 个规模、6 种实现、6 个任务、2 种模式,每个场景 1 次预热和 5 次正式采样:

nix run .#benchmark

默认场景顺序使用固定 seed 随机化,减少框架顺序与温度/缓存漂移的关联。可改变 seed 或强制矩阵顺序:

nix run .#benchmark -- --seed 42
nix run .#benchmark -- --ordered

常用命令

重新生成案例

案例已提交并包含锁文件;只有调整规模或生成器后才需要重新生成:

# 重新生成并重新锁定每个框架;需要网络
nix run .#generate -- --lock

# 只生成选定组合
nix run .#generate -- --scales tiny,small --frameworks snowveil,den

--lock 先为每个框架的第一个规模生成锁文件,再复制到同框架的其他规模。因为同一框架在所有规模中使用完全相同的 input 图,这可确保版本一致。

全规模正确性检查

nix run .#smoke -- --scales tiny,small,medium,large

自定义基准

nix run .#benchmark -- \
  --frameworks plain,snowveil,snowfall,flake-parts,nixos-unified,den \
  --scales small,medium,large \
  --tasks flake-show,eval-first,eval-all,drv-first,drv-all \
  --modes cold,warm \
  --warmup 2 \
  --runs 10 \
  --seed 20260901

可用任务:

  • metadata:解析 flake input 元数据,主要观察输入图管理开销。
  • flake-show:枚举 flake outputs。
  • eval-first / eval-last:强制首台/末台主机的合成配置摘要。
  • eval-all:强制所有主机的配置摘要。
  • drv-first / drv-last:求值一台主机的 toplevel derivation path。
  • drv-all:求值所有主机的 toplevel derivation path。
  • flake-check:执行 nix flake check --no-build;适合作为额外压力任务,不在默认任务中。

固定 CPU

Linux 上可将整个基准固定到指定 CPU:

taskset -c 2 nix run .#benchmark -- --runs 10 --warmup 2

发布结果前还应尽量使用稳定的 CPU governor、关闭高负载后台任务,并在相同电源/温度条件下重复运行。

采集指标

每个样本写入 samples.jsonl,包括:

  • Python monotonic wall time
  • GNU time 的 user/system CPU、最大 RSS、page faults、context switches、文件系统 I/O
  • NIX_SHOW_STATS=1 的 evaluator CPU、heap、thunks、function calls、lookups、sets/lists 等原始统计
  • 完整命令、返回码、stdout/stderr 字节数和失败尾部日志

报告器生成:

  • summary.json
  • summary.csv
  • report.md

报告包含均值、中位数、标准差、最小值、最大值、p95、RSS、Nix CPU,以及相对 plain 和相对最快实现的倍率。

Cold 与 Warm 的准确含义

  • cold:传入 --no-eval-cache,禁用 Nix evaluator cache。
  • warm:保留 evaluator cache,并在正式采样前预热。

两种模式都不会清空 Linux page cache,也不会清空 /nix/store。因此这里的 cold 是“evaluator-cache cold”,不是重启机器后的绝对冷启动。详细边界见 docs/methodology.md

为什么不直接比较完整系统构建时间

完整构建通常主要受 nixpkgs revision、二进制缓存、下载带宽、磁盘和具体软件集合影响,容易掩盖配置框架的求值差异。本项目默认比较 outputs 构造、NixOS module 合并与 derivation 实例化;drv-* 会求值得到 toplevel derivation,但不实际构建系统 closure。

如需测构建,可在严格控制缓存与工作负载后,基于案例执行:

nix build --no-link path:cases/medium/snowveil#nixosConfigurations.host-000.config.system.build.toplevel

此类结果应单独报告,不应与纯求值结果混为一谈。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages