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… 未变。
167 lines
7.5 KiB
C
167 lines
7.5 KiB
C
/* 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;
|
||
}
|