perf(gpos): 预扫描 coverage 未命中的 subtable 直接写空跳过 serializeSinglePos/PairPos

anyHit lookup 内逐 subtable 序列化时,subtableCoverageHits 预扫描已判定
coverage 是否含子集字形。初夏 96~98% SinglePos subtable coverage 完全不含
子集字形(hits[j]==false),原实现仍 readCoverageGids + 构建 kept(空) +
emit 空 coverage 走完整 serializeSinglePos,纯浪费。

coverageHit=false 时直接 writeEmptyPosSubtable(与 lookup 全空折叠同性质
等价变换:空 coverage 浏览器查不到字形即跳过,valueFormat 0 与原值渲染等价),
跳过 readCoverageGids/kept/emit 全部开销。

A/B(min 微基准,checksum 因空 subtable valueFormat 0 替代原值变化,SSIM 全不变):
- 初夏纯标点 subsetGPOS 0.110→0.084ms (-24%)
- 初夏汉字标点 0.101→0.073ms (-28%)
- FiraCode 0.002ms(无 GPOS 热点,符合预期)

全量基准 SSIM 全部不变(初夏纯标点 0.9920/汉字标点 1.0000/霞鹜 0.9987/FiraCode 0.9923 等固有值)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 18:08:49 +08:00
parent 6c59c3e7e6
commit fb721a216b

View File

@ -312,7 +312,7 @@ export function subsetGPOS(
for (let j = 0; j < lk.subtableAbsOffs.length; j++) {
subtableAbsPositions[j] = w.length;
r.clearError();
const ok = serializeSubtable(w, r, lk.subtableAbsOffs[j], lk.effectiveType, origToNew);
const ok = serializeSubtable(w, r, lk.subtableAbsOffs[j], lk.effectiveType, origToNew, hits[j]);
/** 越界读取(异常表)也降级为保留原始 GPOS 字节(调用方安全降级) */
if (!ok || r.errorFlag) return null;
w.writeInt16At(subtableSlotsStart + j * 2, subtableAbsPositions[j] - lookupStart);
@ -357,7 +357,16 @@ function serializeSubtable(
subAbs: number,
type: number,
origToNew: Map<number, number>,
/** 预扫描结果:该 subtable coverage 是否含任意子集字形。false 时 coverage 必空,直接写空 subtable 跳过全量解析 */
coverageHit: boolean,
): boolean {
if (!coverageHit) {
/** coverage serializeSinglePos/serializePairPos kept coverage
* coverage subtable
* subtable readCoverageGids/kept /emit 96% subtable */
writeEmptyPosSubtable(w, type);
return true;
}
if (type === LT_SINGLE_POS) return serializeSinglePos(w, r, subAbs, origToNew);
if (type === LT_PAIR_POS) return serializePairPos(w, r, subAbs, origToNew);
return false;