perf(reconciler): implement high-speed prop templating

This commit optimizes the data payload generation by replacing the previous slow, generic fingerprinting logic with a fast, specialized one. The previous method using `JSON.stringify` introduced a 20-50ms latency bottleneck; the new implementation uses a targeted string concatenation method to achieve the same payload reduction in about 10ms.
This commit is contained in:
ByteAtATime 2025-07-01 10:13:49 -07:00
parent 4a36997bb2
commit 87a7f946fa
No known key found for this signature in database
2 changed files with 70 additions and 42 deletions

View file

@ -15,7 +15,7 @@ import {
clearCommitBuffer,
commitBuffer
} from './state';
import { writeOutput } from './io';
import { writeLog, writeOutput } from './io';
import { serializeProps, optimizeCommitBuffer, getComponentDisplayName } from './utils';
import React, { type ReactNode } from 'react';
@ -214,7 +214,9 @@ export const hostConfig: HostConfig<
prepareForCommit: () => null,
resetAfterCommit: () => {
if (commitBuffer.length > 0) {
const now = Date.now();
const optimizedPayload = optimizeCommitBuffer(commitBuffer);
writeLog(`time to optimize: ${Date.now() - now}ms`);
writeOutput({
type: 'BATCH_UPDATE',
payload: optimizedPayload

View file

@ -57,13 +57,37 @@ export function serializeProps(props: Record<string, unknown>): Record<string, u
return serialized;
}
function createStableFingerprint(
props: Record<string, unknown>,
namedChildren?: Record<string, number>
): string {
let result = '';
const propKeys = Object.keys(props).sort();
for (const key of propKeys) {
if (key === 'ref') continue;
result += `${key}=${String(props[key])};`;
}
if (namedChildren) {
const childKeys = Object.keys(namedChildren).sort();
if (childKeys.length > 0) {
result += '::nc::'; // named children
for (const key of childKeys) {
result += `${key}=${String(namedChildren[key])};`;
}
}
}
return result;
}
export function optimizeCommitBuffer(buffer: Command[]): Command[] {
const CHILD_OP_THRESHOLD = 10;
const PROPS_TEMPLATE_THRESHOLD = 5;
const childOpsByParent = new Map<ParentInstance['id'], Command[]>();
const updatePropsOps = [] as Extract<Command, { type: 'UPDATE_PROPS' }>[];
const otherOps: Command[] = [];
const updatePropsOps: Extract<Command, { type: 'UPDATE_PROPS' }>[] = [];
const otherNonUpdateOps: Command[] = [];
for (const op of buffer) {
if (op.type === 'UPDATE_PROPS') {
@ -76,41 +100,45 @@ export function optimizeCommitBuffer(buffer: Command[]): Command[] {
const parentId = op.payload.parentId;
childOpsByParent.set(parentId, (childOpsByParent.get(parentId) ?? []).concat(op));
} else {
otherOps.push(op);
otherNonUpdateOps.push(op);
}
}
const finalOps: Command[] = [...otherOps];
const finalOps: Command[] = [...otherNonUpdateOps];
for (const [parentId, ops] of childOpsByParent.entries()) {
if (ops.length <= CHILD_OP_THRESHOLD) {
finalOps.push(...ops);
continue;
}
const parentInstance = parentId === 'root' ? root : instances.get(parentId as number);
if (parentInstance && 'children' in parentInstance) {
const childrenIds = parentInstance.children.map(({ id }) => id);
finalOps.push({
type: 'REPLACE_CHILDREN',
payload: { parentId, childrenIds }
});
} else {
finalOps.push(...ops);
const parentInstance = parentId === 'root' ? root : instances.get(parentId as number);
if (parentInstance && 'children' in parentInstance) {
const childrenIds = parentInstance.children.map(({ id }) => id);
finalOps.push({ type: 'REPLACE_CHILDREN', payload: { parentId, childrenIds } });
} else {
finalOps.push(...ops);
}
}
}
if (updatePropsOps.length < PROPS_TEMPLATE_THRESHOLD * 2) {
finalOps.push(...updatePropsOps);
return finalOps;
}
const propsToIdMap = new Map<string, number[]>();
const idToUpdatePropsMap = new Map<number, Extract<Command, { type: 'UPDATE_PROPS' }>>();
const idToPayloadMap = new Map<number, Extract<Command, { type: 'UPDATE_PROPS' }>['payload']>();
for (const op of updatePropsOps) {
idToUpdatePropsMap.set(op.payload.id, op);
const propsToFingerprint = { ...op.payload.props };
delete (propsToFingerprint as { ref?: unknown }).ref; // ref is instance-specific
const fingerprint = JSON.stringify(propsToFingerprint);
const payload = op.payload;
idToPayloadMap.set(payload.id, payload);
const fingerprint = createStableFingerprint(payload.props, payload.namedChildren);
propsToIdMap.set(fingerprint, (propsToIdMap.get(fingerprint) ?? []).concat(op.payload.id));
const ids = propsToIdMap.get(fingerprint);
if (ids) {
ids.push(payload.id);
} else {
propsToIdMap.set(fingerprint, [payload.id]);
}
}
const handledIds = new Set<number>();
@ -118,28 +146,26 @@ export function optimizeCommitBuffer(buffer: Command[]): Command[] {
for (const ids of propsToIdMap.values()) {
if (ids.length > PROPS_TEMPLATE_THRESHOLD) {
const templateId = ids[0];
const prototypeOp = idToUpdatePropsMap.get(templateId);
const prototypePayload = idToPayloadMap.get(templateId)!;
if (prototypeOp) {
finalOps.push({
type: 'DEFINE_PROPS_TEMPLATE',
payload: {
templateId,
props: prototypeOp.payload.props,
namedChildren: prototypeOp.payload.namedChildren
}
});
finalOps.push({
type: 'DEFINE_PROPS_TEMPLATE',
payload: {
templateId,
props: prototypePayload.props,
namedChildren: prototypePayload.namedChildren
}
});
finalOps.push({
type: 'APPLY_PROPS_TEMPLATE',
payload: {
templateId: templateId,
targetIds: ids
}
});
finalOps.push({
type: 'APPLY_PROPS_TEMPLATE',
payload: {
templateId: templateId,
targetIds: ids
}
});
ids.forEach((id) => handledIds.add(id));
}
ids.forEach((id) => handledIds.add(id));
}
}