From aa2818b13e9de5fa33e899c03a096665883314e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Fri, 24 Jul 2026 10:38:27 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub):=20gidLookup=20=E6=94=B9=20Int32Arra?= =?UTF-8?q?y=EF=BC=8C=E5=A4=A7=E6=95=B0=E7=BB=84=20fill=20=E5=BF=AB=203?= =?UTF-8?q?=C3=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GidLookup(原gid→新gid 查找表)原用 number[].fill(-1), TypedArray 的 fill 是 native memset,比 number[].fill 快约 3×(35515 项:219μs→67μs)。 初夏明朝 subsetGids 仅十余个但 maxOrigGid 达 3.5 万(reachable 的高 gid target), number[].fill(-1) 耗时 ~200μs,占 subsetGSUB 总耗时 ~47%。Int32Array.fill 降至 ~67μs,索引访问速度实测与 number[] 一致(FiraCode 密集查询场景同 8μs)。 初夏 rewrite 0.663→0.501ms(-24.5%),总 fontSubset -9.2%;FiraCode 略快 -2.8%。 GSUB 输出字节级一致(cmp 通过),30 项基准 SSIM 零变化。 推翻记忆 gsub-subset-perf-coverage-cache 中「number[] 比 Int32Array 快 2×」结论 (旧 V8 或不同测试条件),当代 V8 下两者访问速度相同,TypedArray fill 显著更快。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/gsub-subset.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index d3eea7f..31c3df3 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -75,9 +75,12 @@ const EMPTY_GIDS: number[] = []; * origToNew 是 Map,每次 .get() 哈希查询开销大;coverage 解析对每个原 gid 都查一次, * 密度极高。构建索引数组(下标=原gid,值=新gid,-1 表示不在子集)后,查询退化为数组索引, * 比 Map.get 快数倍(subsetGSUB readCoverageRemapped 的主热点)。 - * 用普通 number[] 而非 Int32Array——实测 V8 下 number[] 索引访问比 TypedArray 快约 2×。 + * 用 Int32Array——TypedArray.fill 是 native memset,比 number[].fill 快约 3× + * (初夏明朝 subsetGids 仅十余个但 maxOrigGid 达 3.5 万,number[].fill(-1) 耗时 ~200μs, + * Int32Array.fill 仅 ~67μs,省 subsetGSUB 总耗时 ~6%)。索引访问速度与 number[] 实测一致。 + * 越界访问(gid >= length)返回 undefined,>= 0 判定为不在子集,语义正确。 */ -type GidLookup = number[]; +type GidLookup = Int32Array; /** 读取 Coverage 表,返回覆盖的原 gid 列表(保持顺序)。 * 传入 cache 时按 coverage 绝对偏移缓存解析结果(同一 off 复用同一数组实例)。 @@ -1179,7 +1182,7 @@ export function subsetGSUB( * 下标=原gid,值=新gid,-1 表示不在子集。容量覆盖出现的最大原 gid。 */ let maxOrigGid = 0; for (const g of origToNew.keys()) if (g > maxOrigGid) maxOrigGid = g; - const gidLookup: GidLookup = new Array(maxOrigGid + 1).fill(-1); + const gidLookup: GidLookup = new Int32Array(maxOrigGid + 1).fill(-1); for (const [g, n] of origToNew) gidLookup[g] = n; /** ---- GSUB Header ---- */