/* 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 - (avg ...)` 行,ms 为毫秒(设备时钟 10ms 分辨率, * 单次量用多轮取样给区间;批量操作用「总时长/次数」给出平均值)。 */ #include "reader_internal.h" #include "app/shelf_model.h" #include "app/shelf_index.h" #include "meow_ui_internal.h" #include #include #include #include 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; }