web-font/_prof312.entry.ts
崮生(子虚) 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

45 lines
1.8 KiB
TypeScript

import { performance } from 'perf_hooks';
import { readFileSync } from 'fs';
import { fontSubset } from './backend/font_util/font.js';
import supportMod from './vendor/fonteditor-core/lib/ttf/table/support.js';
const support = (supportMod as any).default || supportMod;
const buf = readFileSync('font/令东齐伋复刻体.ttf');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const times: Record<string, number> = {};
function wrap(proto: any, name: string, label: string) {
const orig = proto[name];
if (typeof orig !== 'function') return;
proto[name] = function(...args: any[]) {
const t = performance.now();
const r = orig.apply(this, args);
times[label] = (times[label]||0) + (performance.now()-t);
return r;
};
}
for (const tname of Object.keys(support)) {
const T = support[tname];
if (!T || !T.prototype) continue;
wrap(T.prototype, 'read', 'read:'+tname);
wrap(T.prototype, 'write', 'write:'+tname);
wrap(T.prototype, 'size', 'size:'+tname);
}
const text = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔';
for (const outType of ['ttf','woff2'] as const) {
for (const k of Object.keys(times)) delete times[k];
const opt = {sourceType:'ttf' as const, outType};
fontSubset(ab, text, opt);
const N = 200;
const t0 = performance.now();
for (let i=0;i<N;i++) fontSubset(ab, text, opt);
const total = performance.now()-t0;
console.log(`\n=== ${outType} total avg=${(total/N).toFixed(3)}ms ===`);
const sorted = Object.entries(times).sort((a,b)=>b[1]-a[1]);
for (const [k,v] of sorted.slice(0,12)) {
console.log(` ${k.padEnd(18)} ${(v*1000/N|0)/1000}µs ${(v/total*100).toFixed(1)}%`);
}
}