From 71105374ee2a336aa01c97826a65b1eb59fa1871 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:05:55 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub/gpos):=20lookup=20=E5=81=8F=E7=A7=BB?= =?UTF-8?q?=E6=A7=BD=E6=94=B9=20writeUint16=20=E5=8D=A0=E4=BD=8D=20+=20wri?= =?UTF-8?q?teInt16At=20=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit subsetGSUB/subsetGPOS 的 lookup 偏移槽(lookupCount 个)与 gsub unsupported 分支的 subtable 偏移槽,从 reserveOffset16 闭包模式改为 writeUint16(0) 占位 + 记录 slot 起点 + 序列化后 writeInt16At 回填——与已做的 subtable 槽优化329、 writeChainFormat3 coverage 槽(gsub-writechainformat3)统一。 消除 lookupCount 个 per-lookup 闭包 + patch 对象分配。思源 GSUB 56 lookup、 FiraCode/霞鹜同。gsub unsupported 分支 subtable 槽也随之统一(原仅 supported 分支用优化329)。 收益不可测(FiraCode min 1.8346 vs 1.8439、思源 1.505 vs 1.479 均噪声内)—— lookupCount 级闭包非瓶颈,被 brotli/glyf transform 淹没。但消除语义冗余 + 统一两侧偏移槽写法。基准测试全 SSIM 一致、输出字节 IDENTICAL。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/gpos-subset.ts | 13 +++++++++---- backend/font_util/gsub-subset.ts | 25 ++++++++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/backend/font_util/gpos-subset.ts b/backend/font_util/gpos-subset.ts index 623d1a0..63e5744 100644 --- a/backend/font_util/gpos-subset.ts +++ b/backend/font_util/gpos-subset.ts @@ -259,12 +259,15 @@ export function subsetGPOS( const lookupListAbs = w.length; lookupListAbsHolder.push(lookupListAbs); w.writeUint16(lookupCount); - /** 预留各 lookup 的 Offset16 槽(相对 LookupList 起始) */ - const lookupAbsPositions: number[] = new Array(lookupCount); + /** + * 优化333: lookup 偏移槽用 writeUint16(0) 占位 + 记录 slot 起点,序列化后统一 writeInt16At 回填, + * 替代 reserveOffset16 的 per-lookup 闭包分配 + patch push(与优化329 subtable 槽同思路)。 + */ + const lookupSlotsStart = w.length; for (let i = 0; i < lookupCount; i++) { - const slotIdx = i; - w.reserveOffset16(lookupListAbs, () => lookupAbsPositions[slotIdx]); + w.writeUint16(0); } + const lookupAbsPositions: number[] = new Array(lookupCount); /** 逐个 lookup 序列化 */ for (let i = 0; i < lookupCount; i++) { @@ -358,6 +361,8 @@ export function subsetGPOS( writeEmptyPosSubtable(w, lk.effectiveType); w.writeInt16At(subtableSlotsStart, subtablePos - lookupStart); } + /** 优化333: 回填 lookup 偏移槽(相对 LookupList 起始) */ + w.writeInt16At(lookupSlotsStart + i * 2, lookupAbsPositions[i] - lookupListAbs); } w.flush(); diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index bda3d2c..3d77137 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -1623,12 +1623,18 @@ export function subsetGSUB( /** LookupList 重写 */ lookupListAbsHolder[0] = w.length; + const lookupListAbs = lookupListAbsHolder[0]; w.writeUint16(lookupCount); - const lookupAbsPositions: number[] = new Array(lookupCount); + /** + * 优化333: lookup 偏移槽用 writeUint16(0) 占位 + 记录 slot 起点,序列化后统一 writeInt16At 回填, + * 替代 reserveOffset16 的 per-lookup 闭包分配 + patch push(与优化329 subtable 槽同思路)。 + * 思源 GSUB 56 lookup,消除 56 次闭包 + patch 对象分配。 + */ + const lookupSlotsStart = w.length; for (let i = 0; i < lookupCount; i++) { - const slotIdx = i; - w.reserveOffset16(lookupListAbsHolder[0], () => lookupAbsPositions[slotIdx]); + w.writeUint16(0); } + const lookupAbsPositions: number[] = new Array(lookupCount); /** 逐 lookup 序列化 */ for (let i = 0; i < lookupCount; i++) { @@ -1694,19 +1700,24 @@ export function subsetGSUB( w.writeUint16(lookupFlag); w.writeUint16(lk.subtableAbsOffs.length); const lookupStart = w.length - 6; - const subtableAbsPositions: number[] = new Array(lk.subtableAbsOffs.length); + /** + * 优化334(与 supported 分支优化329 一致):unsupported lookup 的 subtable 偏移槽也用 + * writeUint16(0) 占位 + 记录 slot 起点 + 序列化后 writeInt16At 回填,替代 reserveOffset16 闭包。 + */ + const subtableSlotsStart = w.length; for (let j = 0; j < lk.subtableAbsOffs.length; j++) { - const slotIdx = j; - w.reserveOffset16(lookupStart, () => subtableAbsPositions[slotIdx]); + w.writeUint16(0); } if (useMarkFilteringSet) { w.writeUint16(r.u16(lk.origLookupOff + 6 + lk.subtableAbsOffs.length * 2)); } for (let j = 0; j < lk.subtableAbsOffs.length; j++) { - subtableAbsPositions[j] = w.length; + w.writeInt16At(subtableSlotsStart + j * 2, w.length - lookupStart); writeEmptySubtable(w, lk.effectiveType); } } + /** 优化333: 回填 lookup 偏移槽(相对 LookupList 起始) */ + w.writeInt16At(lookupSlotsStart + i * 2, lookupAbsPositions[i] - lookupListAbs); } w.flush();