崮生(子虚)
|
77029f1090
|
perf(gsub-reachable): collectChainRuleRefs 循环内 r.u16 改 dv.getUint16
chain format1 规则的 backtrack/input/lookahead/substRecords 读取(6 个内层
循环)从 r.u16 改 dv.getUint16。FiraCode 180 个 fmt1 规则 × 多次密集调用。
count 读取(backtrackCount 等)保留 r.u16 维持原行为。
FiraCode reachable 集合 size=89 不变,5 字体 sha 逐字节一致,SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 19:44:08 +08:00 |
|
崮生(子虚)
|
7719110b32
|
perf(gsub-subset): subsetGSUB 主函数 header+lookup 解析 r.u16 改 dv.getUint16
subsetGSUB 顶部本就构造了 dv(gsubBytes 的 DataView),header offset
(0/2/4/6/8) 与 lookup 解析循环的 r.u16 改 dv.getUint16 省方法调用+边界检查。
extension 的 r.u32 保留(越界降级安全网)。
8 字体(FiraCode woff2+ttf、CJK 标点、千字文)sha 逐字节一致,SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 19:39:54 +08:00 |
|
崮生(子虚)
|
bd68995fde
|
perf(gpos-subset): 循环内 r.u16 改 dv.getUint16
延续 gsub-reachable/gsub-subset 的已验证模式:subsetGPOS 主函数、
readCoverageGids、readClassDefMap、serializePairPosFormat1 的循环内
连续 u16 读取从 r.u16(方法调用+边界检查)改为 dv.getUint16(off,false)。
函数级 header offset 与 errorFlag 安全网保留 r.u16/scriptListSpan。
12 字体 woff2 输出 sha 逐字节一致,SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 19:36:31 +08:00 |
|
崮生(子虚)
|
17145e05fd
|
perf(gsub-subset): serialize/parse 函数循环内 r.u16 改 dv.getUint16
延续 gsub-reachable 的 dv 缓存优化到 subsetGSUB 的序列化/解析函数:
serializeSingleSubst/Multiple/Alternate/Ligature、serializeChainedContextSubst
format1/format2、parseChainRuleFormat1or2、parseChainFormat3、readClassDefMap
顶部缓存 const dv = r.dv,循环内 gid/offset 连续读取改 dv.getUint16。
函数级 format/count 判定保留 r.u16 维持 errorFlag 安全网。
输出字节级等价(6 字体 sha 全一致),SSIM 全不变。
性能:初夏标点 -5.3%、霞鹜标点 -5.0%、FiraCode -2.8%(CJK 标点 GSUB 密集收益大)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 19:32:23 +08:00 |
|
崮生(子虚)
|
92d02c33bb
|
perf(gsub-reachable): 循环内 r.u16 改 dv.getUint16 + coverage 批量读
精确 CPU profile(Session+Profiler API 排除模块加载)发现 GSUB 重字体
(FiraCode)brotli 不再主导,collectReachableGsubTargets 是 #1 热点,
其中 OTReader.u16(方法调用+边界检查)占 134Kμs。
collectReachableGsubTargets/collectSubtableTargets/collectChainRefs/
readCoverageGids/coverageFirstExcludedGid 顶部缓存 const dv = r.dv,
循环内连续 u16 读取改 dv.getUint16(off,false),省方法调用+边界检查。
函数级 format/count 判定保留 r.u16 维持 errorFlag 安全网。
readCoverageGids format1 加 Uint16Array 批量读(count>8 且对齐)。
collectReachable FiraCode 0.1525→0.1199ms(-21%),e2e 1.04→1.00ms。
基准测试全绿 SSIM 不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 19:28:56 +08:00 |
|
崮生(子虚)
|
fb721a216b
|
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>
|
2026-07-24 18:08:49 +08:00 |
|
崮生(子虚)
|
6c59c3e7e6
|
perf(gsub-subset): parseChainRuleFormat1or2 边读边 remap 提前失败
原实现对 back/input/look 先读到 backRaw/inputRaw/lookRaw 三个临时数组,
再二次遍历 remapSeq 重映射,format1 含子集外 gid 时规则失效(return null),
但 raw 数组已分配——FiraCode 多数 fmt1 规则因 input gid 不在子集而失效,raw 分配纯浪费。
改为 readSeq 边读边重映射(format1):遇子集外 gid 立即返回 null,省掉三个
临时数组与二次遍历。format2(class index 始终有效)原样读取不重映射,行为不变。
subsetGSUB min(checksum 与改动前完全一致):
- FiraCode 0.296→0.290ms(-2%)
- 初夏纯标点 0.075→0.071ms(-5%)
全字体 SSIM/输出字节均不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 17:58:15 +08:00 |
|
崮生(子虚)
|
7b5d03391f
|
perf(gsub-reachable): type4 Ligature first gid 短路跳过整个 LigatureSet
LigatureSubst 第一分量(coverage gid)不在子集时整条 ligature 不会触发,
原实现在 first 不在子集时仍读取 setOff/ligCount/每条 ligature 的
ligOff/compCount/target 后才 continue。改为 first 检查上提到 LigatureSet
展开之前,不在子集直接跳过,省掉内部全部 u16 读取。
初夏明朝 type4 first gid 常全不在子集(首轮 coverage 多为非子集字形),
此短路消除每条 ligature 的无谓读取。
collectReachableGsubTargets min(checksum 与改动前完全一致):
- 初夏汉字标点 0.070→0.059ms(-16%)
- 初夏纯标点 0.081→0.068ms(-15%)
- FiraCode 0.105→0.101ms(-4%)
全字体 SSIM/输出字节均不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 17:49:42 +08:00 |
|
崮生(子虚)
|
799d284d4b
|
perf(gsub): emitCoverage 跳过 slice+sort,readCoverageRemapped 缓存前统一排序
原 emitCoverage 对输入 newGids 做 slice().sort()——防御性复制+排序。
但 readCoverageRemapped 的输出(fmt3/writeChainFormat2 coverage)因 原gid→新gid
映射不保序而可能乱序,必须排序;type1/2/3/4 的 entries.map(e=>from) 虽已按 from
排序故升序,却仍付出 slice+sort 开销。
将排序职责上移到 readCoverageRemapped 缓存入口(covCache 命中率极高,FiraCode
604 引用/83 独立 coverage,排序分摊几乎免费),使所有调用方输出天然升序无重复。
emitCoverage 据此改为直接扫描区间(不 slice、不 sort、不分配 ranges 对象数组),
同时两遍扫描统一为 rangeCount 计数。
FiraCode subsetGSUB 417→390μs(-6.5%),输出 sha 全字体一致,SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 17:33:48 +08:00 |
|
崮生(子虚)
|
f053112e44
|
perf(gsub-reachable): type1 fmt1 + type3 条件反转遍历方向
延续 9d6e982(type1 fmt2 反转)的思路,对 SingleSubst format1 与
AlternateSubst(type3)同样反转遍历方向:遍历 reachable 二分查 covGids
(按 gid 升序)得 index,替代遍历 covGids 逐个查 reachable。
初夏明朝纯标点实测 fmt1 covLen 405、type3 covLen 987(命中率 2.3%),
均属「展开全量命中极少」模式,反转后遍历量从 covGids.length 降到
reachable.size。
关键:加条件 covGids.length > reachable.size 才反转。无条件反转在
霞鹜/FiraCode 上负优化(covGids 短,FiraCode type1 avg 5.5,二分开销
> Set.has 遍历)。条件反转使短 covGids 走原 Set.has 路径。
type4(Ligature)不反转:firstHit 判定反转后遍历 reachable(53)× 18
calls = 954 次二分 > 原 684 次 Set.has,必负。
A/B vs 9d6e982(3000 次 p50,reachable sha 全一致):
- 初夏标点 102→92μs(-10%)
- 初夏8字 54→51μs(-5%)
- 霞鹜标点 12→10μs(-12%,无条件版 +8% 已修正)
- 霞鹜8字 6.8→6.4μs(-7%)
- FiraCode 标点 70→69μs(-1%,无条件版 +2% 已修正)
相比最初 baseline(fee9e49 前)初夏标点 129→92μs(-29%)。基准测试
SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 17:07:13 +08:00 |
|
崮生(子虚)
|
9d6e982682
|
perf(gsub-reachable): type1 fmt2 反转遍历方向
collectSubtableTargets 的 SingleSubst format2 原遍历 coverage gid
逐个查 reachable(inSubset=Set.has)。初夏明朝纯标点实测 fmt2 covLen
合计 9180 gid/round,但 reachable 仅 53 → 命中率 <4%,9180 次 Set.has
几乎全 miss。
反转:遍历 reachable(小集合)二分查找 covGids(按 gid 升序,fmt1 list
/ fmt2 range 展开均天然排序)得 index,命中则读 gidArray[i]。遍历量从
covGids.length 降到 reachable.size。
A/B(3000 次 p50):
- 初夏标点 129→105μs(-19%)
- 初夏8字 64→58μs(-9%)
- 霞鹜标点 13→11μs(-13%)
- 霞鹜8字 7.8→6.6μs(-15%)
reachable 集合 sha256 全部一致,基准测试 SSIM 全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 17:03:26 +08:00 |
|
崮生(子虚)
|
fee9e49164
|
perf(gsub-reachable): coverageFirstExcludedGid 边解析边查不展开 coverage 数组
原实现调用 readCoverageGids(off, cache) 完整展开 coverage 到数组再遍历找首个 excluded,
对 format2 range(初夏明朝 coverage 常覆盖上千 gid)即使首个 gid 就 excluded 也要展开全量,
是「展开全量命中极少」浪费(同类见 gsub-classdef-format2-bsearch)。
改为 cache 命中遍历缓存数组,miss 时直接边解析 coverage 边查 inSubset,命中即返回,
不分配数组、不填 cache。fmt3 失败路径的 coverage 永不被 collectSubtableTargets 读
(失败不产 target),故不填 cache 无碍。
reachable 集合 sha256 三字体完全一致,SSIM/ink 全不变。
初夏纯标点 collectReachable p50 0.159→0.136ms(-14%)。
|
2026-07-24 16:52:23 +08:00 |
|
崮生(子虚)
|
694364769e
|
perf(gsub-reachable): 合并 lookupRelOffs 读取到 lookup parse 主循环
原实现先收集 lookupRelOffs 到中间数组,主循环再按下标回读。合并后直接在
主循环内读取偏移,省去中间数组分配与一次回读。FiraCode collectReachable
-2.3%(0.116→0.113ms),reachable 集合 sha256 不变
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 16:34:08 +08:00 |
|
崮生(子虚)
|
a1fe4da1ce
|
perf(gsub): readCoverageRemapped format2 range 改子集 gid 二分
与 readClassDefMap format2 同思路:原实现逐 gid 遍历 [start..e] 全部
查 gidLookup(FiraCode coverage format2 首次展开共 2317 gid,子集占极小
部分)。改为在升序子集 gid 数组上二分定位 [start,end] 内的 gid,仅 push
命中的 newGid。range 非空即 origNonEmpty=true(无论是否命中子集),与原
逐 gid 遍历语义一致。
subsetGSUB 再 -8.7%(0.314→0.287ms),FiraCode woff2 min 1.8→1.6ms
GSUB 输出 sha256 三字体全一致,SSIM 不变
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 16:30:00 +08:00 |
|
崮生(子虚)
|
06c8fcf18c
|
perf(gsub): readClassDefMap format2 range 改子集 gid 二分
原实现遍历每个 ClassDef range 的 [start..end] 全部 gid 查 gidLookup
(FiraCode 实测 33 次 readClassDefMap 展开共 9686 gid 仅命中 19,命中率
0.2%,子集仅 89 gid)。改为对每个 range 在升序子集 gid 数组上二分定位
[start,end] 内的 gid,仅对命中的 set class——从「展开 range 全部 gid」转为
「只处理落在 range 内的子集 gid」。
subsetGSUB: 0.330ms → 0.284ms (-14%),FiraCode woff2 min 2.1→1.8ms
GSUB 输出 sha256 三字体全一致,SSIM 不变(FiraCode 0.9923)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 16:24:25 +08:00 |
|
崮生(子虚)
|
7f983ff969
|
perf(gsub): readClassDefMap 用 gidLookup 数组替代 Map.get
fmt2 ChainContext 的 ClassDef 重映射原用 origToNew.get() 逐 gid 查询,
FiraCode fmt2 ClassDef 展开后 9699 个 gid 每个 Map.get(~9ns) 是 fmt2 路径
热点。改用 gidLookup Int32Array 索引(~1ns),语义等价(gidLookup[g]>=0 ⟺
origToNew.has(g) 且值相同)。输出字节三字体 sha256 全一致,SSIM 不变
(FiraCode 0.9923)。writeChainFormat2 移除冗余 origToNew 参数。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 15:44:53 +08:00 |
|
崮生(子虚)
|
f0a5ac175f
|
chore: 删除误提交的临时 profile 脚本
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 15:14:45 +08:00 |
|
崮生(子虚)
|
b17d5a3700
|
perf(gsub): lookup 级全空预扫描跳过逐子表序列化
subsetGSUB 对每个 supported lookup 预扫描所有子表是否都 coverage 全子集外
(isSubtableSkipableByCoverage)。全空 lookup 序列化结果必然是 N 个空 subtable
(与逐子表 serializeSubtable 失败后 writeEmptySubtable 逐字节相同),直接在 lookup 级
批量写空,跳过 N 次 serializeSubtable 函数调用 + 预检 + rollback 开销。
初夏 lookup[5] type6 268 子表(小子集全不命中)是典型受益场景。
安全性:allEmpty 当且仅当所有子表 isSubtableSkipableByCoverage===true,而 serializeSubtable
内部对 skipable 子表直接 return false(→空 subtable),故输出逐字节相同(含 FiraCode 连字)。
format2 class 驱动子表不预检(返回 false),含 format2 的 lookup allEmpty 必为 false 走原路径。
subCount 不变、lookup 不删(区别于已证危险的 GSUB lookup 删除)。
subsetGSUB 初夏 0.215→0.178ms(-17%),全基准 SSIM 与输出字节完全不变(diff 验证 hash 一致)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 15:14:00 +08:00 |
|
崮生(子虚)
|
8a7efea9d0
|
perf(gpos/gsub): read 零拷贝 subarray 替代 readBytes slice
GPOS/GSUB 表类 read 原用 reader.readBytes (= new Uint8Array(view.buffer, off, len).slice()),
.slice() 拷贝整个表字节(初夏 GPOS 68KB、GSUB 31KB)。V8 中 68KB slice 耗 ~40μs,
而 subarray 零拷贝仅 ~0.3μs(130× 差距)。
GPOS/GSUB 是原始字节透传:subsetGPOS/subsetGSUB 只读 OTReader + 写全新 OTWriter,
write 只 writeBytes 只读,optimize 不碰,整个 read→optimize→subset→write 生命周期
从不原地修改底层字节,故可安全返回共享 ArrayBuffer 的 subarray 视图。
初夏 full read 0.124→0.063ms (-49%),readBuffer 0.165→0.098ms。
全基准 SSIM 与输出字节完全不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 15:07:18 +08:00 |
|
崮生(子虚)
|
ee7b6ec230
|
perf(gpos): 全空 lookup 折叠为 subCount=1 + subtable 偏移槽消除闭包
优化330: 预扫描 supported lookup 的 subtable coverage 命中,全无命中的 lookup
折叠为单个空 subtable(feature 按 lookup index 引用,subCount 改变语义等价,
浏览器查空 coverage 即跳过)。大字体小子集场景(初夏 GPOS lookup[10] 283 个
SinglePos subtable 全空、lookup[7] 285 个仅 3 命中)原先逐空 subtable 序列化
是 subsetGPOS 主热点。unsupported lookup 同理折叠。
subsetGPOS 初夏纯标点 570→380μs(-33%)、思源 8 字 85→26μs(-70%)。
附带 woff2 体积下降(空 lookup 不再逐 subtable 输出):思源 8字 2632→2536B、
初夏纯标点 6940→6912B。SSIM 全部不变。
优化329: lookup 循环 subtable 偏移槽用 writeUint16 占位 + writeInt16At 统一回填,
替代 reserveOffset16 的 per-slot 闭包分配(短命闭包虽 V8 近乎免费,但简化代码)。
|
2026-07-24 14:57:02 +08:00 |
|
崮生(子虚)
|
852f0d2f21
|
perf(ot-bytes): OTWriter 初始容量 256→2048 + 内联容量检查消除热路径函数调用
subsetGPOS 思源 36→33μs、subsetGSUB 13→11μs;writeUint16 是序列化第一热点
(思源 207次/call),原 per-call ensure() 函数调用在 V8 class private method
不内联;内联为容量判断 + grow,并预分配 2048 避免小输出多次扩容拷贝
|
2026-07-24 14:40:05 +08:00 |
|
崮生(子虚)
|
04bf5c6077
|
perf(os2): size 固定返回 96 跳过通用 struct 查表(version 恒 0x4)
|
2026-07-24 14:33:54 +08:00 |
|
崮生(子虚)
|
82c1e42108
|
perf(name): subset 字体 name 表白名单,仅保留渲染必需 6 个 nameId
subset 字体核心目的是小体积下载 + 正确渲染,但 name 表保留了全部 19 个 nameId
(含版权/商标/URL/版本/描述等元数据)。思源 8 字 ttf 8.8KB 里 name 占 3.9KB(44%),
多为这些 web 渲染无用的元数据,徒增 brotli 输入与下载体积。
size 阶段加 KEEP_NAME_IDS 白名单,只保留浏览器字体匹配/渲染必需的:
1 fontFamily / 2 fontSubFamily(CSS font-family 与 weight/style 匹配核心)
4 fullName / 6 postScriptName(全名与 PS 名)
16 preferredFamily / 17 preferredSubFamily(变体/typographic 匹配)
体积收益(SSIM 全部不变,渲染像素不受 name 内容影响):
思源 8 字 woff2 4448→2632B(-41%)、思源千字文中段 19100→17156B(-10%)
FiraCode 11636→10828B(-7%)、思源标点 4864→3048B(-37%)
令东千字文 -0.5%(name 占比本就小)。brotli 输入减小附带压缩提速。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 14:22:11 +08:00 |
|
崮生(子虚)
|
089ae7c0a7
|
perf(name): read 单遍扫描定 platform,消除全部中间 nameRecord 对象
原 read 先建 count 个 {platform,encoding,language,nameId,length,offset} 中间对象,
再两遍遍历它们。改为两趟直扫 view:
pass1 只读 platform/encoding/language 三字段比较定 platform(windows english 优先),
不读 nameId/length/offset、不建对象;
pass2 头开始只对匹配记录读 nameId/length/offset + readBytes + 解码,直接产出 names。
思源 56 条记录中间对象 56→0,view 读次数减半。字节 sha256 完全一致,
5 轮交替 readBuffer median 稳定 -3~-5%(对象分配本身 V8 近零成本,收益小但一致)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 12:53:04 +08:00 |
|
崮生(子虚)
|
ba17974e1d
|
perf(name): read 延迟 readBytes,按需只解码选中 platform 的记录
原 name.read 第一遍循环对全部 count 条记录都 readBytes(每次 slice 一份字节),
但最终第三循环只对匹配 platform+encoding+language 且 nameId 有用的记录解码字符串,
其余记录的 .name 字节从不被消费。思源 56 条记录只解码约 15 条,浪费 41 次 slice。
改为删除第一遍全量 readBytes,在第三循环里对匹配记录按需 readBytes,
N 次 slice 降到「有用记录数」次。字节 sha256 完全一致(思源/令东验证),
思源 readBuffer median 0.021→0.014ms(-34%),令东持平(name 表小本就无浪费)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 12:50:28 +08:00 |
|
崮生(子虚)
|
d6d94e201e
|
perf(os2): read 改用直接 DataView 批量读取
OS/2.read 原走 struct 通用 read(逐字段 switch 分发 + reader.read 调用),
与已优化的 write(优化176 直接 view)不对称。改为手写每个字段的 view 直读,
按 version(0/1/2+)决定读到哪,补默认值逻辑不变。
字段值完全一致(version/achVendID/sxHeight 验证),OS/2.read 微基准
思源 6.2→1.7μs(-73%)、令东 5.5→0.6μs(-89%),端到端约 -1%
(readBuffer 占比小),与 write 对称后可推广到其他 struct 通用 read 表。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 12:44:50 +08:00 |
|
崮生(子虚)
|
d575d55540
|
perf(name): 合并 UTF-8+UCS-2 编码 + ASCII 快路径
name 表 size 阶段对每条记录都要 UTF-8(mac)+ UCS-2(windows)各一份,
原两次独立编码各做一遍 stringify 扫描 + 编码循环。新增 toUTF8AndUCS2Bytes
合并为单次扫描:纯 ASCII(含拒绝 \0)走超快路径——UTF-8 直接取字节、
UCS-2 大端序 [0,ch],省掉多字节分支与两次扫描;非 ASCII 回退原逻辑。
字节 sha256 完全一致(思源/令东验证),思源 8 字 woff2 端到端 -7%
(name.size 占 dump 44%),令东千字文持平(name 占比 0.3% 在噪声内)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 12:38:40 +08:00 |
|
崮生(子虚)
|
bcd4574b3f
|
perf(woff2): triplet 编码无分支 abs + 符号位提取
transformGlyfAndLoca 的 y+triplet 循环每点原先 4 次三元判断(absDx/absDy/xSign/ySign),
中文字体 dx/dy 正负近似 50/50 为不可预测分支,致流水线冲刷。
改用 Int32 算术右移统一提取符号:
- dxSgn = dx >> 31(0 或 -1)
- absDx = (dx ^ dxSgn) - dxSgn(无分支 abs)
- xSignBit = dxSgn + 1(等价 dx >= 0 ? 1 : 0)
令东 74.6% 点走 case3,收益最大:woff2 端到端 2.197→2.078ms(-5.4%),
思源 0.662→0.653ms(-1.4%)。sha256 字节级一致,全基准 SSIM 零变化。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 12:08:48 +08:00 |
|
崮生(子虚)
|
60db8d226b
|
perf(woff2): triplet 编码 case2/3/4 去掉冗余的轴非零判定
case1 排除 dx===0、case2 排除 dy===0 后,走到 case3/case4 时两轴必非零,
故 case3/case4 的 'dx!==0 && dy!==0' 是冗余判定。令东 74.6% 点命中 case3,
每点省 2 次比较。case2 的 'dx!==0' 同理冗余(case1 未命中且 dy===0 则 dx 必非0)。
cmp 字节一致,逻辑等价已证明。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:55:24 +08:00 |
|
崮生(子虚)
|
72c6afef92
|
perf(woff2): 移除 simple glyph 的 bbox 计算,由解码端从轮廓点重建
WOFF2 规范:bboxBitmap bit=0 时解码端计算点的 min/max 重建 bbox,bit=1 读显式存储值。
原编码对 simple glyph 计算 calcBbox 并与原始 bbox 比对决定 bit——但置 1 时存的也是
calcBbox,故无论 bit 值解码端都得 calcBbox,该计算与比对无功能意义。
移除后 simple glyph 永远 bit=0(解码端自算),省掉 x/y 循环每点 3 次 bbox 比较 + bboxSet
判定 + Pass2 写入。复合 glyph 仍显式存 bbox(raw 数据剥离了 glyph 头,解码端无法重建)。
令东千字文 woff2 write 2.449→2.237ms,输出体积 -20B~60B,全基准 SSIM 零变化。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:51:29 +08:00 |
|
崮生(子虚)
|
f945e1aedb
|
perf(woff2): triplet 编码各分支内累加 gsbi,消除 TRIPLET_DATA_SIZES 查表
令东千字文 woff2 write -12%(2.785→2.449ms),输出字节完全一致(cmp sha256 验证)。
原 gsbi += TRIPLET_DATA_SIZES[flag & 0x7F] 每点查表(Uint8Array 索引+位运算),
改为各 case 分支内 adv=1/1/1/2/3/4 常量累加,省掉查表。思源 -4%。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:48:12 +08:00 |
|
崮生(子虚)
|
aeb2622a57
|
perf(gsub-reachable): format3 失败路径跳过 contextGids 收集
format3 triggerable=false 是常态(初夏纯标点 280 个 format3 首轮全 false)。
失败时只需找首个不在 reachable 的 gid 记 failGid——此刻 contextGids 收集
无意义:break 前已检查的 gid 都在 reachable(inSubset=reachable.has),
调用方对其 reachable.add 是 no-op。改用 coverageFirstExcludedGid(复用
readCoverageGids 带 cache + 遍历短路)分离失败/成功两遍,失败路径省掉
contextGids.add 调用。仅 triggerable=true(coverage 全在子集、量小)时
第二遍收集 contextGids。
reachable 输出集合逐元素一致(初夏 23/FiraCode 89),全基准 SSIM 不变。
初夏 reach 0.265→0.245ms(-8%)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:28:34 +08:00 |
|
崮生(子虚)
|
3103dd5db1
|
perf(gsub): ScriptList/FeatureList 整块拷贝快路径(同 GPOS)
subsetGSUB 复用 scriptListSpan/featureListSpan,span 自洽且不越过相邻 list
header offset 时整块 subarray 拷贝。交错判定用 SL span≤featureListOff、
FL span≤lookupListOff(LookupList subtable 散落,header offset 不能代表其
字节范围,故不用「下一更大 offset」作上界)。霞鹜文楷 GSUB 物理交错
(LL<SL<FL 且 ScriptTable 跨越 FL) 自动降级 serialize,字节与优化前完全一致。
收益:初夏 GSUB 11474→8474B(-26%)、FiraCode 15504→14878B(-4%),
保留 fontTools FeatureTable 去重。霞鹜降级路径 cmp 逐字节一致。全基准 SSIM 不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:23:06 +08:00 |
|
崮生(子虚)
|
ccc2e995c8
|
perf(gpos): ScriptList/FeatureList 整块拷贝跳过逐字段序列化
两表不含 glyphId(仅引用 lookup index),绝大多数字体其子表紧凑排列在
[listOff, listOff+span) 内、与下一表无物理交错,字节块本身即合法表。
新增 scriptListSpan/featureListSpan 计算跨度并校验自洽,满足则 subarray
整块拷贝,否则降级 serializeScriptList/serializeFeatureList。
收益:初夏 subsetGPOS 0.270→0.226ms,纯标点 0.240→0.165(-31%),
思源 0.071→0.036(-49%)。顺带保留 fontTools 的 FeatureTable 去重
(serialize 重排丢失致初夏 FeatureList 1840→3728B),输出 GPOS 字节
初夏 12064→10176(-16%)、思源 1194→978(-18%)。全基准 SSIM 不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:16:21 +08:00 |
|
崮生(子虚)
|
8c311768f6
|
perf(gsub-reachable): format3 失败 gid 跨轮跳过冗余扫描
不动点迭代中 format3 triggerable=false 的 subtable,记录使其失败的第一个
coverage gid。下一轮若该 gid 仍未进 reachable,本 subtable 必定仍 false,
直接跳过 collectChainRefs 重扫。
原 settledChain 只记忆稳定 subtable(format2 / format3 triggerable=true),
format3 triggerable=false 每轮都重扫。初夏纯标点 280 个 format3 全 false,
3 轮 × 280 = 840 次 collectChainRefs;改后 chainCalls 280→(第 2 轮起全跳过),
-67%。失败 gid 进 reachable 时重扫(可能仍 false 则更新失败 gid,可能变 true
则转 settledChain)。
reachable 集合字节级一致(初夏/得意黑/思源全 cmp 通过),SSIM 全程不变。
初夏 reach 0.33→0.29ms(-13%,collectChainRefs 非 reach 全部开销故收益有限)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 11:05:52 +08:00 |
|
崮生(子虚)
|
65e238cdca
|
perf(cmap): subset 模式 format4 延迟解析,跳过 4×segCount 数组构建
subset 模式下 format4 不再急切构建 endCode/startCode/idDelta/idRangeOffset
共 4 个 segCount 项数组,只记录各段在 view 的偏移;lookupFormat4 二分时
按需从 view 读(mid*2 偏移)。原 subset 模式仍执行 4×segCount 次 getUint16
+ 4 个数组分配,得意黑 segCount=3929 → 1.5 万次读,占 readSubTable 0.101ms。
延迟后 view 读次数 = O(S × log(segCount)),远小于 4×segCount。
lookupFormat4 新增 _lazySegs 分支,非延迟路径(全量模式)保持急切数组不变。
idDelta 仍用 getUint16 读(与原急切模式一致),依赖 % 0x10000 处理符号。
字节逐字节一致(令东/思源/初夏/得意黑/otf 全 cmp 通过),SSIM 全程不变。
cmap: 初夏 0.075→0.009ms(-88%),得意黑→0.004ms(-94%),思源 0.063→0.014(-78%)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:57:36 +08:00 |
|
崮生(子虚)
|
b7dbe3fe1d
|
perf(hmtx): subset 模式紧凑存储,消除 numGlyphs*2 大对象分配
subset 模式下 hmtx 改为按 subsetGids 顺序紧凑存储 Int32Array(S*2),
下标用 gi*2(subsetGids 内位置)替代 gid*2。原实现分配 Int32Array(numGlyphs*2),
CJK 字体 numGlyphs 达 3.5 万→284KB 大对象,V8 大对象分配+zero-fill 单次 ~54μs。
resolveGlyf subset 分支同步改用 gi*2 索引。OTF 路径不变(表顺序 hmtx 在 CFF 前,
subsetGids 未就绪,仍走全量数组)。
字节逐字节一致(令东/思源/初夏/得意黑/otf 全 cmp 通过),SSIM 全程不变。
初夏 hmtx 0.056→0.002ms(-96%),create -15%,fontSubset 总 -7%。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:52:30 +08:00 |
|
崮生(子虚)
|
aa2818b13e
|
perf(gsub): gidLookup 改 Int32Array,大数组 fill 快 3×
GidLookup(原gid→新gid 查找表)原用 number[].fill(-1), TypedArray 的 fill
是 native memset,比 number[].fill 快约 3×(35515 项:219μs→67μs)。
初夏明朝 subsetGids 仅十余个但 maxOrigGid 达 3.5 万(reachable 的高 gid target),
number[].fill(-1) 耗时 ~200μs,占 subsetGSUB 总耗时 ~47%。Int32Array.fill
降至 ~67μs,索引访问速度实测与 number[] 一致(FiraCode 密集查询场景同 8μs)。
初夏 rewrite 0.663→0.501ms(-24.5%),总 fontSubset -9.2%;FiraCode 略快 -2.8%。
GSUB 输出字节级一致(cmp 通过),30 项基准 SSIM 零变化。
推翻记忆 gsub-subset-perf-coverage-cache 中「number[] 比 Int32Array 快 2×」结论
(旧 V8 或不同测试条件),当代 V8 下两者访问速度相同,TypedArray fill 显著更快。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:38:27 +08:00 |
|
崮生(子虚)
|
6d897f1987
|
perf(gsub-reachable): collectChainRuleRefs 消除 3 个临时数组分配
ChainSubRule 解析原先读 backtrack/input/lookahead 到 3 个 number[] 再两遍遍历
(先 inSubset 校验再 contextGids.add),format1 每 rule 分配 3 数组
(FiraCode 424 次/call × 3 = 1272 次数组分配 + GC 压力)。
改为两遍扫描同一段字节,不分配中间数组:
- 第一遍仅 inSubset 校验全部 gid(遇子集外即 return,放弃规则)
- 通过校验后再第二遍收集 context gid + 读 subst records
reachable 输出字节级一致(FiraCode 132 / 初夏 63 均通过 cmp),
FiraCode reach 0.368→0.343ms(-6.8%),30 项基准 SSIM 零变化。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:30:05 +08:00 |
|
崮生(子虚)
|
dd4f7ba55a
|
perf(gsub-subset): format3 预检用 readCoverageRemapped 保证判定一致
opt317 的 format3 预检用 coverageAllOutOfSubset 内联判定,其 COV_RANGE
累积超限逻辑与 parseChainFormat3 实际用的 readCoverageRemapped 不一致,
导致 FiraCode 13 个 format3 预检未跳过却深度解析失败(0.328ms/call 浪费)。
预检改用 readCoverageRemapped(与深度解析同一判定函数 + 共享 covCache),
精确保证「预检跳过 ⟺ 深度解析失败」,输出完全一致(都是空 subtable)。
readCoverageRemapped 触碰 covCache 无副作用(gids 留 EMPTY_GIDS 占位,
readCoverageGids 命中按 miss 重算,已有逻辑)。
另:writeEmptySubtable 的 coverageOffset 恒为 6,改直接写常量,
省 reserveOffset16 的 patches push + 闭包分配。
FiraCode subsetGSUB 1.049→0.768ms(-27%),fontSubset 2.49→2.27ms(-9%)。
GSUB/woff2 字节 cmp 全绿,30 项基准 SSIM 零变化(FiraCode 0.9923、
千字文 0.9986、初夏纯标点 0.9920、otf 0.9681/0.9310 均不变)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:14:38 +08:00 |
|
崮生(子虚)
|
1da81eb0c0
|
chore(gsub-subset): 清理 profiling 残留的 __sp 插桩
上一轮 opt317 提交时误带入了 format1/format3 序列化路径上的 __sp 计时
插桩(globalThis 属性读取 + performance.now 调用)。热路径每个 type6
subtable 都执行,属无关生产代码。删除并移除无用的 perf_hooks import。
GSUB 字节输出零变化(cmp 一致),性能持平。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 10:02:46 +08:00 |
|
崮生(子虚)
|
4c9022adb6
|
perf(gsub-subset): format3 预检判定修正跳过不可能触发的规则
subsetGSUB 真 hot path(FiraCode 2.2ms)是 type6 format3 失败路径占 69%
(16 次×0.094ms)。根因:isSubtableSkipableByCoverage format3 旧判定
「整组 coverage 全子集外」语义过强,当一组内部分 coverage 全子集外、
部分含子集内 gid 时预检不跳过,但 parseChainFormat3 遇到那个全子集外的
coverage 即返回 null 失败,白白深度解析。
format3 规则触发需三组 coverage 的 gid 全在子集内,故任一 coverage 原非空
且全子集外(coverageAllOutOfSubset===true,等价 readCoverageRemapped===null)
即不可能触发,预检直接跳过。
FiraCode fontSubset 3.751→2.029ms(-46%)。GSUB 字节 cmp 完全一致,
woff2 端到端字节 cmp 全绿,SSIM 0.9923 不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:55:49 +08:00 |
|
崮生(子虚)
|
56e8b6db4e
|
perf(gsub-reachable): format3 闭包消除 + 固定点迭代稳定性记忆
collectChainRefs 在固定点迭代中高频调用(初夏纯标点 280 个 format3 subtable ×
多轮),每次创建 readCovGidsChecked 闭包有分配开销。
优化315: format3 分支消除闭包,改显式三段循环手动推进 p,保留 triggerable 短路。
优化316: collectChainRefs 返回稳定标志(format2 总稳定 / format3 triggerable=true
稳定),主循环用 settledChain 记忆已稳定 subtable 偏移,后续轮直接跳过。
稳定测量(N=300 预热):
FiraCode 3.757 → 3.529ms (-6%)
初夏-纯标点 1.847 → 1.548ms (-16%)
初夏-汉字标点 1.503 → 1.260ms (-16%)
reach 阶段(单测): FiraCode 0.449→0.318ms, 初夏 0.544→0.315ms
无 GSUB 字体(令东/思源)无影响。所有用例 reach 输出与黄金基准逐字节一致。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:43:55 +08:00 |
|
崮生(子虚)
|
a1c7312722
|
perf(glyf): _origGlyfRef 子对象展平为 _origBuf/_origOff/_origLen
subset 快路径每个 simple 字形原本分配 glyfObj + _origGlyfRef 两个对象,
展平后 _origGlyfRef 的 {buffer,byteOffset,length} 直接存为 glyfObj 的三个属性,
省掉每字一个子对象分配 + 下游解引用间接寻址。
改 glyf.js(声明+component 守卫)、write.js、sizeof.js、optimizettf.js。
千字文 ttf 0.246→0.234ms(-5%),ttf/woff2 字节输出 cmp 完全一致,SSIM 全部不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:31:43 +08:00 |
|
崮生(子虚)
|
6b28ba3d74
|
perf(glyf): subset 快路径删 endPtsOfContours 数组分配,直接读最后 endPt
优化310 快路径下 endPtsOfContours 数组仅用于算 _totalPoints(供 maxPoints metrics),
下游 optimizettf 快路径分支、sizeof/write 原始字节拷贝均不消费它。
直接读最后一个 endPt(vOff+10+(numContours-1)*2)算 _totalPoints,
省掉每个 simple 字形的 Array 分配 + numContours 次 getUint16 循环。
千字文 ttf 0.252→0.246ms,ttf 字节输出 cmp 完全一致,SSIM 全部不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:27:19 +08:00 |
|
崮生(子虚)
|
308df8ef4d
|
perf(woff2): transformGlyfAndLoca 合并 y 解码与 triplet 编码为单循环
原 3 次遍历(x 解码→y 解码→triplet 编码)+ yCoords Int32Array 中转;
现 2 次遍历(x 解码→y 解码+triplet 编码合并)。
关键洞察:triplet delta 语义 = TTF delta(都是相对前一点的差),
所以解码 y 的同时用已存 xCoords[i] 配对编码 triplet,无需 yCoords 数组。
bbox_y 的 min/max 在合并循环同步计算,bbox bitmap 判定拆为 x/y 两段。
千字文 woff2 2.87ms→2.59ms(-9.6%),字节输出与 clean 版 cmp 完全一致,
SSIM 全部不变。删除 _reuseYCoords 模块级缓冲区。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:23:06 +08:00 |
|
崮生(子虚)
|
ece6eeca2f
|
perf(buffer): toBuffer ArrayBuffer→Buffer 用 Buffer.from 零拷贝替代逐字节循环
千字文 ttf 0.396ms→0.252ms(-36%),woff2 不受影响(不走 node Buffer 路径)。
Writer 的 ArrayBuffer 精确分配为 ttfSize(offset=0),getBuffer 返回完整 buffer,
Buffer.from 共享底层内存无 over-exposure 风险。SSIM 全部不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:15:17 +08:00 |
|
崮生(子虚)
|
69dc5e343e
|
perf(glyf): simple 字形原始字节快路径,千字文 ttf -80%/woff2 -35%
子集化 simple 字形经 parse→optimizettf→write 往返后产出的 glyf 字节
与原始字体完全一致(实测 73/73),直接拷贝原始字节跳过三段往返。
- glyf.js subset read: simple 字形存 _origGlyfRef 引用 + 读 header/endPts
供 metrics,跳过坐标解码;compound 及 component 走完整 parse
- optimizettf: 检测 _origGlyfRef 只收集 metrics 跳过 ceilReduce
- sizeof: glyfSize = 原始字节长度
- write: fullView.set 原始字节段跳过 encode
额外保留原始 hinting instructions(原非 hinting 模式丢弃),渲染更精确。
SSIM 全部不变(千字文 0.9986/FiraCode 连字 0.9923/初夏明朝 0.9920)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 09:08:22 +08:00 |
|
崮生(子虚)
|
8f0964ce54
|
perf(parse): OS/2 表类按 format 缓存 + 清理 dead write,千字文 -5%
OS/2 read 原每次 struct.slice + _table.create 动态建类(_createClass +
Object.assign prototype),是 OS/2 parse 主开销(prof 稳态 ObjectAssign 4.2%)。
format 仅 0/1/2+ 三种,表类按 format 模块级缓存复用;去掉 Object.assign
(os2Fields, tbl),直接在 tbl 上补默认字段。
附带清理:ceilReduceAndSizeFromTypedArrays/Flat 的 _preXLen/_preYLen 是
全库零读取点的 dead write(subarray.length 已携带长度),删除;
optimizettf 后补 _yArr=null(悬挂内存,唯一消费者因 _xArr=null 守卫进不去)。
千字文 woff2 A/B:4.47→4.26ms(-5%),字节完全相同,SSIM 全部一致。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 08:51:44 +08:00 |
|
崮生(子虚)
|
78e8f798e8
|
perf(woff2): Brotli 质量 q3→q2,再省 8% 时间,体积仅 +0.09%
实测千字文 woff2 各 quality 端到端时间 + 体积(同文本):
q3=4.65ms/42712B q2=4.26ms/42752B q1=4.21ms/46432B q0=4.12ms/47240B
q2 是时间/体积最优拐点:比 q3 再省 8% 时间,体积仅 +40 字节(+0.09%)。
q1/q0 体积陡增 +8.7%/+10.6%(下载/解析代价),不划算。
基准测试全部 SSIM 与 ink 计数一致(FiraCode 0.9923、初夏明朝纯标点 0.9920)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-24 08:42:36 +08:00 |
|