# 硬盘分析工具怎么测才公平？我在 GitHub Actions 上搭了个公开跑分

**URL:** <https://meta.appinn.net/t/topic/95661>\
**Category:** 讨论分享\
**Tags:** linux, github, windows, macos\
**Created:** [2026 年11 月 10 日 21:42 UTC](https://meta.appinn.net/t/topic/95661 "2026-11-10T21:42:52Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![DuskXi](https://meta-edge.appinn.com/user_avatar/meta.appinn.net/duskxi/32/82098_2.png) [@DuskXi](https://meta.appinn.net/u/DuskXi)\
发布日期： [2026 年11 月 10 日 21:42 UTC](https://meta.appinn.net/t/topic/95661/1 "2026-11-10T21:42:52Z")

</div>

先说利益关系：我是 SpaceKnife 的作者，SpaceKnife 也在这个榜上，目前总榜第一。测试数据、计时方法和评分规则也都是我写的，作者和参赛者是同一个人，有利益冲突的风险，这一点我承认。(这只是为了声明有关联，该贴为讨论硬盘扫描软件跨平台基准测试的做法，而非任何软件的推荐帖子)

所以我给自己定了两条：

1. 规则只照着真实硬盘和日常使用的情况来定，不看哪个工具在上面跑得快；
2. 生成数据的代码、计时脚本、每个工具的版本和命令行、每次运行的日志和原始输出全部公开，欢迎监督，也欢迎开 issue 提建议，有道理就改。

仓库：[GitHub - DuskXi/Disk-Scan-Benchmark · GitHub](https://github.com/DuskXi/Disk-Scan-Benchmark)

下面主要聊技术细节，也想听听大家的看法。

## 为什么要自己搭

在自己电脑上测硬盘分析工具，结果很难复现：盘上的内容、碎片程度、缓存状态、硬件都不一样，换台机器名次就可能变。我想要一个谁都能看着它跑、也能 fork 了自己重跑的榜单，没找到现成的，就用 GitHub Actions 的免费 runner 搭了一个：

- 平台：Windows / Linux / macOS，x64 和 ARM64
- 文件系统：NTFS、ReFS、ext4、Btrfs、XFS、ZFS、APFS（Windows 上的 Btrfs 是实验项）
- 体量：10 万到 900 万个文件，逻辑上 100 GiB 到 3 TiB；另有一档 1400 万文件、塞满 99% 的 4 TB 盘，目前只在 MFT 专栏跑过
- 16 个扫描器：gdu、dua-cli、dust、ncdu、pdu、diskus、erdtree、SquirrelDisk、WinDirStat、WizTree、SpaceSniffer、Sysinternals du、GNU du、BSD du，以及 SpaceKnife 0.3.5 和 0.3.4

TreeSize Free 没有参加：它的许可证不允许在 Windows Server 上使用，而 GitHub 的 Windows runner 就是 Windows Server。

## 测试盘：合成的，但尽量像一块用了几年的盘

真实硬盘没法拿来做公开测试：每次内容不同，文件名和路径还涉及隐私。所以用合成文件树。目录树完全由随机种子决定，同一个种子在所有平台、所有运行里一模一样。生成器同时写出标准答案：文件数、目录数、逻辑大小、实际占用、每个区域的明细。

合成数据最大的风险是不像真的。一块新格式化、按顺序一口气写满的卷，目录和文件的记录（NTFS 的 MFT 记录、ext4 的 inode）挤在一起，冷扫时读一块就能顺带拿到一大串有用的记录，比真实盘便宜好几倍。所以生成器默认做「老化」：

- 每个目录在父目录之后的某个随机时间点创建；
- 文件拆成 4–64 个一批，分散在目录创建之后的整条时间线上；
- 大约每 8 个文件旁边建一个临时文件再删掉，留下空洞和可复用的记录；
- 同一目录里的文件按打乱的顺序创建，避免「名字顺序 = 磁盘顺序」这种真实盘上不存在的巧合。

 ![01-aging](https://h2cdn.appinn.me/original/3X/1/d/1d347a66bb038fdefa7da3aac7b853c523a2b7fc.jpeg)

树分成四个区域，各对应一类真实结构：

- 依赖目录那样的深链（8–25 层）；
- 浏览器缓存那样的大目录；
- 视频、虚拟机磁盘那样的大文件；
- 硬链接、符号链接循环、超长路径这些边界情况。

生成算法的取舍只用一个标准：生成出来的结构和磁盘布局是不是更接近真实盘。每次生成后都会测一组布局指标，和真实盘的同一组数字对比：

- 同一目录下的记录号有多集中；
- 按名字相邻的文件在盘上是否也相邻；
- 文件离目录记录有多远；
- 目录索引被分成了几段。

某个工具在合成数据上变快或变慢，不作为调整生成器的理由。

## 冷扫、热扫和访问时间

- **冷扫** ：每次冷扫前卸载并重新挂载测试盘（Windows 用 VHDX、Linux 用直接 I/O 的 loop 设备、macOS 用磁盘镜像），Linux 再清页缓存。计时的冷扫是进程第二次启动，第一次启动的杀毒扫描、DLL 加载不算在内。
- **热扫** ：冷扫后一般连续扫三次取平均。
- **计时** ：只用进程外测的墙钟时间，工具自己报的用时不计分。

访问时间这一条想重点讨论。测试时，访问时间更新是开着的：

- Linux 默认挂载 relatime；
- Windows 10 起由系统托管，桌面机上一般是开的；
- macOS 也默认更新。

扫描读目录时，文件系统要回写目录的访问时间（NTFS 写在 MFT 记录里，ext4 写在 inode 里），冷扫会多出一批写盘。low 体量（100 万文件）实测，开着比关着的冷扫用时（各扫描器的中位数）：

 ![02-atime](https://h2cdn.appinn.me/original/3X/b/7/b7546ee3b7f64c97fc1c780f4c7b80621c2c1132.jpeg)

日常电脑上它是开着的，用户扫盘前也不会特意去关，所以关掉再测，测出来的冷扫会比用户实际碰到的快。为了让每个工具付同样的代价：

- 每个工具冷扫前，整棵树的访问时间都调回两天前，越过 relatime 的 24 小时门槛，保证每个工具都要自己触发这批写入；
- Windows 上每个工具用一份新解出来的测试盘。

需要说明，这条规则对 SpaceKnife 有利。内存盘上的对照里，开着访问时间，gdu、dua、WinDirStat 冷扫多出 5.4–6.3 秒，SpaceKnife 只多约 0.3 秒，它扫描时基本不触发访问时间更新。我认为开着测才符合日常使用，但这也是最可能被质疑的一条，欢迎讨论。

## 结果核验

只用工具自己输出的数字，从不拿标准答案补位：

- 总大小不低于标准答案的 75%；
- 报了文件数的，不低于 90%；
- 报了顶层目录的，每个区域都要报出大小，且不低于 75%。

少报说明漏扫，成绩作废。多报（跟进了链接、重复计数）只标警告，因为多扫只会更慢，占不到便宜。每个命令行工具的原始输出都和结果一起保存；只报总数的工具标「只核对大小」，没有任何输出的图形界面工具标「未核验」。

## NTFS MFT：单独一个专栏

直接读 NTFS 的 $MFT 会比目录遍历快得多，但比的不是同一件事。所以普通榜要求走目录遍历，读 MFT 的模式单独排。

为了发现普通榜里偷读 MFT 的情况，同一份数据在 NTFS 和没有 MFT 的 ReFS 上各跑一次，两个迹象同时出现才判：

1. ReFS 对它明显比对别的工具慢（ReFS / NTFS 耗时比超过所有扫描器中位数的 2 倍）；
2. 它在 NTFS 上冷扫和热扫几乎一样快（冷 ÷ 热 ≤ 1.3）：读 MFT 每次都要读完整张表，冷热没区别。

只中第 1 条的，只是「在 ReFS 上偏慢」，比如每个文件都单独查询一次的工具，会标出来但不处罚。

MFT 专栏目前的冷扫秒数：

 ![03-mft](https://h2cdn.appinn.me/original/3X/3/d/3d5cf87e5521da7b1c27afd4d3c08f7239a07e58.jpeg)

| # | 扫描器 | 综合 | 100 万 | 500 万 | 900 万 | 1400 万 |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | SpaceKnife 0.3.4 | 100.0 | 3.57 | 15.6 | 27.6 | 31.2 |
| 2 | WizTree | 37.0 | 6.74 | 35.4 | 62.9 | 80.1 |
| 3 | WinDirStat | 35.5 | 7.75 | 40.0 | 74.4 | 80.5 |

SpaceKnife 0.3.5 还没有 MFT 成绩。SpaceKnife 的 MFT 扫描一旦退回普通遍历，成绩直接作废。

## 计分

- **平台内相对分** ：该平台最快用时 ÷ 自己的用时 × 100，硬件快慢在这一步就约掉了。冷扫、热扫各算一次。
- **均衡指数** ：同时看用时和资源，跑得快但把 CPU 和磁盘压得更满的，不会因此占便宜。
  - CPU 时间 = 进程树的 CPU 时间 ÷ 逻辑核数；
  - IO 时间 = 读盘次数 ÷ 该平台测得的随机读上限；
  - B = √(墙钟 × (CPU 时间 + IO 时间))，均衡指数 = 项数 ÷ B。

- **跨平台** ：每一项在工具能跑的平台上取几何平均。综合分是冷扫、热扫、均衡三项的几何平均。
- **跨体量** ：总榜只算 100 万文件及以上的体量。10 万、40 万两档几秒就扫完，计时以进程启动为主，只作参考。

 ![04-score](https://h2cdn.appinn.me/original/3X/f/b/fb1daeff63221f64a4eb5fd3e2584f13c0fc1e75.jpeg)

缺平台的处理：不扣分也不加分，只在它能跑的平台上算平均，等于把缺的平台按它自己的平均分补上。只有 Windows 版的工具不会因为没有 Linux、macOS 版吃亏，平台多的也不能靠多跑几个平台刷分。代价是平台少的工具被检验得没那么全面，所以榜上另列有效平台数。

## 当前总榜（截至 11 月 10 日）

 ![05-board](https://h2cdn.appinn.me/original/3X/6/9/69dab0adcfae53baa97bd55f4d6d91df36c9450a.jpeg)

「平台」是 low 体量的有效 / 参加数。榜单会随新的运行刷新，最新的以仓库 README 为准。

## 已知局限

**CI 的盘是限速云盘。**

- 同时发 32 个随机 4 KiB 读，每秒只有约 4 千到 2.4 万次。
- Windows x64 上单次读的延迟约 0.1 ms，但每秒读次数很快封顶在 9 千到 1 万次，同时发 1 个读和发 64 个几乎一样快。
- 所以 CI 上的冷扫更像慢盘场景：并发很深的工具只是在盘上多排队。

**换成内存盘，排名会变。** 同一棵 100 万文件的树（总大小缩到 4 GiB），冷扫秒数，两轮取第一轮。10 月 26 日测，当时诊断工作流还没有打开访问时间更新：

 ![06-disks](https://h2cdn.appinn.me/original/3X/f/c/fcda5b36367a4b48b80a3cffc2653d1de2bed1af.jpeg)

- 物理盘上和 VHDX 一样，都是 WinDirStat 最快，另外三个挤在 1 秒以内，说明偏差来自云盘的 IOPS 上限，不是 VHDX。
- 内存盘上读盘不再是瓶颈，比的是 CPU，WinDirStat 从第一变成最后。
- 真实的 NVMe 介于两者之间。

**其他局限**

- 只有 4 个逻辑核：能随核数扩展的工具，在多核桌面机上会更快。
- macOS 的冷扫不够冷：磁盘镜像的缓存清不干净，冷扫接近热扫。
- 部分图形界面工具只能用导出模式无界面运行，比如 WinDirStat 会写出每一项的 CSV，这部分时间也算在成绩里。

想看自己盘上的情况，可以用仓库里的本地套件，在自己的盘上跑同样版本的工具，成绩能提交到社区榜。目前社区榜上只有我自己电脑上的 3 块盘：1 块 NTFS 系统盘、2 块 ReFS 数据盘。

## 想听听大家的意见

1. 访问时间开着测，你们觉得合理吗？如果你的系统是关着的，也想知道是什么情况。
2. 合成数据还有哪些真实盘上常见、但没覆盖到的结构？比如云盘占位文件、OneDrive 这类按需下载的文件、压缩或加密的文件。
3. CI 上只有限速云盘，有没有更好的办法测出本地 NVMe 上的表现？
4. 还想看哪些工具加入？需要能在命令行或无界面下运行、能读出结果，许可证也要允许在 Windows Server 上用。

觉得规则、参数或测试数据对哪个工具不公平，欢迎回帖，或者到 GitHub 开 issue。

方法说明：[Disk-Scan-Benchmark/docs/METHODOLOGY.md at main · DuskXi/Disk-Scan-Benchmark · GitHub](https://github.com/DuskXi/Disk-Scan-Benchmark/blob/main/docs/METHODOLOGY.md)  
本地套件：[Disk-Scan-Benchmark/docs/LOCAL\_SUITE.md at main · DuskXi/Disk-Scan-Benchmark · GitHub](https://github.com/DuskXi/Disk-Scan-Benchmark/blob/main/docs/LOCAL_SUITE.md)
