Files
MeowOS-For-R3P2/patch_v2/reader_app/test/shelf_bench_ondev.c
T
AskaEth 58f2d9bff4 perf(reader): M32 r2 书架索引改 level-1 校验 — 真机冷缓存首开 3.47s→0.13–0.16s(r1 全目录校验被真机证伪)
r1 用宿主 1.1µs/stat + M28 的**热** stat 150µs 外推「434 次≈65ms」,真机 drop_caches 后
实测是 3.15s(7.26ms/次),与 readdir 的 3.0s 同量级:全目录校验的索引命中冷启动仍要
3.06s,只比全扫快 0.2s。「3.0s→0.15s」在冷启动不成立。按任务 1 自己定的判据
(stat 接近 1s 就退回 level-1)改为只校验 Books 根 + 每个一级目录。

真机冷缓存实测(exFAT /usr/data/mnt/sd_0,434 目录/646 文件/595 本;sync + drop_caches;
探针走 /proc/uptime,UI 走 meow_ui_time_ms):
- 索引命中首开:**127 / 138 / 157ms**(复测 147ms),热 dentry 107–120ms;
- 全量扫描首开:**3428 / 3477ms**(复测 3711ms),热 dentry 不变(readdir 不吃 page cache);
- 探针冷分项:索引载入 20–30ms;载入+rdprog×595 70–120ms;4 个目录 stat 0–30ms;
- 从阅读页返回书架 29–31ms(M29 语义未变)。
→ 冷启动 22–26 倍提升,方案值得做;索引实测 **63,579B**(v2;r1 全目录 97,490B)。

实现(格式 v2,magic MEOWIDX2 / version 2):
- writer 回调过滤 rel 含 '/' 的目录:只落 root + 一级目录的 mtime;书/大小/格式记录仍全量;
  level-2+ 不落盘、载入不 stat。载入仍然 0 次 opendir/readdir(strace:0 getdents64,
  真实库 4 次 stat;宿主模拟库 11 次)。
- 校验记录里出现 level-2+ rel 视为坏档;sum(books_here) 改为「≤ nbooks」合理性上界。
- fail-open/原子写/CRC/安全加固/ts 不缓存全部保留(r1 已过评审的部分未动)。

失效矩阵(报文逐条写清):
自动(根 mtime):Books 根下直接增/删/改名书;一级目录新增/删除/改名。
自动(一级目录 mtime):一级目录里直接增/删/改名书、新建子目录。
必须刷新:level-2 及更深处的任何变化(只改深层目录 mtime)。刷新=删索引+全扫。
真机 exFAT mtime 实测 **1 秒**(tm_nsec=0;同秒连续增删 mtime 不变)。我复测 10 轮
「目录刚改过就扫描、同秒再改」→ 9/10 仍命中(陈旧),与评审 29/30 一致;sleep 跨秒后
全部正常失效。头注释已把「10ms」改为「1 秒」,并写明「不在同一秒刻度的变更靠索引失效,
同秒二次变更需刷新」。

bench 修复(P3-3):MEOW_BENCH_IDX=only —— **不预先 load/scan**,直接进书架量生产首开;
miss —— 先删索引再进书架。两者配合 drop_caches 即冷启动口径。旧行为(先 bench_index/
bench_scan 焐热)保留为默认模式。新增 test/shelf_index_probe_mipsel(write/scan/load/
loadts/statpaths/mtime/size)用于真机分项与 mtime 粒度验证,不进发行包。

测试:shelf_index_smoke 170 pass(新增:level-3/level-2 内变更「索引仍命中且先看到旧
列表 → 刷新后可见」、新建 level-2 目录自动失效、一级目录直接删书自动失效、v2 ndirs=
根+一级数);ASan+UBSan 干净;build_reader.sh 0 warning,core 6846 / seg 19 / chapter 71 /
shelf_group 63 / index 170 / font 14 / loaders PASS。真机 6 场景逐项验证:一级直接增/
新建子目录/一级改名/根下删书/一级直接删书 = 自动失效;level-3 深处新增 = 仍命中(需刷新)。

交付:app.json 0.1.5/code6 → **0.1.6/code7**;reader md5 5c848c99… 1,711,988B;
mpk md5 10d676f5… 814,574B;未签名不 push。设备验证后已清理 /tmp 与 m32probe,
真实 MeowData 的 shelf.idx 已删(app 自己的章节 .idx 未动),Books 434/646 不变,
已装 0.1.4 md5 4036b889… 未变。
2026-11-10 18:49:48 +08:00

167 lines
7.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test/shelf_bench_ondev.c — 真机耗时基准(不进发行包,不参与 mpk)。
*
* 目的:在**同一台设备、同一字库/主题环境**下量 App 的同步耗时,用来给书架缓存 /
* 行自绘 / 开书路径做前后对比(宿主数字不算数)。
*
* 只调 App/SDK 公共入口(与 app.c 的调用顺序一致),不复制任何内部实现:
* meow_ui_init → 建三页 → push 书架(= 首次进书架,含扫描)
* → push/pop 阅读页 ×N(= 从阅读页返回书架)
* → shelf_on_swipe ×N(= 书架滑动,含 widget_draw + flush 上屏)
* → reader_open + push 阅读页(= 点书开读首屏)、翻页
*
* 用法(设备):shelf_bench_ondev [book1 [book2 ...]]
* 输出:`BENCH <key> <min>-<max> (avg ...)` 行,ms 为毫秒(设备时钟 10ms 分辨率,
* 单次量用多轮取样给区间;批量操作用「总时长/次数」给出平均值)。
*/
#include "reader_internal.h"
#include "app/shelf_model.h"
#include "app/shelf_index.h"
#include "meow_ui_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
static uint64_t now_ms(void) { return meow_ui_time_ms(); }
/* M32:索引命中/未命中的耗时(不含 UI)。先在本次运行开头测:
* - index_load:读到上次运行写下的索引(首次运行 miss);
* - scan_save :全量扫描 + 落盘(生产「无索引首次进入」)。
* 之后 shelf_on_enter 会走生产路径:有索引=命中(默认),MEOW_BENCH_IDX=miss 时先删索引。 */
static void bench_index(void)
{
static shelf_book books[SHELF_MAX_BOOKS];
shelf_scan_report rep;
uint64_t t0 = now_ms();
int hit = shelf_index_load(READER_SD_ROOT "/Books", books, SHELF_MAX_BOOKS, NULL, &rep);
uint64_t t1 = now_ms();
printf("BENCH index_load_ms %llu (hit=%d n=%d trunc=%d skip=%d)\n",
(unsigned long long)(t1 - t0), hit, rep.nbooks, rep.truncated, rep.depth_skipped);
t0 = now_ms();
shelf_idx_writer *w = shelf_index_writer_open(READER_SD_ROOT "/Books");
int n = shelf_scan_books_ex(READER_SD_ROOT "/Books", books, SHELF_MAX_BOOKS, NULL, &rep,
w ? shelf_index_writer_dir : NULL, w);
int ok = w ? shelf_index_writer_commit(w, books, n, &rep) : 0;
t1 = now_ms();
char ip[500];
struct stat st;
long long isz = (shelf_index_path(READER_SD_ROOT "/Books", ip, sizeof ip) == 0 &&
stat(ip, &st) == 0) ? (long long)st.st_size : -1;
printf("BENCH scan_save_ms %llu (n=%d commit=%d index_bytes=%lld)\n",
(unsigned long long)(t1 - t0), n, ok, isz);
fflush(stdout);
}
static void bench_scan(void)
{
static shelf_book books[SHELF_MAX_BOOKS];
shelf_scan_report rep;
uint64_t t0 = now_ms();
int n = shelf_scan_books(READER_SD_ROOT "/Books", books, SHELF_MAX_BOOKS, NULL, &rep);
uint64_t t1 = now_ms();
printf("BENCH scan_ms %llu (n=%d truncated=%d depth_skipped=%d root_missing=%d)\n",
(unsigned long long)(t1 - t0), n, rep.truncated, rep.depth_skipped, rep.root_missing);
fflush(stdout);
}
int main(int argc, char **argv)
{
meow_ui_opts o = { 480, 720 };
if (meow_ui_init(&o) != 0) { printf("BENCH init_fail\n"); return 1; }
meow_theme_add_listener(NULL);
/* M32 r2:MEOW_BENCH_IDX 三种模式
* 未设置 —— 先跑 bench_index()+bench_scan()(会焐热缓存,只适合看热值);
* only —— **不预先 load/scan**:直接进书架,量生产首次进入(配合 drop_caches 看冷启动);
* miss —— 同上,且先删索引:量生产「无索引全量扫描」首次进入(冷启动对比)。
* 冷/热对照用法:sync; echo 3 > /proc/sys/vm/drop_caches; MEOW_BENCH_IDX=only ./bench */
const char *eidx = getenv("MEOW_BENCH_IDX");
int idx_only = eidx && strcmp(eidx, "only") == 0;
int idx_miss = eidx && strcmp(eidx, "miss") == 0;
if (!idx_only && !idx_miss) {
bench_index(); /* 索引命中/未命中 + 扫描落盘(不含 UI;会焐热缓存) */
bench_scan(); /* 纯扫描基线(不建 UI) */
} else {
char ip[500];
struct stat st;
int have = (shelf_index_path(READER_SD_ROOT "/Books", ip, sizeof ip) == 0 &&
stat(ip, &st) == 0);
printf("BENCH index_mode %s prewarm=0 index_present=%d index_bytes=%lld\n",
idx_miss ? "miss" : "only", have, have ? (long long)st.st_size : -1LL);
if (idx_miss) shelf_index_remove(READER_SD_ROOT "/Books");
fflush(stdout);
}
shelf_view_create();
reader_view_create();
settings_view_create();
/* ① 首次进书架(only/miss 模式下这就是生产首次进入;配合 drop_caches 为冷启动值) */
uint64_t t0 = now_ms();
meow_view_page_push(ACT_SHELF);
uint64_t t1 = now_ms();
printf("BENCH shelf_first_enter_ms %llu\n", (unsigned long long)(t1 - t0));
/* ② 从阅读页返回书架:push 阅读页(不等书)→ pop 回书架;连做 5 次给区间 */
unsigned long long rmin = 0, rmax = 0, rsum = 0;
for (int i = 0; i < 5; i++) {
meow_view_page_push(ACT_READER);
uint64_t a = now_ms();
meow_view_page_pop();
uint64_t b = now_ms();
uint64_t d = b - a;
if (i == 0 || d < rmin) rmin = d;
if (i == 0 || d > rmax) rmax = d;
rsum += d;
}
printf("BENCH shelf_return_ms %llu-%llu (avg %llu)\n",
rmin, rmax, rsum / 5);
/* ③ 书架滑动:每步 62px(与 survey 同口径:scroll 62 + widget_draw + flush) */
for (int batch = 0; batch < 3; batch++) {
uint64_t a = now_ms();
for (int i = 0; i < 30; i++) shelf_on_swipe(MEOW_SWIPE_UP, 0, 62);
uint64_t b = now_ms();
printf("BENCH shelf_scroll_step_ms %.1f (batch %d: 30 steps %llu ms)\n",
(double)(b - a) / 30.0, batch, (unsigned long long)(b - a));
for (int i = 0; i < 30; i++) shelf_on_swipe(MEOW_SWIPE_DOWN, 0, 62); /* 回到顶部 */
}
fflush(stdout);
/* ④ 开书首屏 / 翻页(argv 给书路径;不给则跳过) */
for (int i = 1; i < argc; i++) {
uint64_t a = now_ms();
int rc = reader_open(argv[i]);
uint64_t b = now_ms();
if (rc != 0) { printf("BENCH open_fail %s\n", argv[i]); continue; }
meow_view_page_push(ACT_READER);
uint64_t c = now_ms();
printf("BENCH open_ms %llu (reader_open %llu + page_push %llu) %s\n",
(unsigned long long)(c - a), (unsigned long long)(b - a),
(unsigned long long)(c - b), argv[i]);
/* 翻页:reader_page_key 置 pending → reader_on_tick 消费(同步段=turn+refill+redraw)
* 轮数由 MEOW_BENCH_TURNS 控制(0=不翻页 → 用于「开书不翻页进度文件不变」回归)。 */
const char *et = getenv("MEOW_BENCH_TURNS");
int nturns = et && et[0] ? atoi(et) : 4;
unsigned long long tmin = 0, tmax = 0, tsum = 0;
for (int p = 0; p < nturns; p++) {
reader_page_key(1);
uint64_t x = now_ms();
reader_on_tick(now_ms());
uint64_t y = now_ms();
uint64_t d = y - x;
if (p == 0 || d < tmin) tmin = d;
if (p == 0 || d > tmax) tmax = d;
tsum += d;
}
printf("BENCH turn_ms %llu-%llu (avg %llu) %s\n", tmin, tmax, nturns ? tsum / nturns : 0, argv[i]);
meow_view_page_pop();
fflush(stdout);
}
meow_ui_shutdown();
return 0;
}