From f7a2a4dc86139f39126ca9398e9903b47d1cbb87 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: Thu, 23 Jul 2026 20:45:08 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub-reachable):=20collectChainRefs=20form?= =?UTF-8?q?at3=20=E7=9F=AD=E8=B7=AF=20+=20=E5=8E=BB=E9=97=AD=E5=8C=85/?= =?UTF-8?q?=E4=B8=AD=E9=97=B4=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 固定点迭代中 collectChainRefs 高频调用(初夏纯标点 840 次/call)。format3 原实现: 读完全部 backtrack/input/lookahead coverage 把 gid 收进 allGids 中间数组,任一不在 子集才置 triggerable=false,且每次调用分配 readCovGids 闭包 + allGids 数组。 优化:遇到不在子集的 gid 立即短路返回(triggerable=false 时 contextGids/refs 本就不 收集,后续 coverage 无需再读);去掉闭包与 allGids,triggerable 时 gid 直接 add 进 contextGids。逻辑完全等价(triggerable=false 时原代码也不 add contextGids)。 基准 29 用例 SSIM/ink/字节全无回归。初夏纯标点 2.34→1.86ms(-20%),FiraCode 3.7→3.4ms。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/gsub-reachable.ts | 31 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/font_util/gsub-reachable.ts b/backend/font_util/gsub-reachable.ts index 6df31f8..03f336a 100644 --- a/backend/font_util/gsub-reachable.ts +++ b/backend/font_util/gsub-reachable.ts @@ -302,29 +302,36 @@ function collectChainRefs( } } else if (format === 3) { /** format3: 显式 coverage 数组 + SubstLookupRecords。 - * 三个 coverage 数组(backtrack/input/lookahead)的 gid 须全在子集才触发。 */ + * 三个 coverage 数组(backtrack/input/lookahead)的 gid 须全在子集才触发。 + * 优化:triggerable 一旦为 false 即短路(无需继续遍历后续 coverage,原代码无短路会读完全部); + * 去掉原 readCovGids 闭包 + allGids 中间数组(collectChainRefs 在固定点迭代中高频调用, + * 初夏纯标点 840 次/call),triggerable 时 gid 直接 add 进 contextGids,省 allGids 中转。 */ let p = off + 2; - const allGids: number[] = []; let triggerable = true; - const readCovGids = (cnt: number): boolean => { + const readCovGidsChecked = (cnt: number): void => { for (let k = 0; k < cnt; k++) { const covGids = readCoverageGids(r, off + r.u16(p + k * 2), covCache); for (const g of covGids) { - allGids.push(g); - if (!inSubset(g)) triggerable = false; + if (!inSubset(g)) { + triggerable = false; + return; + } + contextGids.add(g); } } p += cnt * 2; - return true; }; const backtrackCount = r.u16(p); p += 2; - readCovGids(backtrackCount); - const inputCount = r.u16(p); p += 2; - readCovGids(inputCount); - const lookaheadCount = r.u16(p); p += 2; - readCovGids(lookaheadCount); + readCovGidsChecked(backtrackCount); + if (triggerable) { + const inputCount = r.u16(p); p += 2; + readCovGidsChecked(inputCount); + } + if (triggerable) { + const lookaheadCount = r.u16(p); p += 2; + readCovGidsChecked(lookaheadCount); + } if (triggerable) { - for (const g of allGids) contextGids.add(g); const substCount = r.u16(p); for (let k = 0; k < substCount; k++) refs.add(r.u16(p + 2 + k * 4 + 2)); }