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>
This commit is contained in:
崮生(子虚) 2026-07-24 09:27:19 +08:00
parent 308df8ef4d
commit 6b28ba3d74
5 changed files with 61 additions and 8 deletions

8
_cmpttf.entry.ts Normal file
View File

@ -0,0 +1,8 @@
import { readFileSync, writeFileSync } from 'fs';
import { fontSubset } from './backend/font_util/font.js';
const buf = readFileSync('font/令东齐伋复刻体.ttf');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const text = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔';
const out = fontSubset(ab, text, {sourceType:'ttf',outType:'ttf'});
writeFileSync('/tmp/ttf_opt313.bin', out);
console.log('len', out.length);

8
_cmpttf2.entry.ts Normal file
View File

@ -0,0 +1,8 @@
import { readFileSync, writeFileSync } from 'fs';
import { fontSubset } from './backend/font_util/font.js';
const buf = readFileSync('font/令东齐伋复刻体.ttf');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const text = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔';
const out = fontSubset(ab, text, {sourceType:'ttf',outType:'ttf'});
writeFileSync('/tmp/ttf_clean.bin', out);
console.log('len', out.length);

21
_prof_stage.entry.ts Normal file
View File

@ -0,0 +1,21 @@
import { fontSubset } from './backend/font_util/font.js';
import { readFileSync } from 'fs';
const buf = readFileSync('font/令东齐伋复刻体.ttf');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const text = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔';
const g:any = globalThis;
const N=300;
fontSubset(ab, text, {sourceType:'ttf',outType:'woff2'}); // warmup
g.__wt.flagDecode=0; g.__wt.xDecode=0; g.__wt.yTriplet=0;
import('perf_hooks').then(({performance})=>{
const t0=performance.now();
for(let i=0;i<N;i++) fontSubset(ab, text, {sourceType:'ttf',outType:'woff2'});
const total=performance.now()-t0;
console.log(`woff2 total avg=${(total/N).toFixed(3)}ms`);
const f=g.__wt.flagDecode/N, x=g.__wt.xDecode/N, y=g.__wt.yTriplet/N;
const tInside=(f+x+y);
console.log(` flagDecode: ${f.toFixed(3)}ms (${(f/total*100).toFixed(1)}%)`);
console.log(` xDecode: ${x.toFixed(3)}ms (${(x/total*100).toFixed(1)}%)`);
console.log(` y+triplet: ${y.toFixed(3)}ms (${(y/total*100).toFixed(1)}%)`);
console.log(` 循环内合计: ${tInside.toFixed(3)}ms (${(tInside/total*100).toFixed(1)}%)`);
});

18
_prof_v8.entry.ts Normal file
View File

@ -0,0 +1,18 @@
import { fontSubset } from './backend/font_util/font.js';
import { readFileSync, writeFileSync } from 'fs';
import { Session } from 'inspector';
const buf = readFileSync('font/令东齐伋复刻体.ttf');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const text = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔';
const opt = {sourceType:'ttf' as const, outType:'woff2' as const};
(async()=>{
for(let i=0;i<50;i++) fontSubset(ab, text, opt);
const s = new Session();
s.connect();
await new Promise<void>(r=>s.post('Profiler.enable',()=>r()));
await new Promise<void>(r=>s.post('Profiler.start',()=>r()));
for(let i=0;i<2000;i++) fontSubset(ab, text, opt);
const prof:any = await new Promise(r=>s.post('Profiler.stop',(_,v)=>r(v)));
writeFileSync('/tmp/prof.json', JSON.stringify(prof));
console.log('profile written, samples=', prof.profile.samples.length);
})();

View File

@ -95,8 +95,10 @@ var _default = exports.default = _table.default.create('glyf', [], {
var numberOfContours = view.getInt16(vOff, false);
/** 非(component 引用的) simple 字形走快路径compound 及 component 走完整 parse */
if (numberOfContours >= 0 && !componentGids[index]) {
/** 310: header(bbox) + endPtsOfContours( metrics)
* 跳过 instructions/flags/坐标解码 parseSimpleGlyfglyph.read 第一热点 */
/** 310+313: header(bbox) + endPt _totalPoints metrics
* 存原始字节引用跳过 instructions/flags/坐标解码 parseSimpleGlyfglyph.read 第一热点
* 优化313: 不再分配 endPtsOfContours 数组下游optimizettf 快路径分支sizeof/write 拷贝
* 原始字节均不消费它_totalPoints 只需最后一个 endPt 即可算出 */
var glyfObj = {};
glyfObj.xMin = view.getInt16(vOff + 2, false);
glyfObj.yMin = view.getInt16(vOff + 4, false);
@ -104,13 +106,9 @@ var _default = exports.default = _table.default.create('glyf', [], {
glyfObj.yMax = view.getInt16(vOff + 8, false);
glyfObj._origGlyfRef = { buffer: fullBuf, byteOffset: fullBufOff + gStart, length: gEnd - gStart };
if (numberOfContours > 0) {
var endPts = new Array(numberOfContours);
for (var ei2 = 0; ei2 < numberOfContours; ei2++) {
endPts[ei2] = view.getUint16(vOff + 10 + ei2 * 2, false);
}
glyfObj.endPtsOfContours = endPts;
glyfObj._numContours = numberOfContours;
glyfObj._totalPoints = endPts[numberOfContours - 1] + 1;
/** 最后一个 endPt 在 vOff + 10 + (numContours-1)*2+1 即总点数 */
glyfObj._totalPoints = view.getUint16(vOff + 10 + (numberOfContours - 1) * 2, false) + 1;
} else {
glyfObj._numContours = 0;
glyfObj._totalPoints = 0;