Hacker News 中文摘要

RSS订阅

经典基准测试忽略的负载下 Btrfs/ZFS/bcachefs 表现如何 -- Btrfs/ZFS/bcachefs under workloads classic benchmarks skip

多设备写时复制(CoW)文件系统在经典基准测试跳过的工作负载下进行测试,最新运行时间为2026-09-20 01:04:41 UTC,内核7.0.0-1012-azure,记录595次运行,展示145个趋势点。CI运行采用共享临时虚拟机(VM)上的回环设备(loop devices),每种文件系统分配一个独立虚拟机,比较形态与比率而非绝对MiB/s。每个作业会记录主机校准锚点。

正文

多设备 CoW 文件系统在经典基准测试通常会跳过的负载下的表现——最近一次运行 2026-09-20 01:04:41 UTC,内核 7.0.0-1012-azure,已记录 595 次运行 · 显示 145 个趋势点 · 代码仓库

CI 运行在共享的临时 VM 上用 loop 设备(每个文件系统一个 VM):比较的是形状和比例,不是绝对的 MiB/s。每个作业都会记录一个主机校准锚点——见表格。

其他环境:真实硬件 (farm3)、SAS HDD

测试环境

  • 环境:GitHub 托管的 Ubuntu VM;每种文件系统配置都在全新 VM 中运行
  • 设备:四个 16 GiB loop 设备,外加一个 16 GiB 重建备用盘;ZFS-single 作业使用一个 32 GiB loop 设备
  • 存储注意事项:loop 设备文件共享 VM 的临时后端存储,它们不是相互独立的物理磁盘
  • 镜像布局:Linux md/LVM RAID10、两对条带化的 ZFS mirror、Btrfs RAID1,或跨四个 loop 设备的 bcachefs replicas=2
  • 校验布局:Linux md RAID6、ZFS RAIDZ1/RAIDZ2、带 RAID1C3 元数据的 Btrfs RAID6,或 bcachefs 纠删码
  • 单设备布局:使用一个 loop 设备

如何阅读这些结果

工作负载范围。 fio 是成熟工具,MiB/s、IOPS 和延迟百分位也是常规测量,但这些具体的工作负载配方和 Overall Core 指数是本项目特有的,不是行业标准基准套件。

缓存策略。 套件不使用 O_DIRECT。标注为冷缓存的读阶段会丢弃 Linux 页缓存(并对 ZFS 池做 export/import),然后执行正常读操作把缓存重新填起来。其他阶段使用普通缓冲 I/O,并在有说明处使用 fsync、fdatasync 或 sync;热目录 stat 的结果单独报告。

损坏探测。 覆盖某个成员设备上 2 GiB 的原始区间后,SURVIVED 只表示被跟踪的 read.dat 文件在 scrub 之后仍然可读且哈希一致;FAIL 表示它变了或读不出来。UNPROVEN 表示读取来自一个没有端到端数据校验和的栈。这并不认证整个文件系统,也不能证明被覆写的每个字节都属于已分配数据。见完整方法与注意事项。

汇总指数

评分模型 v1:100 = 所选队列的中位数。指标按方向归一化,再以子组和组等权做几何平均。使用最近 8 次完整的选中队列运行;损坏探测从不参与平均。点击列头排序;选择一个分数可展开其归一化贡献。

每个条目都覆盖子组 6/6、4/4、9/9 项指标。损坏探测结论:

  • ext4/single — N/A
  • ext4/md-raid10 — FAIL
  • ext4/lvm-raid10 — FAIL
  • xfs/single — N/A
  • xfs/md-raid10 — UNPROVEN
  • xfs/lvm-raid10 — FAIL
  • xfs/zvol — SURVIVED
  • xfs/lvm-raid10-int — SURVIVED
  • zfs/mirror — SURVIVED
  • zfs/mirror-8k — SURVIVED
  • zfs/single — N/A
  • zfs/raidz2 — SURVIVED
  • zfs/raidz1 — SURVIVED
  • btrfs/raid1 — SURVIVED
  • btrfs/single — N/A
  • btrfs/raid6 — SURVIVED
  • bcachefs/replicas2 — SURVIVED
  • bcachefs/single — N/A
  • bcachefs/ec — SURVIVED
  • ext4/md-raid10-luks — FAIL
  • ext4/md-raid6 — FAIL
  • zfs/mirror-enc — SURVIVED
  • zfs/raidz1-enc — SURVIVED
  • zfs/raidz2-enc — SURVIVED
  • btrfs/raid1-luks — SURVIVED
  • bcachefs/replicas2-enc — SURVIVED

最新一次运行

每项指标一张卡片,按最好优先排序——卡片上的按钮可切换到分组矩阵顺序。每个数值也出现在下方的表格里。

  • 顺序写 MiB/s · 越高越好
  • 随机写 4k + fsync IOPS · 越高越好
  • 随机写 4 线程 IOPS · 越高越好
  • fsync p99 延迟 ms · 越低越好
  • fsync p99.9 延迟 ms · 越低越好
  • 随机读 4k 冷缓存 IOPS · 越高越好
  • 随机读 4 线程 IOPS · 越高越好
  • 顺序读 MiB/s · 越高越好
  • 空闲时琐碎操作 p99 ms · 越低越好
  • 流式写下琐碎操作 p99 ms · 越低越好
  • 负载下琐碎操作最坏情况 ms · 越低越好
  • 负载下完成的琐碎操作数 · 越高越好
  • 创建 20k 文件树 ms · 越低越好
  • 创建 20k 文件树 4 线程 ms · 越低越好
  • cp -r 20k 文件树 冷缓存 ms · 越低越好
  • rm -rf 20k 文件树 ms · 越低越好
  • ftruncate 空文件到 1G ms · 越低越好
  • 稀疏 1G 的分配字节数 B · 越低越好
  • ftruncate 256M 文件到 512M ms · 越低越好
  • 单目录创建 100k 文件 ms · 越低越好
  • 枚举 100k 名称 冷缓存 ms · 越低越好
  • stat 100k 文件 冷缓存 ms · 越低越好
  • stat 100k 文件 热缓存 ms · 越低越好
  • 删除 100k 文件目录 ms · 越低越好
  • 创建快照 ms · 越低越好
  • 删除全部快照 ms · 越低越好
  • 删除后空间回收 s · 越低越好
  • 回收期间写入 MiB/s · 越高越好
  • zstd 压缩比 x · 越高越好
  • 可压缩数据写入 MiB/s · 越高越好
  • reflink 复制 2G ms · 越低越好
  • 覆写普通文件 MiB/s · 越高越好
  • 覆写新建 reflink 克隆 MiB/s · 越高越好
  • 覆写刚做快照的文件 MiB/s · 越高越好
  • 降级随机写 IOPS · 越高越好
  • 降级随机读 IOPS · 越高越好
  • 设备丢失后重建 s · 越低越好
  • 损坏后 scrub s · 越低越好
  • 接近写满(95% 目标)写入 MiB/s · 越高越好
  • 接近写满(99% 目标)写入 MiB/s · 越高越好
  • 500 个快照时创建快照 ms · 越低越好
  • 带 500 快照重新挂载 ms · 越低越好
  • 删除 500 个快照 ms · 越低越好

快照老化

每轮迭代的随机覆写带宽(MiB/s),同时快照不断累积——平坦是好的,下降就是 CoW 碎片化的代价。快照数量因设计而异:技术在允许时取 100,默认记录大小的 ZFS 取 10,LVM 取 8。

各次运行的趋势

每项指标一张卡片,每次运行一个点——最近 100 次运行单独显示,更早的运行折叠为每日中位数(完整历史在 results-data 分支)。在图上拖动可缩放,双击重置;y 轴会重新缩放到可见范围。图表覆盖与上一节相同的指标。

探索趋势

现有图表旁边的一个可选交互视图。它使用同一份压缩历史和作用中的文件系统筛选器;滚动或拖动可缩放,用工具箱恢复或导出。

指标 显示 范围 基线

正在加载增强图表控件……

表格视图

最新一次运行的全部指标——点击列头排序。calib = 在文件系统存在之前测得的主机磁盘锚点(VM 噪声指标)。

| 文件系统 | 顺序写 MiB/s | 随机写 4k + fsync IOPS | 随机写 4 线程 IOPS | fsync p99 延迟 ms | fsync p99.9 延迟 ms | 随机读 4k 冷缓存 IOPS | 随机读 4 线程 IOPS | 顺序读 MiB/s | 空闲时琐碎操作 p99 ms | 流式写下琐碎操作 p99 ms | 负载下琐碎操作最坏情况 ms | 负载下完成的琐碎操作数 | 创建 20k 文件树 ms | 创建 20k 文件树 4 线程 ms | cp -r 20k 文件树 冷缓存 ms | rm -rf 20k 文件树 ms | ftruncate 空文件到 1G ms | 稀疏 1G 分配字节数 B | ftruncate 256M 文件到 512M ms | 单目录创建 100k 文件 ms | 枚举 100k 名称 冷缓存 ms | stat 100k 文件 冷缓存 ms | stat 100k 文件 热缓存 ms | 删除 100k 文件目录 ms | 创建快照 ms | 删除全部快照 ms | 删除后空间回收 s | 回收期间写入 MiB/s | zstd 压缩比 x | 可压缩数据写入 MiB/s | reflink 复制 2G ms | 覆写普通文件 MiB/s | 覆写新建 reflink 克隆 MiB/s | 覆写刚做快照的文件 MiB/s | 降级随机写 IOPS | 降级随机读 IOPS | 设备丢失后重建 s | 损坏后 scrub s | 接近写满 95% 写入 MiB/s | 接近写满 99% 写入 MiB/s | 500 快照时创建快照 ms | 带 500 快照重新挂载 ms | 删除 500 个快照 ms | scrub 发现错误数 | scrub 修复数 | 损坏后测试文件完好 | FIEMAP 显示共享区段 | 100% 满时删除 | 删除后可写 | calib 顺序 MiB/s | calib 随机 IOPS | 工具/模块版本 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | ext4/single | 381 | 6,775 | 14,705 | 11.9 | 26.6 | 5,981 | 8,184 | 417 | 2.51 | — | 30,291 | 5 | 1,410 | 763 | 2,061 | 431 | 8.48 | 0 | 1.07 | 3,333 | 92 | 1,284 | 309 | 1,318 | — | — | — | — | — | — | — | 67.1 | — | — | — | — | — | — | 158 | 161 | — | — | — | — | — | — | — | yes | yes | 415 | 14,766 | mke2fs 1.47.2 (1-Jan-2025) | | ext4/md-raid10 | 192 | 3,231 | 4,720 | 39.1 | 41.7 | 5,909 | 19,435 | 335 | 3.19 | — | 14,696 | 10 | 1,705 | 1,039 | 2,325 | 431 | 9.75 | 0 | 0.75 | 3,404 | 95 | 1,321 | 311 | 1,327 | — | — | — | — | — | — | — | 38.6 | — | — | 11,350 | 5,740 | 87 | 168 | 89.9 | 86.3 | — | — | — | 4,194,304 | 0 | NO | — | yes | yes | 407 | 15,557 | mke2fs 1.47.2 (1-Jan-2025) | | ext4/lvm-raid10 | 191 | 3,065 | 4,549 | 35.9 | 40.6 | 5,562 | 18,881 | 375 | 2.74 | 54.8 | 60.1 | 139 | 1,661 | 1,019 | 2,497 | 439 | 16.4 | 0 | 0.85 | 3,477 | 89 | 1,320 | 313 | 1,397 | 277 | 2,261 | 4 | 35.1 | — | — | — | 37.3 | — | 34.2 | 9,538 | 5,539 | 44 | 85 | 90.4 | 80.3 | 3,321 | 65 | 298,055 | 4,192,256 | 0 | NO | — | yes | yes | 407 | 16,293 | mke2fs 1.47.2 (1-Jan-2025) | | xfs/single | 359 | 5,425 | 6,966 | 5.87 | 13.2 | 5,780 | 7,918 | 416 | 1.32 | — | 31,732 | 2 | 4,877 | 1,555 | 1,704 | 805 | 94.9 | 0 | 0.36 | 2,815 | 83 | 1,200 | 301 | 3,092 | — | — | — | — | — | — | 5 | 71.0 | 18.9 | — | — | — | — | — | 185 | 179 | — | — | — | — | — | — | yes | yes | yes | 409 | 15,206 | mkfs.xfs version 6.18.0 | | xfs/md-raid10 | 196 | 3,144 | 6,579 | 31.6 | 37.0 | 6,040 | 19,610 | 335 | 2.02 | — | 4,890 | 8 | 7,075 | 1,627 | 1,666 | 646 | 14.9 | 0 | 0.48 | 2,715 | 76 | 1,140 | 292 | 2,828 | — | — | — | — | — | — | 6 | 39.5 | 14.1 | — | 8,239 | 6,314 | 85 | 167 | 85.0 | 86.4 | — | — | — | 4,194,304 | 0 | yes | yes | yes | yes | 409 | 15,587 | mkfs.xfs version 6.18.0 | | xfs/lvm-raid10 | 195 | 5,733 | 3,744 | 5.28 | 8.45 | 5,309 | 18,479 | 378 | 3.19 | 54.3 | 55.4 | 137 | 2,527 | 1,703 | 1,707 | 708 | 74.3 | 0 | 0.55 | 2,862 | 88 | 1,205 | 298 | 3,002 | 306 | 2,531 | 4 | 31.2 | — | — | 5 | 33.2 | 15.4 | 34.7 | 6,199 | 5,515 | 43 | 84 | 87.0 | 67.9 | 3,624 | 3,342 | 319,769 | 4,194,304 | 0 | NO | yes | yes | yes | 410 | 17,083 | mkfs.xfs version 6.18.0 | | xfs/zvol | 162 | 3,010 | 5,042 | 11.3 | 119 | 4,228 | 15,673 | 323 | 8.72 | — | 19,874 | 2 | 7,987 | 2,000 | 1,673 | 784 | 80.0 | 0 | 1.42 | 2,978 | 100 | 1,184 | 291 | 2,829 | 1,386 | 529 | 2 | 44.5 | 1.14 | 225 | 31 | 69.7 | 19.9 | 43.3 | 3,313 | 3,829 | 37 | 79 | 203 | 195 | — | — | — | 14,905 | 14,905 | yes | yes | yes | yes | 412 | 14,961 | mkfs.xfs version 6.18.0 on zfs-2.4.1-1ubuntu5.1 | | xfs/lvm-raid10-int | 89.4 | 3,999 | 3,889 | 30.5 | 48.0 | 5,667 | 19,087 | 383 | 3.82 | 451 | 453 | 80 | 2,756 | 2,072 | 1,623 | 687 | 73.7 | 0 | 3.14 | 2,777 | 86 | 1,153 | 293 | 2,959 | 753 | 4,649 | 7 | 24.7 | — | — | 483 | 43.5 | 17.5 | 25.8 | 3,300 | 5,583 | 55 | 87 | 81.0 | 85.6 | 4,144 | 4,515 | 375,666 | 0 | 0 | yes | yes | yes | yes | 413 | 14,843 | mkfs.xfs version 6.18.0 | | zfs/mirror | 193 | 1,786 | 1,719 | 190 | 287 | 16,524 | 23,672 | 285 | 1.47 | — | 1,453 | 8 | 1,971 | 857 | 1,846 | 845 | 7.02 | 512 | 0.7 | 3,987 | 103 | 1,600 | 384 | 4,502 | 136 | 1,195 | 12 | 5.9 | 3.87 | 417 | 161 | 8.82 | 5.25 | 5.91 | 2,250 | 46,863 | 26 | 60 | 6.24 | 0 | 18 | 1,347 | 2,525 | 11,013 | 11,013 | yes | NO | yes | yes | 413 | 14,736 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/mirror-8k | 164 | 10,350 | 14,129 | 3.42 | 152 | 5,411 | 17,691 | 292 | 1.43 | — | 651 | 17 | 2,052 | 854 | 1,858 | 847 | 6.83 | 512 | 0.68 | 4,041 | 103 | 1,617 | 388 | 4,455 | 687 | 3,764 | 4 | 166 | 3.16 | 242 | 1,344 | 134 | 12.2 | 132 | 12,506 | 6,614 | 22 | 49 | 45.6 | 0 | 18 | 1,347 | 2,497 | 25,963 | 25,963 | yes | NO | yes | yes | 406 | 14,600 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/single | 372 | 3,533 | 3,457 | 43.3 | 54.3 | 12,060 | 18,347 | 413 | 3.82 | 1,149 | 1,144 | 21 | 1,995 | 925 | 1,871 | 865 | 8.99 | 512 | 0.79 | 4,019 | 114 | 1,637 | 393 | 4,626 | 386 | 353 | 5 | 13.2 | 3.87 | 475 | 491 | 17.1 | 8.5 | 14.6 | — | — | — | — | 11.8 | 0 | 16 | 769 | 2,589 | — | — | — | NO | yes | yes | 389 | 14,617 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/raidz2 | 189 | 1,744 | 1,700 | 110 | 173 | 14,107 | 19,255 | 402 | 3.65 | — | 1,301 | 11 | 3,784 | 837 | 1,877 | 850 | 7.29 | 512 | 0.88 | 4,002 | 111 | 1,633 | 392 | 4,574 | 297 | 312 | 13 | 5.01 | 3.87 | 390 | 1,784 | 8.45 | 4.93 | 5.6 | 1,931 | 42,037 | 51 | 60 | 6.09 | 4.46 | 17 | 1,776 | 2,547 | 7,401 | 7,401 | yes | NO | yes | yes | 408 | 14,732 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/raidz1 | 277 | 2,585 | 2,554 | 101 | 150 | 16,130 | 20,589 | 401 | 1.25 | — | 1,524 | 13 | 1,697 | 640 | 929 | 388 | 6.9 | 512 | 0.98 | 1,796 | 92 | 875 | 251 | 2,466 | 484 | 393 | 9 | 7.02 | 3.87 | 452 | 1,474 | 12.9 | 7.38 | 9.69 | 3,159 | 36,851 | 34 | 41 | 9.44 | 0 | 11 | 784 | 1,433 | 22,527 | 22,527 | yes | NO | yes | yes | 377 | 14,793 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | btrfs/raid1 | 170 | 2,672 | 3,766 | 18.5 | 29.0 | 6,573 | 20,671 | 416 | 3.75 | 6,275 | 6,297 | 32 | 10,569 | 1,324 | 1,349 | 679 | 1.12 | 0 | 0.86 | 2,490 | 160 | 1,309 | 358 | 3,323 | 35 | 8 | 16 | 70.1 | 3.57 | 257 | 4 | 77.9 | 130 | 79.9 | 3,664 | 6,806 | 23 | 44 | 0 | 0 | 24 | 239 | 70 | 264,188 | 264,188 | yes | yes | yes | yes | 354 | 14,322 | btrfs-progs v6.17.1 | | btrfs/single | 303 | 3,508 | 5,163 | 7.24 | 15.9 | 5,584 | 7,855 | 413 | 1.79 | 2,139 | 2,144 | 41 | 7,544 | 1,336 | 1,385 | 587 | 0.9 | 0 | 0.63 | 2,414 | 211 | 1,361 | 316 | 3,377 | 29 | 7 | 56 | 61.2 | 3.57 | 306 | 3 | 74.5 | 135 | 73.7 | — | — | — | — | 0 | 0 | 20 | 185 | 72 | — | — | — | yes | yes | yes | 373 | 12,970 | btrfs-progs v6.17.1 | | btrfs/raid6 | 173 | 1,994 | 2,701 | 26.9 | 37.0 | 7,324 | 21,130 | 410 | 2.87 | — | 9,645 | 12 | 4,785 | 1,454 | 1,323 | 664 | 0.85 | 0 | 0.74 | 2,488 | 166 | 1,296 | 359 | 3,134 | 36 | 8 | 34 | 70.3 | 3.57 | 209 | 4 | 72.2 | 119 | 69.2 | 2,606 | 9,935 | 34 | 52 | 0 | 0 | 23 | 240 | 80 | 131,233 | 131,233 | yes | yes | yes | yes | 366 | 13,338 | btrfs-progs v6.17.1 | | bcachefs/replicas2 | 195 | 8,728 | 18,503 | 4.55 | 7.63 | 10,849 | 13,217 | 360 | 4.69 | — | 3,241 | 18 | 2,604 | 1,908 | 2,092 | 923 | 12.9 | 0 | 1.45 | 2,808 | 122 | 1,127 | 349 | 2,478 | 34 | 2,870 | 6 | 32.0 | 3.74 | 345 | 216 | 58.8 | 65.4 | 74.8 | 8,334 | 35,165 | 22 | 47 | 126 | 134 | 32 | 550 | 11,385 | — | — | yes | NO | yes | yes | 407 | 15,110 | tools 1.39.6 / module 1.39.6 | | bcachefs/single | 359 | 10,042 | 26,479 | 4.69 | 9.63 | 14,945 | 19,781 | 393 | 4.49 | 2,600 | 2,590 | 28 | 2,173 | 1,267 | 2,074 | 884 | 3.39 | 0 | 0.75 | 2,912 | 117 | 1,150 | 350 | 2,481 | 33 | 2,693 | 8 | 41.1 | 3.74 | 367 | 196 | 96.5 | 85.6 | 112 | — | — | — | — | 156 | 141 | 35 | 457 | 10,799 | — | — | — | NO | yes | yes | 411 | 14,832 | tools 1.39.6 / module 1.39.6 | | bcachefs/ec | 96.2 | 6,724 | 10,626 | 6.91 | 12.9 | 15,370 | 21,340 | 415 | 2.74 | 2,433 | 2,437 | 22 | 3,369 | 2,331 | 2,196 | 973 | 5.71 | 0 | 0.53 | 2,973 | 125 | 1,176 | 350 | 2,597 | 34 | 3,099 | 8 | 13.8 | 3.73 | 299 | 205 | 37.4 | 41.2 | 42.5 | 7,030 | 22,351 | 58 | 41 | 0 | 0 | 31 | 568 | 11,942 | — | — | yes | NO | yes | yes | 409 | 15,016 | tools 1.39.6 / module 1.39.6 | | ext4/md-raid10-luks | 192 | 3,140 | 4,622 | 37.5 | 40.6 | 5,956 | 19,143 | 394 | 2.31 | — | 26,641 | 3 | 2,957 | 1,105 | 2,748 | 439 | 11.9 | 0 | 0.91 | 3,395 | 92 | 1,353 | 312 | 1,369 | — | — | — | — | — | — | — | 37.9 | — | — | 10,528 | 5,982 | 85 | 167 | 85.0 | 83.8 | — | — | — | 4,194,304 | 0 | NO | — | yes | yes | 407 | 14,811 | mke2fs 1.47.2 (1-Jan-2025) | | ext4/md-raid6 | 192 | 2,219 | 3,248 | 41.7 | 45.4 | 5,968 | 17,911 | 272 | 2.64 | 1,351 | 1,345 | 39 | 4,149 | 1,089 | 2,544 | 436 | 13.2 | 0 | 1.49 | 3,429 | 93 | 1,317 | 312 | 1,401 | — | — | — | — | — | — | — | 27.9 | — | — | 5,146 | 5,101 | 108 | 102 | 88.9 | 87.3 | — | — | — | 4,192,000 | 0 | NO | — | yes | yes | 411 | 15,706 | mke2fs 1.47.2 (1-Jan-2025) | | zfs/mirror-enc | 191 | 1,673 | 1,675 | 158 | 363 | 13,794 | 23,502 | 284 | 1.7 | — | 1,409 | 10 | 2,042 | 912 | 1,919 | 863 | 26.2 | 512 | 0.77 | 4,111 | 113 | 1,775 | 393 | 4,498 | 185 | 357 | 12 | 5.35 | 3.87 | 395 | 84 | 7.23 | 4.8 | 5.27 | 2,064 | 45,523 | 26 | 60 | 6.15 | 0 | 19 | 1,151 | 2,557 | 4,718 | 4,718 | yes | NO | yes | yes | 383 | 14,736 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/raidz1-enc | 270 | 2,467 | 2,494 | 106 | 173 | 13,614 | 20,965 | 400 | 3.46 | — | 1,776 | 13 | 2,046 | 870 | 1,922 | 841 | 27.5 | 512 | 0.83 | 4,081 | 122 | 1,721 | 389 | 4,547 | 350 | 755 | 11 | 6.38 | 3.87 | 433 | 84 | 11.1 | 6.1 | 8.43 | 2,607 | 43,309 | 34 | 42 | 9.37 | 0 | 17 | 1,443 | 2,582 | 29,782 | 29,782 | yes | NO | yes | yes | 407 | 14,688 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | zfs/raidz2-enc | 188 | 1,668 | 1,651 | 135 | 179 | 11,876 | 19,467 | 403 | 3.88 | — | 1,297 | 11 | 3,936 | 940 | 1,898 | 853 | 27.1 | 512 | 1.07 | 4,112 | 133 | 1,738 | 379 | 4,498 | 335 | 601 | 11 | 5.85 | 3.87 | 390 | 87 | 6.98 | 4.4 | 4.88 | 1,719 | 41,549 | 51 | 61 | 5.96 | 0 | 16 | 1,824 | 2,586 | 15,663 | 15,663 | yes | NO | yes | yes | 388 | 14,782 | zfs-2.4.1-1ubuntu5.1 zfs-kmod-2.4.1-1ubuntu5 | | btrfs/raid1-luks | 168 | 2,219 | 3,242 | 11.7 | 25.0 | 5,109 | 16,400 | 414 | 4.23 | — | 14,841 | 12 | 2,859 | 1,484 | 1,475 | 650 | 1.17 | 0 | 0.93 | 2,524 | 196 | 1,419 | 324 | 3,348 | 44 | 8 | 25 | 71.0 | 3.57 | 233 | 4 | 66.7 | 99.6 | 62.4 | — | — | — | 41 | 0 | 0 | 27 | 216 | 90 | 521,984 | 521,984 | yes | yes | yes | yes | 375 | 13,221 | btrfs-progs v6.17.1 | | bcachefs/replicas2-enc | 193 | 8,706 | 18,947 | 3.59 | 5.93 | 10,018 | 13,380 | 358 | 3.88 | 1,636 | 1,643 | 27 | 2,747 | 1,591 | 2,168 | 932 | 34.0 | 0 | 1.18 | 2,874 | 118 | 1,192 | 348 | 2,564 | 34 | 2,996 | 6 | 17.6 | 3.79 | 333 | 236 | 71.3 | 57.7 | 75.8 | 9,335 | 25,507 | 20 | 48 | 135 | 140 | 35 | 1,252 | 12,412 | — | — | yes | NO | yes | yes | 411 | 15,810 | tools 1.39.6 / module 1.39.6 |

指标参考

上面每个数字背后到底跑的是什么,以及代码在哪里。可由环境变量调整的大小(SEQ_SIZE 等)显示的是 CI 默认值。脚本位于仓库的 scripts/run-bench.sh 以及 scripts/fs 下按后端划分的脚本中。

顺序写(MiB/s)

fio 顺序写一个新文件:bs=1M,size=SEQSIZE(CI 中为 2G),单作业,末尾 fsync(--endfsync=1)。报告为写带宽。阶段 1。

随机写 4 线程(IOPS)

阶段 2 的随机写负载加 --numjobs=4(每线程一个文件,每 16 次 IO 一次 fdatasync,4 线程对应 runner 的 4 个 vCPU)。文件系统的锁架构只在并发下才暴露——按 bcachefs 作者的说法,跨文件系统差异最大的就在这里。与单线程卡片对比:明显高于 1x 就是并行度,低于 1x 就是锁竞争。阶段 2。

创建 20k 文件树,4 个 worker(ms)

4 个并发 worker 在互不重叠的目录子集上创建这棵 20k 文件树——考验元数据锁竞争(btrfs 树锁 vs XFS 的每 AG 并行 vs bcachefs 的 b-tree 设计)。与单 worker 卡片对比。阶段 3.6。

随机写,4k + fsync(IOPS)

fio 对 1G 文件做 30 秒随机 4k 写,每 16 次 IO 一次 fdatasync(--rw=randwrite --bs=4k --fdatasync=16 --time_based)。报告为 IOPS。阶段 2。

fsync p99 延迟(ms)

阶段 2 中 fdatasync 完成延迟的第 99 百分位(取自 fio 的 sync.lat_ns 百分位)。CoW 事务提交(ZFS 约每 5 秒一个 txg、btrfs 的提交间隔)在这里表现为周期性尖峰,而这些被 IOPS 平均值掩盖了。阶段 2。

fsync p99.9 延迟(ms)

阶段 2 中 fdatasync 完成延迟的第 99.9 百分位——尾部中的尾部。zfs mirror-8k 的 IOPS 和 p99 都很漂亮,但这一项会爆到约 180ms。阶段 2。

随机读,4k 冷缓存(IOPS)

fio 对 2G 文件随机 4k 读,单线程,对 512M 不重复块只跑一遍(基于时间的循环会把冷读和缓存命中混在一起),并且在冷缓存屏障之后进行(丢弃页缓存;ZFS 池要 export/import,因为 drop_caches 碰不到 ARC)。阶段 3。

随机读,4 线程(IOPS)

同样的冷缓存单遍随机读,加 --numjobs=4(每个 128M)。镜像只有在并发下才能同时从两份副本读——单条依赖读流显现不出副本读扩展。在 CI 的 loop 设备上所有副本共享一块物理盘,所以带宽收益只在真实硬件上才出现。阶段 3。

顺序读(MiB/s)

fio 对 2G 文件顺序 1M 读,完整跑一遍(不循环——基于时间的运行会重读缓存块并报出内存速度),冷缓存。阶段 3.4。

空闲时琐碎操作 p99(ms)

一个自制的持久化延迟探针——每 200ms 一次 4k 写 + fsync,单独运行 10 秒。它受小型交互式写入启发,不是每个 shell 或编辑器的字面模型,也不是行业标准负载。窗口内大约能容纳 50 次操作,所以 p99 实际上是最慢样本所在的直方图桶,而不是稳定的总体百分位。它是下面那个负载版孪生指标的基线。阶段 3.5。

流式写下琐碎操作 p99(ms)

同样的琐碎 4k+fsync 操作,但在第二个 fio 作业用 1M 流式写灌满文件系统时测量 30 秒。「我的提示符多久才回来」:CoW 提交纠缠让这个小小的 fsync 去等大写入者的事务。只有在至少完成 20 次操作时才报告——低于这个数,百分位在统计上没有意义(fio 的对数直方图分桶甚至会让不同机器给出完全相同的桶边界值);在饥饿状态下,以“负载下完成的琐碎操作数”为准。阶段 3.5。

负载下琐碎操作最坏情况(ms)

30 秒流式写灌入期间观测到的最差单次琐碎 fsync——“提示符”卡住的最长时间。注意:fio 把延迟存进对数直方图桶(约 1.5% 宽),所以极端值会被量化——不同配置可能报出同一个桶边界值(例如 17,113ms)。而当一次操作要花约 17 秒时,30 秒窗口里只有 1-2 个样本,此时 p99 就等于最大值。下面那个“完成的琐碎操作数”是不受分桶影响的同伴指标。阶段 3.5。

负载下完成的琐碎操作数(ops)

30 秒灌入期间完成了多少次琐碎 4k+fsync 操作,按 200ms 节奏最多约 145 次——一个不受直方图分桶影响的饥饿比率:140+ 表示提示符保持响应,个位数表示它被流式写入者劫持了。它统计整个循环(写 + fsync + 思考),所以阻塞在 write() 调用里——比如 ZFS 的 txg 背压——会在这里显现,尽管上面的 fsync 百分位根本看不到。完整的交互性图景需要两者。阶段 3.5。

创建 20k 文件树(ms)

创建一棵确定性的源树:200 个目录下 20,000 个 1-8k 文件(种子 RNG),然后 sync。每次运行都是同一棵树,所以趋势可比。阶段 3.6。

cp -r 20k 文件树,冷缓存(ms)

冷缓存屏障之后 cp -r 这棵 20k 文件树,再加 sync——也就是“复制一棵内核树”的测试。阶段 3.6。

rm -rf 20k 文件树(ms)

对复制出来的 20k 文件树执行 rm -rf,再加 sync。阶段 3.6。

单目录创建 100k 文件(ms)

在一个目录里串行创建 LARGEDIR_FILES 个确定性空文件,然后 sync(CI 中为 100,000 个;可调到一百万以还原最初的那个超大目录负载)。这隔离出单个目录的索引与插入行为。阶段 3.8。

枚举 100k 名称,冷缓存(ms)

冷缓存屏障之后,用 os.scandir 消费大目录里的每个名字,不做 stat、不排序。数量会与 LARGEDIR_FILES 核对。这隔离了目录枚举与元数据查找。阶段 3.8。

stat 100k 文件,冷缓存(ms)

在第二个冷缓存屏障之后,枚举大目录并显式 stat 每个条目。这是不带终端格式化和输出开销的 ls -lU 的文件系统核心部分。阶段 3.8。

stat 100k 文件,热缓存(ms)

不丢缓存、连续重复三次 stat 扫描的中位数。与冷缓存 stat 卡片对比,可以暴露 dentry/inode 缓存行为。阶段 3.8。

删除 100k 文件目录(ms)

rm -rf 那个装有 LARGEDIR_FILES 个空文件的单目录,然后 sync。测量批量 unlink 与目录索引清理。阶段 3.8。

ftruncate 空文件到 1G(ms)

把空文件 ftruncate 到 1GiB 再 fsync——稀疏文件创建应该是纯元数据操作。这里量时间,下一张卡片量分配字节数(st_blocks):两者合起来回答“稀疏是否真的稀疏”。社区请求。阶段 3.7。

稀疏 1G 的分配字节数(B)

刚截断出来的 1GiB 空文件的 st_blocks × 512——一个理应是纯空洞的文件实际占用的字节数(有些文件系统的元数据/间接块会在这里露面)。阶段 3.7。

ftruncate 256M 文件到 512M(ms)

把一个写满的 256MiB 文件 ftruncate 到 512MiB(+fsync)——跨一个新空洞把文件扩大;JSON 里还记录 sparsegrowbytes,即分配增量(预期约 0)。阶段 3.7。

快照老化曲线(MiB/s)

老化曲线:每轮迭代用 64M 随机 4k 写覆写一个 2G 文件,每次之前先做快照;每轮的带宽构成这条曲线。技术在允许时做 100 轮;默认记录大小的 ZFS 做 10 轮(128K 记录使每个快照几乎钉住整个文件),LVM 做 8 轮(dm-snapshot 把 origin 的写复制进每个快照)。各后端的快照机制:btrfs subvolume 快照、zfs snapshot、bcachefs subvolume 快照、lvcreate -s。阶段 4。

创建快照(ms)

老化过程中每轮创建快照时间的中位数(阶段 4)。

删除全部快照(ms)

那个删除全部老化快照的调用所花的时间——btrfs subvolume delete(毫秒级返回,清理器随后工作)、逐个 zfs destroy、bcachefs subvolume delete、lvremove。阶段 5。

删除后空间回收(s)

删掉所有快照后,空闲空间真正回到老化前 80% 所需的秒数(df 每秒轮询;LVM 看 VG 空闲空间,它的快照位于文件系统之外)。这个值与删除调用之间的差就是后台清理窗口;null 表示 300 秒内空间没回来。阶段 5。

回收期间的写入(MiB/s)

后台回收运行时测得的前台写带宽(负载与一轮老化迭代相同)——清理器从你这里偷走多少。阶段 5。

500 个快照时创建快照(ms)

连续创建 500 个快照,之间没有任何数据搅动(把元数据扩展性与保留成本分开);这是最后 20 次创建延迟的中位数。LVM 以 24M 快照参与(500 个必须装进 VG)——没有搅动意味着创建期间没有 CoW 放大;如果提前停下,JSON 里的 snapscale_count 会记录 dm-snapshot 实际走到哪里。阶段 5.5。

带 500 快照重新挂载(ms)

在存在 500 个快照时做完整卸载 + 挂载(ZFS 为 zpool export/import)。阶段 5.5。

删除 500 个快照(ms)

批量删除全部 500 个快照:btrfs subvolume delete(一次调用)、zfs 区间 destroy snap1%snap500(一次调用)、bcachefs 每个快照一次删除。阶段 5.5。

zstd 压缩比(x)

把 2G 的 75% 可压缩数据(fio --buffercompresspercentage=75 --refill_buffers --fallocate=none;btrfs 从不压缩进预分配区段)写入强制 zstd 的区域。比例按原生方式测量:compsize(btrfs)、zfs get compressratio、池 Used 差值 / 副本数(bcachefs)。阶段 6。

可压缩数据写入(MiB/s)

同一可压缩流的写带宽——压缩能让写更快(到达磁盘的字节更少),也可能消耗 CPU。阶段 6。

reflink 复制 2G(ms)

cp --reflink=always 复制那个 2G 文件——纯元数据克隆。btrfs 一次操作就克隆整个区段树(约毫秒级);bcachefs 按区段 reflink(约 200ms);OpenZFS 2.2+ 用它自己的 Block Reference Table;ext4 做不到。表里还记录 FIEMAP(filefrag -v)是否真的把克隆的区段报告为共享——这是深层的管线检查。阶段 6(divergence)。

覆写普通文件(MiB/s)

unshare 惩罚的基线:把 128M 随机 4k 覆写(end_fsync)写入一个普通、未共享的 2G 文件。阶段 6(divergence)。

覆写新建 reflink 克隆(MiB/s)

同样的覆写负载写入一个新建的 reflink 克隆——每次写都必须打破区段共享。与普通基线对比;XFS 也参与,使这一项成为集成式 vs 经典式的对照。阶段 6(divergence)。

覆写刚做快照的文件(MiB/s)

同样的覆写负载,写入刚刚被做过快照的那个普通文件——unshare 惩罚的快照版本。LVM 通过 lvcreate -s 参与。阶段 6(divergence)。

降级随机写(IOPS)

让一个设备失效(zpool offline / mdadm --fail / btrfs 用 loop-detach + 降级挂载 / bcachefs device offline / 在某个 LVM PV 下加 dm-error),然后在降级阵列上跑阶段 2 的随机写负载。它有可能正当地超过健康的阶段 2 数值:降级镜像会跳过对缺失成员的写,而且两个阶段跑在文件系统不同的年龄上。阶段 7。

降级随机读(IOPS)

阵列降级时的冷缓存随机 4k 读。阶段 7。

设备丢失后重建(s)

把完整冗余恢复到备用设备上的墙钟时间:zpool replace + 等 resilver、mdadm --add + --wait(整成员重同步)、btrfs replace -B、bcachefs device add + evacuate(阻塞到丢失设备上零数据为止)、lvconvert --repair + 轮询 sync_percent。CoW 文件系统只搬自己在活数据中占的那一份(到这一阶段 CI 中约 8G 逻辑数据),所以在相同的 loop 设备上它们收敛到相近的时间——md 则不管内容重同步整个成员,这个差距才是要看的。md/lvm 的数值在 ext4/xfs/LUKS 变体之间按构造几乎一致——重同步运行在文件系统之下,既不认识也不关心上面是什么。阶段 7。

损坏后 scrub(s)

把 2G 随机垃圾直接写到一个成员设备上(绕过文件系统,偏移 1G——用 python 注入器;uutils dd 在 dm 设备上会 seek 错)。丢弃 Linux 页缓存,然后跑完整 scrub:btrfs scrub -B、zpool scrub + wait、bcachefs scrub、md/lvm 的 sync-action 'check'。普通 md/lvm 只能统计不匹配数,因为它没有校验和来识别哪份是好的副本;dm-integrity 变体是例外。它在重建之后运行,所以也顺带验证重建。损坏窗口可能覆盖已分配数据、元数据或未使用空间。损坏探测的结论只比较 2G 的 read.dat 测试文件在此前后的 md5,不是整个文件系统的体检。SURVIVED 表示该文件可读且逐字节相同,FAIL 表示它变了或读不出来。非零的 found 计数说明检测到了损坏,非零的 repaired 计数是重建的最强证据;计数为零或缺失并不能证明注入区间打到了测试文件。scrub 之后、计算哈希之前会丢缓存;ZFS 和 XFS-on-zvol 做 export/import,其他后端不重新挂载。found/repaired 计数以各文件系统自己的单位计(块、记录、扇区——同样损坏下 zfs-8k 的计数约为默认 zfs 的 16 倍),并随损坏窗口恰好覆盖多少已分配数据而变化。在没有 dm-integrity 的普通 md/lvm 上,结论是概率性的:读操作在两条腿之间轮转,所以运气好的运行可以全部从好副本读出并报告完好。md/lvm 的 check 时长与文件系统无关(块级成员扫描),所以它们的 ext4/xfs/LUKS 变体报告的时间几乎相同——而 lvm 的时间约为 md 的一半,因为基准 LV 只占 VG 的一半。阶段 8。

接近写满(95% 目标)写入(MiB/s)

在一个同样布局的全新小阵列上(4x2G——写满主阵列会撑爆 runner 自己的磁盘):用不可压缩数据按 df 填到 95%,然后对已有文件做 64M 随机 4k 覆写。在这么小的设备上,btrfs 会在 df 越过目标之前先撞上 1G chunk 分配墙,所以它的探测是在墙处运行的——实际填充度记在 JSON 的 nearfull95_pct 里。阶段 9。

接近写满(99% 目标)写入(MiB/s)

按 df 填到 99% 之后的同样探测(见 95% 那条的注意事项)。表里的“100% 满时删除”和“删除后可写”结论来自同一阶段:填到硬 ENOSPC,rm 一个文件,验证空间回来且新写入成功。阶段 9。

评论

讨论整体围绕"这份跨文件系统基准到底能信几分"展开:多数人对在共享 CI 虚拟机和稀疏文件 loop 设备上跑出的数字持保留态度,同时也为 bcachefs 成绩亮眼却始终进不了主流内核而惋惜;少数人则从自身经验出发,坚持稳定与维护者可靠比跑分更重要。

认为共享云 CI 上跑出来的数字不能当真

  • 测试若不在裸机上进行,结果可能根本不可比,同一台机器上还有别的租户在抢磁盘(较多人) > 只要不是裸机,就很难说这些结果之间还有可比性,谁知道隔壁租户是不是也在读写同一块盘。(@Farmadupe)
  • 至少要有一组真机 SSD 与 HDD 的基线快照作参照,否则从 loop 设备背书的数字里读不出什么(较多人)
  • 即便作者做了校准,RAID 里到底几块盘、EC 配置长什么样等具体拓扑也没写清楚(较多人)

认为 bcachefs 在性能上已经是赢家

  • 结果里 bcachefs 表现相当好,在存储成本高企的当下是最合适的 Linux 文件系统(较多人)
  • 能把不同容量、不同类型的设备混进同一个池,可划分前台与后台设备来权衡性能,两层事务还能用不同的压缩设置(较多人)
  • 副本数可以精确到单个文件或目录:能重新下载、能重建的文件设低,重要文件设高(较多人) > 存储越来越贵的今天,bcachefs 就是最好的 Linux 文件系统。(@irusensei)

成绩越好,越为它不在内核树内而惋惜

  • Kent 与其他内核开发者没能达成一致,bcachefs 留在树外;想要带现代特性的树内文件系统,实际只剩 btrfs 可选(较多人) > 看到 bcachefs 跑得这么好,我反而更难受它没能留在内核里,想用也只能继续忍 btrfs。(@bhaney)

选文件系统还得看维护者和社区生态

  • 历史上从不缺有前途的新文件系统,但存储不仅要躲技术坑,还要躲维护者之间的内耗(少数人) > 评估文件系统也该把"社会层面"算进去,存储最怕的恰恰是维护者闹剧。(@blop)

宁可牺牲性能也要安心,继续留在 ZFS

  • 用 ZFS 十五年图的是省心,但承认它的性能表现并不让人满意(少数人)
  • 三个候选里两个被内核长期排斥、一个被红帽砍掉且可靠性口碑存疑,结论是干脆换个操作系统用 ZFS(少数人) > 我选别的操作系统上的 ZFS。(@sippingabonedry)

作者回应:承认噪声,用校准与样本量兜底

  • 每个测试前先跑校准,主动剔除不可靠的虚拟机;这挡不住全部噪声,只能限制其影响,但目前已有 593 次运行记录,平均值仍有参考价值
  • 一直在努力上真机,也确有进展;真机单轮耗时远高于 CI,所以推进很慢
  • 源码全部公开,不认为它百分之百正确,欢迎任何人来改进(较多人) > 我很清楚基于 CI runner 的跑分远非完美,也欢迎别人来改进它。(@fenio)

认为 md-raid10 与 lvm-raid10 的差距说不通

  • 从结果页和仓库里都看不出为什么 md-raid10 在 I/O 与响应上明显更差,按原理两者应该相近(少数人)

对结果表本身的可读性和定义有疑问

  • 结果页可读性差,建议重做 HTML/CSS(少数人)
  • 第一张表里的 "integrity" 失败究竟指什么,是指没能从 2G 损坏中恢复吗(少数人)
  • 打开页面第一件事是搜 SSD 和 HDD,搜不到就关掉了(少数人)

有价值的补充

  • bcachefs 的差异化能力集中在四点:池内混用不同容量与类型的设备;以前台/后台设备区分性能层级;前台与后台事务分别设置压缩;副本数可精确到单个文件或目录,例如可重建的下载文件设低副本、重要文件设高副本。
  • 有人提出一个值得关注的设想:把目前投入在"用 LLM 找安全漏洞"上的同等精力,转到挖掘常规的可靠性与性能 bug 上,会出现什么局面。
  • 数据里有个反常点:xfs 在"把空文件 ftruncate 到 1G"这项上远慢于别人,却在"把 256M 文件扩到 512M"上是最快的,评论里没人给出解释。
  • 作者的真机测试已经落地:一台由 Kent Overstreet 提供的 Hetzner 机器跑完三轮后报废,眼下在一台多盘 SAS 机械盘机器上跑第二轮,后续还计划测试更复杂的分层缓存拓扑。