[Perf Tracks]: Clear potentially large measures (#34803)

Fixes https://github.com/facebook/react/issues/34770.

We need to clear measures at some point, otherwise all these copies of
props that we end up recording will allocate too much memory in
Chromium. This adds `performance.clearMeasures(...)` calls to such cases
in DEV.

Validated that entries are still shown on Performance panel timeline.
This commit is contained in:
Ruslan Lesiutin
2025-10-13 17:42:13 -04:00
committed by GitHub
parent 47905a7950
commit b9ec735de2
2 changed files with 48 additions and 18 deletions
+27 -10
View File
@@ -102,6 +102,7 @@ export function logComponentRender(
const entryName =
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
const debugTask = componentInfo.debugTask;
const measureName = '\u200b' + entryName;
if (__DEV__ && debugTask) {
const properties: Array<[string, string]> = [];
if (componentInfo.key != null) {
@@ -110,9 +111,10 @@ export function logComponentRender(
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0, '');
}
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(performance, '\u200b' + entryName, {
performance.measure.bind(performance, measureName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
detail: {
@@ -125,9 +127,10 @@ export function logComponentRender(
},
}),
);
performance.clearMeasures(measureName);
} else {
console.timeStamp(
'\u200b' + entryName,
measureName,
startTime < 0 ? 0 : startTime,
childrenEndTime,
trackNames[trackIdx],
@@ -152,6 +155,7 @@ export function logComponentAborted(
const isPrimaryEnv = env === rootEnv;
const entryName =
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
const measureName = '\u200b' + entryName;
if (__DEV__) {
const properties: Array<[string, string]> = [
[
@@ -165,7 +169,8 @@ export function logComponentAborted(
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0, '');
}
performance.measure('\u200b' + entryName, {
performance.measure(measureName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
detail: {
@@ -178,9 +183,10 @@ export function logComponentAborted(
},
},
});
performance.clearMeasures(measureName);
} else {
console.timeStamp(
entryName,
measureName,
startTime < 0 ? 0 : startTime,
childrenEndTime,
trackNames[trackIdx],
@@ -206,6 +212,7 @@ export function logComponentErrored(
const isPrimaryEnv = env === rootEnv;
const entryName =
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
const measureName = '\u200b' + entryName;
if (__DEV__) {
const message =
typeof error === 'object' &&
@@ -222,7 +229,8 @@ export function logComponentErrored(
if (componentInfo.props != null) {
addObjectToProperties(componentInfo.props, properties, 0, '');
}
performance.measure('\u200b' + entryName, {
performance.measure(measureName, {
start: startTime < 0 ? 0 : startTime,
end: childrenEndTime,
detail: {
@@ -235,9 +243,10 @@ export function logComponentErrored(
},
},
});
performance.clearMeasures(measureName);
} else {
console.timeStamp(
entryName,
measureName,
startTime < 0 ? 0 : startTime,
childrenEndTime,
trackNames[trackIdx],
@@ -397,6 +406,7 @@ export function logComponentAwaitAborted(
},
}),
);
performance.clearMeasures(entryName);
} else {
console.timeStamp(
entryName,
@@ -453,6 +463,7 @@ export function logComponentAwaitErrored(
},
}),
);
performance.clearMeasures(entryName);
} else {
console.timeStamp(
entryName,
@@ -514,6 +525,7 @@ export function logComponentAwait(
},
}),
);
performance.clearMeasures(entryName);
} else {
console.timeStamp(
entryName,
@@ -538,6 +550,7 @@ export function logIOInfoErrored(
const description = getIODescription(error);
const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
const debugTask = ioInfo.debugTask;
const measureName = '\u200b' + entryName;
if (__DEV__ && debugTask) {
const message =
typeof error === 'object' &&
@@ -550,9 +563,10 @@ export function logIOInfoErrored(
const properties = [['rejected with', message]];
const tooltipText =
getIOLongName(ioInfo, description, ioInfo.env, rootEnv) + ' Rejected';
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(performance, '\u200b' + entryName, {
performance.measure.bind(performance, measureName, {
start: startTime < 0 ? 0 : startTime,
end: endTime,
detail: {
@@ -565,9 +579,10 @@ export function logIOInfoErrored(
},
}),
);
performance.clearMeasures(measureName);
} else {
console.timeStamp(
entryName,
measureName,
startTime < 0 ? 0 : startTime,
endTime,
IO_TRACK,
@@ -590,6 +605,7 @@ export function logIOInfo(
const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
const color = getIOColor(entryName);
const debugTask = ioInfo.debugTask;
const measureName = '\u200b' + entryName;
if (__DEV__ && debugTask) {
const properties: Array<[string, string]> = [];
if (typeof value === 'object' && value !== null) {
@@ -605,7 +621,7 @@ export function logIOInfo(
);
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(performance, '\u200b' + entryName, {
performance.measure.bind(performance, measureName, {
start: startTime < 0 ? 0 : startTime,
end: endTime,
detail: {
@@ -618,9 +634,10 @@ export function logIOInfo(
},
}),
);
performance.clearMeasures(measureName);
} else {
console.timeStamp(
entryName,
measureName,
startTime < 0 ? 0 : startTime,
endTime,
IO_TRACK,
+21 -8
View File
@@ -133,6 +133,7 @@ function logComponentTrigger(
} else {
performance.measure(trigger, reusableComponentOptions);
}
performance.clearMeasures(trigger);
}
}
@@ -200,7 +201,7 @@ const reusableComponentOptions: PerformanceMeasureOptions = {
},
};
const resuableChangedPropsEntry = ['Changed Props', ''];
const reusableChangedPropsEntry = ['Changed Props', ''];
const DEEP_EQUALITY_WARNING =
'This component received deeply equal props. It might benefit from useMemo or the React Compiler in its owner.';
@@ -261,7 +262,7 @@ export function logComponentRender(
alternate.memoizedProps !== props
) {
// If this is an update, we'll diff the props and emit which ones changed.
const properties: Array<[string, string]> = [resuableChangedPropsEntry];
const properties: Array<[string, string]> = [reusableChangedPropsEntry];
const isDeeplyEqual = addObjectDiffToProperties(
alternate.memoizedProps,
props,
@@ -293,18 +294,20 @@ export function logComponentRender(
reusableComponentOptions.start = startTime;
reusableComponentOptions.end = endTime;
const measureName = '\u200b' + name;
if (debugTask != null) {
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(
performance,
'\u200b' + name,
measureName,
reusableComponentOptions,
),
);
} else {
performance.measure('\u200b' + name, reusableComponentOptions);
performance.measure(measureName, reusableComponentOptions);
}
performance.clearMeasures(measureName);
} else {
if (debugTask != null) {
debugTask.run(
@@ -421,14 +424,17 @@ export function logComponentErrored(
},
},
};
const measureName = '\u200b' + name;
if (__DEV__ && debugTask) {
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(performance, '\u200b' + name, options),
performance.measure.bind(performance, measureName, options),
);
} else {
performance.measure('\u200b' + name, options);
performance.measure(measureName, options);
}
performance.clearMeasures(measureName);
} else {
console.timeStamp(
name,
@@ -488,14 +494,16 @@ function logComponentEffectErrored(
},
};
const debugTask = fiber._debugTask;
const measureName = '\u200b' + name;
if (debugTask) {
debugTask.run(
// $FlowFixMe[method-unbinding]
performance.measure.bind(performance, '\u200b' + name, options),
performance.measure.bind(performance, measureName, options),
);
} else {
performance.measure('\u200b' + name, options);
performance.measure(measureName, options);
}
performance.clearMeasures(measureName);
} else {
console.timeStamp(
name,
@@ -762,6 +770,7 @@ export function logBlockingStart(
} else {
performance.measure(label, measureOptions);
}
performance.clearMeasures(label);
} else {
console.timeStamp(
label,
@@ -867,6 +876,7 @@ export function logGestureStart(
} else {
performance.measure(label, measureOptions);
}
performance.clearMeasures(label);
} else {
console.timeStamp(
label,
@@ -1007,6 +1017,7 @@ export function logTransitionStart(
} else {
performance.measure(label, measureOptions);
}
performance.clearMeasures(label);
} else {
console.timeStamp(
label,
@@ -1238,6 +1249,7 @@ export function logRecoveredRenderPhase(
} else {
performance.measure('Recovered', options);
}
performance.clearMeasures('Recovered');
} else {
console.timeStamp(
'Recovered',
@@ -1449,6 +1461,7 @@ export function logCommitErrored(
} else {
performance.measure('Errored', options);
}
performance.clearMeasures('Errored');
} else {
console.timeStamp(
'Errored',