92 lines
9.3 KiB
Markdown
92 lines
9.3 KiB
Markdown
# 本机验证结果
|
||
|
||
测量日期:2026-10-07 至 2026-10-08。Windows x64,C: 和 D: 合计约 327 万条文件/目录记录。数字会随本机文件变化、系统负载和缓存状态波动。
|
||
|
||
## 相对路径片段查询优化(2026-10-08)
|
||
|
||
`foo\bar` 没有盘符,也没有通配符,因此此前的绝对路径通配符优化没有覆盖它。修复前依然逐项重建完整路径;同样 17 个结果,原生命令行约 3779 ms,旧包装器约 253 ms。现在根据片段的最后一个组件在名称索引中找候选项,验证前面的目录组件,再遍历匹配目录的子树。`foo\bar` 允许 `afoo\barista` 这样的原有子串匹配,也包含匹配目录下的后代;不会把查询错误地改成组件精确匹配。
|
||
|
||
四次运行排除首次后的三次中位数,索引已在内存,GUI 同等查询每次清除上次结果缓存。命令行总耗时包含进程启动、IPC 和接收完整 stdout;GUI 列只测服务,不含输入防抖、绘制和图标。脚本:`tests/relative_path_benchmark.py`;原始数据:`test-output/relative-path-before.json`、`relative-path-after.json`。
|
||
|
||
| 查询 | 原生修复前总耗时 | 旧包装器本轮总耗时 | 原生修复后总耗时 | GUI 同等查询服务耗时 | 相同结果数 |
|
||
|---|---:|---:|---:|---:|---:|
|
||
| `foo\bar` | 3779.2 ms | 249.0 ms | **17.3 ms** | 1.374 ms | 17 |
|
||
| `Windows\Logs` | 3437.1 ms | 218.9 ms | **18.9 ms** | 1.490 ms | 364 |
|
||
| `Windows\Logs\` | 3382.0 ms | 247.7 ms | **14.7 ms** | 1.340 ms | 363 |
|
||
| `Windows\System32\drivers\etc` | 3423.7 ms | 235.9 ms | **17.6 ms** | 0.602 ms | 7 |
|
||
|
||
四种查询的旧包装器、原生 CLI 和 GUI 同等查询均逐路径比较一致。这里仍比较本机旧包装器,不代表测出了 Everything 搜索引擎本身的耗时或内部实现。
|
||
|
||
该方案复用已有名称前缀索引、父目录关系和子项链表,不增加常驻索引或重复的完整路径缓存。只有单组件加结尾斜杠(如 `foo\`)需要做名称后缀预筛。支持每个 OR 分支带正向路径片段的查询、附加筛选条件、大小写、Unicode、目录改名/移动和实时新增名称;无法安全缩小范围的查询继续通用匹配。2386 项核心断言及现有 GUI/图标测试通过,其中相对路径优化用逐项完整路径匹配作独立候选筛选对照,并验证 30000 个无关名称不会触发全扫描。实际磁盘集成测试同时比较名称查询、绝对路径通配符和相对路径片段,全部通过。
|
||
|
||
## 固定目录前缀的路径通配符优化(22:51 更新)
|
||
|
||
用户报告 `C:\Windows\Logs\*.log` 明显慢于旧版。修复前,路径通配符没有使用目录范围,每次对全盘约 327 万条记录重建完整路径并匹配;GUI 还遍历了全局名称排序。新增按父文件引用号维护的子项链表,先解析固定目录前缀,再遍历对应子树;目录移动/改名、硬链接变化、快照恢复和压缩都会维护或重建此索引。
|
||
|
||
同一台电脑、内存索引已就绪,6 次运行排除首次后的 5 次中位数。原始结果见 `test-output/path-before.json`、`test-output/path-after.json`;复测脚本 `tests/path_benchmark.py`。
|
||
|
||
| 查询 `C:\Windows\Logs\*.log` | 修复前 | 修复后 |
|
||
|---|---:|---:|
|
||
| 旧 `locate.old.exe` 启动至退出 | 152.8 ms | 160.1 ms |
|
||
| 原生 `locate.exe` 启动至退出 | 2598.9 ms | 14.7 ms |
|
||
| 原生命令行查询的服务内耗时 | 2581.8 ms | 0.486 ms |
|
||
| GUI 同等查询:名称排序、全部计数、返回前 256 条,服务内耗时 | 3638.2 ms | 0.571 ms |
|
||
|
||
三种查询均返回相同的 62 个路径。GUI 同等查询通过 CLI 的 Everything 语法和名称排序选项提交,每次先用不同查询清除上次结果缓存;这里测量服务查询,不包含 GUI 的 100 ms 输入防抖、界面绘制或图标加载。命令行总耗时包含进程启动、IPC 和接收 stdout,不包含终端绘制。比较对象是本机旧包装器,不能由此推断所有查询都快于 Everything 引擎。
|
||
|
||
保留原有通配符跨子目录、大小写、OR/AND、排除条件、排序和分页语义。只有每个 OR 分支都有正向、带绝对目录前缀的路径通配符时才缩小范围;其他查询沿用通用路径。子项索引增加每行 4 字节及每个父目录一个哈希表项,不保存重复的完整路径。442 项核心断言、30 项 GUI 缓存检查、16 项图标缓存检查通过;实际磁盘集成测试同时验证名称查询与目录范围查询,包括 30 个硬链接、目录改名、文件移动、删除、Unicode 长路径和 500 文件批量创建。
|
||
|
||
以下为此前测量记录。
|
||
|
||
## 同结果的命令行比较
|
||
|
||
命令通过 Python `subprocess` 启动,接收 stdout,测量从启动到退出的总时间;不包含终端逐行绘制。每项运行 9 次,排除首次后报告 8 次中位数。原始数据:`test-output/benchmark.json`。
|
||
|
||
| 命令 | 原来的 locate.old.exe | 原生 locate.exe | 结果 |
|
||
|---|---:|---:|---|
|
||
| `python*.exe` | 68.86 ms | 9.10 ms | 同样的 66 个完整路径,集合完全一致 |
|
||
| 不存在的文件名通配符 | 68.28 ms | 8.65 ms | 都为 0 条 |
|
||
| `-n 10 "python*.exe"` | 不支持该参数,不比较 | 8.40 ms | 10 条 |
|
||
| `-n 10 "*.exe"` | 不支持该参数,不比较 | 7.61 ms | 10 条 |
|
||
|
||
这里比较的是本机旧包装器和新命令行的端到端时间,不能据此声称原生搜索引擎在所有查询上都比 Everything 引擎快。旧版 `*.exe` 输出为 9999 条;原生为 12286 条,这种不同结果规模未用于速度结论。旧版独有的 9 个 Office 更新临时路径,Win32 检查均返回 ERROR_FILE_NOT_FOUND;记录见 `test-output/exe-comparison.json`。
|
||
|
||
## 原生服务内查询时间
|
||
|
||
来自 `test-output/performance.json`。每个查询连续运行三次;“首次”表示该查询没有已完成结果缓存,磁盘索引已经在内存。
|
||
|
||
| 查询 | 服务内时间 | 说明 |
|
||
|---|---:|---|
|
||
| `-n 10 python*.exe` | 0.30–0.38 ms | 名称前缀索引,找到足够数量就停止 |
|
||
| `-bic python` | 26–28 ms | 名称子串计数,10302 条 |
|
||
| `-ic python` | 43–52 ms | 完整路径子串计数,84020 条 |
|
||
| `-ic -r '^python.*\.exe$'` | 281–309 ms | Unicode PCRE2 JIT 正则,全名称扫描 |
|
||
| 全索引按名称排序,第一页 | 首次 1282 ms;缓存约 0.2 ms | 约 320 万条匹配、返回 256 条 |
|
||
| 全索引按完整路径排序,第一页 | 首次 3972 ms;缓存约 0.2 ms | 同上 |
|
||
| 全索引按大小排序,第一页 | 首次 2325 ms;缓存约 0.4 ms | 同上 |
|
||
|
||
最明显的剩余性能差距是大范围查询的首次排序和通用正则扫描。查询缓存会在相关索引变化后失效;名称查询不会因为仅修改文件大小/时间而失效。一次采样的服务工作集约 642 MiB、私有内存约 751 MiB;包含名称、父子关系、硬链接、文件元数据和查询缓存,未达到 Everything 的内存效率。
|
||
|
||
## 实时性、恢复与正确性
|
||
|
||
- 86 项核心断言通过:UTF-8 进程代码页和窄字符文件 API、查询、Unicode、正则工作量限制、日期边界、硬链接、DOS 别名计数、路径排序、分页、取消、快照校验、压缩与部分卷可用。
|
||
- 实际磁盘测试通过:30 个长名称硬链接跨 MFT 扩展记录;目录改名、单链接删除、跨目录移动、所有链接元数据同步、超过 MAX_PATH 的 Unicode 路径。
|
||
- 常规文件更新在测试中约 0.12–0.35 秒可见;500 文件突发创建约 0.12 秒可见。大范围排序占用索引读锁时,更新发布可能推迟。
|
||
- 服务停止后创建文件,重启后约 2143 ms 可查询,验证快照恢复与 USN 补读。
|
||
- 临时 NTFS VHD 插入后测试文件约 131 ms 可查;拔出后约 1787 ms 从已发布卷中移除,其他卷保持可查询。轮询相位会影响这些数字;热插拔检测周期为 2 秒。
|
||
- 60 次并发查询、20 个客户端线程,结果一致;单次 20000 条 / 约 2.1 MB 结果传输通过。
|
||
- 100 ms 请求超时约 104 ms 返回,随后服务继续正常响应;畸形请求和普通用户重建请求被拒绝。
|
||
- 程序内 GUI 测试通过:搜索、OR 分组与类型筛选、虚拟列表翻页、单文件与跨目录多选的系统/第三方 Shell 菜单、真实中文文件重命名。渲染图在 `test-output/ui-final`,已检查布局。
|
||
|
||
GUI 验证使用程序内集成测试和实际窗口渲染;本次外部桌面自动化接口不可用,未把模拟鼠标逐项点击全部第三方菜单当作已完成的测试。网络盘和非 NTFS 卷不在支持或测试范围。
|
||
|
||
## 主要实现选择
|
||
|
||
- 原生 MFT + USN 服务维护索引,CLI/GUI 通过本机管道提交查询,只传回匹配结果。
|
||
- 名称去重、排序前缀查找、64 位三元组 Bloom 预筛、连续内存扫描;路径子串查询复用父目录匹配结果。
|
||
- 目录采用文件引用号及父行缓存,改名无需重写所有子路径;完整 NTFS 序列号避免文件记录重用串到旧文件。
|
||
- PCRE2 JIT、64 KiB 传输缓冲、服务端限量和虚拟列表分页减少不必要的计算与传输。
|
||
- 快照带 SHA-256 校验并原子替换;未完成的记录更新保留日志重放位置,避免服务重启后漏掉未解决的变化。
|
||
|
||
这些优化解决了之前 C# 适配器整批传送目录、客户端重组和筛选的主要开销;性能问题不应简单归因于 C# 这一语言本身。
|