字体:find_font_rec/resolve_font 从 reader.c 抽到 app/font_resolve.c 并加进程内记忆化。 只缓存成功路径;命中后仍做一次 access() 校验(字库被删即重扫);没找到不缓存, 用户之后拷入的字库下次开书即可找到。顶栏「刷新」额外调 font_cache_invalidate(), 给「新拷入字库、旧字库仍在」一条显式失效路径。宿主实测:35 目录树 walk 0.026~0.54ms (=300+ 次系统调用)vs 命中 0.001ms(1 次 access);开书路径每次省一次完整 walk。 徽标:副题格式化抽成 shelf_fmt_sub()(扫描与「刚读完置顶」共用一份实现), promote_just_read() 改 ts 后立即重算这本的 sub/sub_folder,「· 读」不再等下次全量刷新。 回归:test_reader_core 6846、shelf_group_smoke 63(+8)、font_resolve_smoke 14 全绿; build_reader.sh 0 warning;扫描缓存/进度语义未改。app.json 0.1.3/code4 → 0.1.4/code5。
110 lines
3.4 KiB
C
110 lines
3.4 KiB
C
/* test/font_resolve_bench.c — M31 字体路径记忆化耗时对比(宿主,不依赖设备)。
|
|
*
|
|
* 同一棵树两种口径:
|
|
* walk = 每次都重扫(= 加缓存前的 resolve_font 行为),由 font_cache_invalidate() 强制;
|
|
* hit = 缓存命中 + access() 校验(= 加缓存后开书路径)。
|
|
* 用法:./font_resolve_bench_host [目录] [迭代数],不传目录则先造一棵 35 目录的树。
|
|
*/
|
|
#include "font_resolve.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
static void mkdirs(const char *path)
|
|
{
|
|
char t[512];
|
|
snprintf(t, sizeof t, "%s", path);
|
|
for (char *p = t + 1; *p; p++) {
|
|
if (*p == '/') { *p = 0; mkdir(t, 0755); *p = '/'; }
|
|
}
|
|
mkdir(t, 0755);
|
|
}
|
|
|
|
static void write_file(const char *path, const char *body)
|
|
{
|
|
FILE *f = fopen(path, "wb");
|
|
if (!f) return;
|
|
fputs(body, f);
|
|
fclose(f);
|
|
}
|
|
|
|
static void make_fonts_tree(const char *root, int ndirs, int files_per_dir)
|
|
{
|
|
char p[512];
|
|
for (int i = 0; i < ndirs; i++) {
|
|
snprintf(p, sizeof p, "%s/family_%02d/sub", root, i);
|
|
mkdirs(p);
|
|
for (int j = 0; j < files_per_dir; j++) {
|
|
snprintf(p, sizeof p, "%s/family_%02d/readme_%d.txt", root, i, j);
|
|
write_file(p, "x");
|
|
}
|
|
snprintf(p, sizeof p, "%s/family_%02d/sub/Font%02d.ttf", root, i, i);
|
|
write_file(p, "x");
|
|
}
|
|
}
|
|
|
|
static double ms_now(void)
|
|
{
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6;
|
|
}
|
|
|
|
typedef struct stat_res { double min, avg, max; } stat_res;
|
|
|
|
static stat_res run(const char *root, int iters, int invalidate_each)
|
|
{
|
|
stat_res r = { 1e9, 0, 0 };
|
|
double sum = 0;
|
|
for (int i = 0; i < iters; i++) {
|
|
if (invalidate_each) font_cache_invalidate();
|
|
double t0 = ms_now();
|
|
(void)font_resolve(root);
|
|
double dt = ms_now() - t0;
|
|
if (dt < r.min) r.min = dt;
|
|
if (dt > r.max) r.max = dt;
|
|
sum += dt;
|
|
}
|
|
r.avg = iters ? sum / (double)iters : 0;
|
|
return r;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
const char *root = (argc > 1) ? argv[1] : NULL;
|
|
int iters = (argc > 2) ? atoi(argv[2]) : 200;
|
|
char tmpbuf[512] = { 0 };
|
|
|
|
if (!root) {
|
|
const char *base = getenv("TMPDIR");
|
|
if (!base || !base[0]) base = "/tmp";
|
|
snprintf(tmpbuf, sizeof tmpbuf, "%s/fontbench_XXXXXX", base);
|
|
if (!mkdtemp(tmpbuf)) { printf("mkdtemp 失败\n"); return 2; }
|
|
make_fonts_tree(tmpbuf, 35, 3);
|
|
root = tmpbuf;
|
|
}
|
|
/* 预热(首轮含 dentry 冷缓存,不计入) */
|
|
font_cache_invalidate(); font_resolve(root);
|
|
font_resolve(root);
|
|
|
|
const char *p = font_resolve(root);
|
|
printf("root=%s\n", root);
|
|
printf("font=%s\n", p ? p : "(none)");
|
|
stat_res w = run(root, iters, 1); /* 每次失效 = 旧行为:每次开书重 walk */
|
|
stat_res h = run(root, iters, 0); /* 缓存命中 + access 校验 = 新行为 */
|
|
printf("walk (每次重扫): min=%.3f avg=%.3f max=%.3f ms\n", w.min, w.avg, w.max);
|
|
printf("hit (缓存+access): min=%.3f avg=%.3f max=%.3f ms\n", h.min, h.avg, h.max);
|
|
if (h.avg > 0) printf("提速: %.1fx\n", w.avg / h.avg);
|
|
|
|
if (tmpbuf[0]) {
|
|
char cmd[600];
|
|
snprintf(cmd, sizeof cmd, "rm -rf '%s'", tmpbuf);
|
|
if (system(cmd) != 0) { /* 忽略 */ }
|
|
}
|
|
return 0;
|
|
}
|