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

62 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import fs from 'node:fs';
import { Font } from './vendor/fonteditor-core/lib/ttf/font.js';
import { probeGsubAndCmap, collectReachableGsubTargets } from './backend/font_util/gsub-probe.js';
const fontBuf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf');
const ab = fontBuf.buffer.slice(fontBuf.byteOffset, fontBuf.byteOffset + fontBuf.byteLength) as ArrayBuffer;
const text = ',。!?、;:“”‘’';
const codePoints = Array.from(text);
const option = { sourceType: 'ttf' as const, outType: 'woff2' as const };
function timeOnce<T>(label: string, fn: () => T, runs = 1): T {
// warmup
for (let i = 0; i < 3; i++) fn();
const t0 = performance.now();
let res: T;
for (let i = 0; i < runs; i++) res = fn();
const t1 = performance.now();
console.log(`${label}: ${((t1 - t0) / runs).toFixed(2)}ms`);
return res!;
}
// 阶段拆解best of 5
function median(arr: number[]) { return arr.slice().sort((a,b)=>a-b)[Math.floor(arr.length/2)]; }
function bench(label: string, fn: () => void, iters = 7) {
for (let i = 0; i < 3; i++) fn(); // warmup
const ts: number[] = [];
for (let i = 0; i < iters; i++) {
const t0 = performance.now();
fn();
ts.push(performance.now() - t0);
}
console.log(`${label}: ${median(ts).toFixed(3)}ms (min ${Math.min(...ts).toFixed(3)})`);
}
// 1. probe
bench('1.probe', () => probeGsubAndCmap(ab, codePoints, option.sourceType));
// 2. Font.create
bench('2.Font.create', () => Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true }));
// 完整 Font.create 拿到对象
const font = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
// 3. optimize
bench('3.optimize', () => (font as any).optimize());
// 4. write (woff2)
const optimized = (font as any).optimize();
bench('5.write(woff2)', () => {
const f = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
const o = (f as any).optimize();
o.write({ type: 'woff2', hinting: false } as any);
});
// 端到端对比
bench('E2E', () => {
const f = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
const o = (f as any).optimize();
o.write({ type: 'woff2', hinting: false } as any);
}, 5);