From 7b3b0ef592eeb5b36f4b2da528ba7b6aabc0a6ac 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: Sat, 25 Jul 2026 08:23:01 +0800 Subject: [PATCH] =?UTF-8?q?perf(otf):=20buildSubsetGids=20=E7=94=A8=20gidT?= =?UTF-8?q?oNewGid=20Map=20=E6=9B=BF=E4=BB=A3=20indexOf=20O(n)=20=E6=9F=A5?= =?UTF-8?q?=E6=89=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit subsetOTF 的 buildSubsetGids 原 `cpToNewGid.set(cp, subsetGids.indexOf(gid))` 对每个 codepoint 线性遍历 subsetGids 找索引,O(N×M),全字符集场景退化为 O(N²)。 改用 gidToNewGid Map(gid→新gid)在 push 时记录,O(1) 查找;同时替代 seenGid Set(gidToNewGid.has 等价)。 基准测试 OTF 用例(小字集)SSIM 全一致、输出字节 IDENTICAL(otf-五个汉字 26536/15024、otf-思源 19188/10664、otf-白狐千字文 35896)。算法缺陷修复, 大字集 OTF 子集化(完整 CJK OTF)有实质收益。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/otf-subset.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/font_util/otf-subset.ts b/backend/font_util/otf-subset.ts index e5b0429..c9771fb 100644 --- a/backend/font_util/otf-subset.ts +++ b/backend/font_util/otf-subset.ts @@ -142,7 +142,9 @@ function buildSubsetGids( const { fmt4Off, fmt12Off } = selectCmapSubtables(dv, cmapOff); /** origGid 集合(保持插入顺序,新 gid = 数组 index) */ const subsetGids: number[] = [0]; - const seenGid = new Set([0]); + /** gid → 新 gid(subsetGids 索引),替代 subsetGids.indexOf(gid) 的 O(n) 线性查找。 + * 原 indexOf 对每个 codepoint 遍历 subsetGids,O(N×M);全字符集场景退化为 O(N²)。 */ + const gidToNewGid = new Map([[0, 0]]); const cpToNewGid = new Map(); for (const cp of codePoints) { if (cpToNewGid.has(cp)) continue; @@ -150,11 +152,13 @@ function buildSubsetGids( if (cp < 0x10000 && fmt4Off >= 0) gid = lookupFormat4(dv, fmt4Off, cp); if (gid === 0 && fmt12Off >= 0) gid = lookupFormat12(dv, fmt12Off, cp); if (gid === 0) continue; /** 字体无此字 */ - if (!seenGid.has(gid)) { - seenGid.add(gid); + let newGid = gidToNewGid.get(gid); + if (newGid === undefined) { + newGid = subsetGids.length; subsetGids.push(gid); + gidToNewGid.set(gid, newGid); } - cpToNewGid.set(cp, subsetGids.indexOf(gid)); + cpToNewGid.set(cp, newGid); } return { subsetGids, cpToNewGid }; }