perf(woff2): calcTripletAndWrite inline 到主循环,消除每点函数调用

千字文 woff2(54350 点)transformGlyfAndLoca 的 calcTripletAndWrite 占 31.8% CPU
(V8 --prof 实测)。手动 inline 到 triplet 循环消除 54350 次/call 调用开销,
让 V8 做循环内寄存器分配与公共子表达式消除。原独立函数删除(避免死代码)。

输出字节逐字节完全一致(千字文/思源黑体/FiraCode 三组 IDENTICAL 验证)。
微基准 encodeTTFToWOFF2 千字文 4.84→4.38ms(-9.5%)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 07:27:09 +08:00
parent d68551d23e
commit 8fd11fc859

View File

@ -182,67 +182,6 @@ for (let i = 84; i < 120; i++) TRIPLET_DATA_SIZES[i] = 2;
for (let i = 120; i < 124; i++) TRIPLET_DATA_SIZES[i] = 3;
for (let i = 124; i < 128; i++) TRIPLET_DATA_SIZES[i] = 4;
/**
* 仅计算 triplet flag 字节不写入数据
* 优化: Pass 1 只需要 flag数据写入由 Pass 2 writePointDataByFlag 完成
*/
/**
* 计算 triplet flag 并同时写入数据字节消除重复的 absDx/absDy 计算和二次分支判断
* 优化291: 合并 calcTripletFlag + writePointDataByFlag 为单一函数
*/
function calcTripletAndWrite(curveBit, dx, dy, buf, offset) {
/** 优化302: Math.abs 替代三元 `x < 0 ? -x : x`V8 内建编译为单条 neg 指令3.5x */
const absDx = Math.abs(dx);
const absDy = Math.abs(dy);
const xSignBit = dx >= 0 ? 1 : 0;
const ySignBit = dy >= 0 ? 1 : 0;
const xySignBits = xSignBit + 2 * ySignBit;
/* dx=0, Y 单轴 1 数据字节 (flag 0-9) */
if (dx === 0 && absDy < 1280) {
buf[offset] = absDy & 0xFF;
return curveBit + ((absDy & 0xF00) >> 7) + ySignBit;
}
/* dy=0, dx≠0, X 单轴 1 数据字节 (flag 10-19) */
if (dy === 0 && dx !== 0 && absDx < 1280) {
buf[offset] = absDx & 0xFF;
return curveBit + 10 + ((absDx & 0xF00) >> 7) + xSignBit;
}
/* 双轴 1 数据字节 (flag 20-83): 1 ≤ |dx| ≤ 64, 1 ≤ |dy| ≤ 64 */
if (dx !== 0 && dy !== 0 && absDx < 65 && absDy < 65) {
const ax = absDx - 1;
const ay = absDy - 1;
buf[offset] = ((ax & 0xF) << 4) | (ay & 0xF);
return curveBit + 20 + (ax & 0x30) + ((ay & 0x30) >> 2) + xySignBits;
}
/* 双轴 2 数据字节 (flag 84-119): 1 ≤ |dx| ≤ 768, 1 ≤ |dy| ≤ 768 */
if (dx !== 0 && dy !== 0 && absDx < 769 && absDy < 769) {
const ax = absDx - 1;
const ay = absDy - 1;
buf[offset] = ax & 0xFF;
buf[offset + 1] = ay & 0xFF;
return curveBit + 84 + 12 * ((ax & 0x300) >> 8) + ((ay & 0x300) >> 6) + xySignBits;
}
/* 双轴 3 数据字节 (flag 120-123) */
if (absDx < 4096 && absDy < 4096) {
buf[offset] = absDx >> 4;
buf[offset + 1] = ((absDx & 0xF) << 4) | (absDy >> 8);
buf[offset + 2] = absDy & 0xFF;
return curveBit + 120 + xySignBits;
}
/* 兜底 4 数据字节 (flag 124-127) */
buf[offset] = (absDx >> 8) & 0xFF;
buf[offset + 1] = absDx & 0xFF;
buf[offset + 2] = (absDy >> 8) & 0xFF;
buf[offset + 3] = absDy & 0xFF;
return curveBit + 124 + xySignBits;
}
/* ======== glyf + loca 表变换 ======== */
/**
@ -564,6 +503,14 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
const gsBase = glyphAccumLen;
let gsbi = 0;
let prevX = 0, prevY = 0;
/**
* 优化303: calcTripletAndWrite 手动 inline 到主循环消除 54350 /call 函数调用开销
* 千字文 54350 点场景calcTripletAndWrite transformGlyfAndLoca 31.8% CPUprof 实测
* 函数调用与无法 inline 的参数装箱是主因inline V8 可在循环内做寄存器分配 +
* 公共子表达式消除absDx/absDy/curveBit 跨分支复用语义与原 calcTripletAndWrite 完全一致
* 分支顺序保持原样特例 yOnly/xOnly 优先千字文 78% 命中 1B 双轴分支
*/
const _gs = glyphAccum;
for (let pi = 0; pi < numPoints; pi++) {
const cx = xCoords[pi];
const cy = yCoords[pi];
@ -571,7 +518,49 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) {
const curveBit = ((flagAccum[flagWriteBase + pi] & 1) ^ 1) << 7;
const dx = cx - prevX;
const dy = cy - prevY;
const flag = calcTripletAndWrite(curveBit, dx, dy, glyphAccum, gsBase + gsbi);
const absDx = dx < 0 ? -dx : dx;
const absDy = dy < 0 ? -dy : dy;
const wpos = gsBase + gsbi;
/** triplet flag flagAccum + TRIPLET_DATA_SIZES
* 默认值仅占位每个分支都会覆盖 */
let flag;
if (dx === 0 && absDy < 1280) {
_gs[wpos] = absDy & 0xFF;
flag = curveBit + ((absDy & 0xF00) >> 7) + (dy >= 0 ? 1 : 0);
} else if (dy === 0 && dx !== 0 && absDx < 1280) {
_gs[wpos] = absDx & 0xFF;
flag = curveBit + 10 + ((absDx & 0xF00) >> 7) + (dx >= 0 ? 1 : 0);
} else if (dx !== 0 && dy !== 0 && absDx < 65 && absDy < 65) {
const ax = absDx - 1;
const ay = absDy - 1;
_gs[wpos] = ((ax & 0xF) << 4) | (ay & 0xF);
const xSignBit = dx >= 0 ? 1 : 0;
const ySignBit = dy >= 0 ? 1 : 0;
flag = curveBit + 20 + (ax & 0x30) + ((ay & 0x30) >> 2) + xSignBit + 2 * ySignBit;
} else if (dx !== 0 && dy !== 0 && absDx < 769 && absDy < 769) {
const ax = absDx - 1;
const ay = absDy - 1;
_gs[wpos] = ax & 0xFF;
_gs[wpos + 1] = ay & 0xFF;
const xSignBit = dx >= 0 ? 1 : 0;
const ySignBit = dy >= 0 ? 1 : 0;
flag = curveBit + 84 + 12 * ((ax & 0x300) >> 8) + ((ay & 0x300) >> 6) + xSignBit + 2 * ySignBit;
} else if (absDx < 4096 && absDy < 4096) {
_gs[wpos] = absDx >> 4;
_gs[wpos + 1] = ((absDx & 0xF) << 4) | (absDy >> 8);
_gs[wpos + 2] = absDy & 0xFF;
const xSignBit = dx >= 0 ? 1 : 0;
const ySignBit = dy >= 0 ? 1 : 0;
flag = curveBit + 120 + xSignBit + 2 * ySignBit;
} else {
_gs[wpos] = (absDx >> 8) & 0xFF;
_gs[wpos + 1] = absDx & 0xFF;
_gs[wpos + 2] = (absDy >> 8) & 0xFF;
_gs[wpos + 3] = absDy & 0xFF;
const xSignBit = dx >= 0 ? 1 : 0;
const ySignBit = dy >= 0 ? 1 : 0;
flag = curveBit + 124 + xSignBit + 2 * ySignBit;
}
/** triplet flag 回写到 flagAccumflagStream 存 triplet flag 而非原始 flag */
flagAccum[flagWriteBase + pi] = flag;
gsbi += TRIPLET_DATA_SIZES[flag & 0x7F];