背景
filemap 读路径靠 xarray 迭代收集页缓存里的 folio:filemap_get_read_batch() 服务常规读,filemap_get_folios_contig() 服务连续段收集。两个函数的循环都把边界检查放在迭代器前进之后:拿到范围内最后一个 folio 之后,还要 xas_next() 或 xas_advance() 再多走一步,才发现已经越界。
边界检查后移多走一轮
┌──────────────────────────────────────┐
│ 拿到最后一个所需 folio │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 迭代器照旧前进一步 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 才发现越界退出,多走一轮 xarray 查找 │
└──────────────────────────────────────┘
问题
- 小读通常只取一个 folio,也要多做一次无效的迭代器前进与边界判断
- 大读在收尾前总要多走一轮 xarray 查找
- 快路径上多出无谓的查找与分支
方案
把边界检查移到迭代器前进之前:拿到范围内最后一个 folio 就收工,不再多走那一步。
边界检查移到前进之前
┌────────────────────────────┐
│ 拿到最后一个所需 folio │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ 先查边界,到位即收工 │
└─────────────┬──────────────┘
▼
┌────────────────────────────┐
│ 快路径不再有多余查找与分支 │
└────────────────────────────┘
filemap_get_read_batch() 里,进循环前先查 index 越界即返回;folio 入批后若 xas_advance() 已到达或越过 max,直接退出循环,循环条件里原来的越界检查随之删掉。filemap_get_folios_contig() 用同一手法:入口先查 start 越界,循环条件里的边界判断移到 xas_next() 之前。
xas_next() 在 XAS_RESTART 状态下不更新 xa_index,所以 put 后重试的路径不会因边界前移而误判。
收益
4k 读测试下的函数级开销:
| 指标 | Before | After |
|---|---|---|
| filemap_get_read_batch() 开销占比 | 2.91% | 2.53% |