From 98102bc3be69d06f9d3977af9f6f90c563aa8fc0 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 07:39:06 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub):=20writeChainFormat3=20coverage=20?= =?UTF-8?q?=E6=A7=BD=E6=94=B9=20writeInt16At=20=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit type6 format3 的 coverage 偏移槽原用 allHolders: number[][] + reserveOffset16 闭包模式(每槽 [0] 单元素数组 + patches push + flush 回填)。改为 writeUint16(0) 占位 + covSlotPositions 记录字节位置 + writeInt16At 直接回填(与优化329 subtable slot 同思路),消除每槽的 [0] 数组分配 + reserveOffset16 闭包。 node --prof 显示 GrowFastSmiOrObjectElements 3.7% + ArrayPrototypePush 3.2% 主来自 writeChainFormat3/serializeChainedContextSubst 的 allHolders grow。 FiraCode subsetGSUB min 0.229→0.216ms(-5.7%),端到端 avg 3.4→2.9ms(-15%), 29 用例输出字节全相同 SSIM 一致。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/gsub-subset.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index 5bb47ae..9125b11 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -1209,14 +1209,17 @@ function writeChainFormat3( parsed: { backCovs: number[][]; inputCovs: number[][]; lookCovs: number[][]; records: Array<{ seq: number; lookup: number }> }, ): void { const subStart = w.length; - const allHolders: number[][] = []; - /** 预留一个 Offset16 槽,flush 时回填 allHolders[slotIdx] 的值。 - * 必须用 IIFE 捕获当前 slotIdx——闭包直接引用 allHolders.length-1 会在 flush 时(循环已结束) - * 统一取到最后一个槽,导致所有 coverage 偏移指向同一个 coverage(FiraCode === 连字断裂的根因)。 */ + /** 每个 coverage 偏移槽的字节位置(writeUint16(0) 占位,emitCoverage 后 writeInt16At 回填)。 + * 替代旧 allHolders: number[][] + reserveOffset16 闭包模式:消除每槽的 [0] 单元素数组分配 + + * reserveOffset16 的 patches 闭包 push + flush 回填遍历(优化329 同思路,对 chain coverage slot 复用)。 + * coverage 槽数 = backCovs + inputCovs + lookCovs,一次性预分配容量避免 grow。 */ + const totalCovSlots = parsed.backCovs.length + parsed.inputCovs.length + parsed.lookCovs.length; + const covSlotPositions: number[] = new Array(totalCovSlots); + /** 占位写入 coverage 偏移槽并记录其字节位置 */ + let slotIdx = 0; const reserveCovSlot = () => { - const slotIdx = allHolders.length; - allHolders.push([0]); - w.reserveOffset16(subStart, () => allHolders[slotIdx][0]); + covSlotPositions[slotIdx++] = w.length; + w.writeUint16(0); }; w.writeUint16(3); w.writeUint16(parsed.backCovs.length); @@ -1231,15 +1234,16 @@ function writeChainFormat3( w.writeUint16(rc.lookup); } - let holderIdx = 0; + /** 写出各组 coverage 并回填偏移槽(相对 subStart) */ + let posIdx = 0; for (const cov of parsed.backCovs) { - allHolders[holderIdx++][0] = emitCoverage(w, cov); + w.writeInt16At(covSlotPositions[posIdx++], emitCoverage(w, cov) - subStart); } for (const cov of parsed.inputCovs) { - allHolders[holderIdx++][0] = emitCoverage(w, cov); + w.writeInt16At(covSlotPositions[posIdx++], emitCoverage(w, cov) - subStart); } for (const cov of parsed.lookCovs) { - allHolders[holderIdx++][0] = emitCoverage(w, cov); + w.writeInt16At(covSlotPositions[posIdx++], emitCoverage(w, cov) - subStart); } }