mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Optimize performance.mark and performance.measure (#52429)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52429 Changelog: [internal] This implements a significant optimization for `performance.mark` and `performance.measure`. After these changes, they take 1/3 of the time they took before. We've seen these methods show up too often in traces (in the Hermes sampling profiler) so this should significantly reduce their overhead. I found out that most of the time spent in `performance.mark` and `performance.measure` was creating the `PerformanceMark` and `PerformanceMeasure` instances returned by those methods. I applied the same playbook I did for `ReactNativeElement` (avoiding private fields and `super()` calls, which yielded these wins. * Before | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | -------------------- | ------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'mark (default)' | '5579.80 ± 0.32%' | '5479.00' | '181368 ± 0.02%' | '182515' | 179218 | | 1 | 'mark (with custom startTime)' | '5759.72 ± 0.99%' | '5648.00' | '176162 ± 0.02%' | '177054' | 173620 | | 2 | 'measure (with start and end timestamps)' | '6506.38 ± 0.34%' | '6390.00' | '155503 ± 0.02%' | '156495' | 153696 | | 3 | 'measure (with mark names)' | '6770.94 ± 0.72%' | '6620.00' | '149833 ± 0.03%' | '151057' | 147691 | | 4 | 'clearMarks' | '785.89 ± 0.07%' | '771.00' | '1291356 ± 0.01%' | '1297017' | 1272442 | | 5 | 'clearMeasures' | '777.98 ± 0.06%' | '761.00' | '1303362 ± 0.01%' | '1314060' | 1285383 | | 6 | 'mark + clearMarks' | '5995.34 ± 1.37%' | '5779.00' | '171874 ± 0.03%' | '173040' | 166797 | | 7 | 'measure + clearMeasures (with start and end timestamps)' | '7040.28 ± 0.57%' | '6830.00' | '145289 ± 0.03%' | '146413' | 142040 | | 8 | 'measure + clearMeasures (with mark names)' | '7184.43 ± 0.40%' | '6990.00' | '141809 ± 0.03%' | '143062' | 139190 | * After | (index) | Task name | Latency average (ns) | Latency median (ns) | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ------- | --------------------------------------------------------- | -------------------- | ------------------- | -------------------------- | ------------------------- | ------- | | 0 | 'mark (default)' | '1678.19 ± 0.73%' | '1633.00' | '607139 ± 0.01%' | '612370' | 595882 | | 1 | 'mark (with custom startTime)' | '1920.23 ± 1.30%' | '1843.00' | '538217 ± 0.01%' | '542594' | 520772 | | 2 | 'measure (with start and end timestamps)' | '2651.72 ± 0.94%' | '2554.00' | '388312 ± 0.02%' | '391543' | 377114 | | 3 | 'measure (with mark names)' | '2815.84 ± 0.75%' | '2744.00' | '362303 ± 0.02%' | '364431' | 355134 | | 4 | 'clearMarks' | '743.82 ± 0.06%' | '731.00' | '1363190 ± 0.01%' | '1367989' | 1344417 | | 5 | 'clearMeasures' | '776.69 ± 0.07%' | '761.00' | '1306563 ± 0.01%' | '1314060' | 1287512 | | 6 | 'mark + clearMarks' | '2043.97 ± 1.26%' | '1973.00' | '504750 ± 0.01%' | '506842' | 489801 | | 7 | 'measure + clearMeasures (with start and end timestamps)' | '3048.39 ± 0.87%' | '2965.00' | '335285 ± 0.02%' | '337268' | 328042 | | 8 | 'measure + clearMeasures (with mark names)' | '3132.75 ± 0.80%' | '3065.00' | '324365 ± 0.02%' | '326264' | 319209 | Reviewed By: huntie Differential Revision: D77790874 fbshipit-source-id: baf7aca07d281fef4373956d125c63f006fab592
This commit is contained in:
committed by
Facebook GitHub Bot
parent
74c03b67e3
commit
9005d93e32
+20
-16
@@ -29,10 +29,14 @@ export type PerformanceEntryJSON = {
|
||||
};
|
||||
|
||||
export class PerformanceEntry {
|
||||
#name: string;
|
||||
#entryType: PerformanceEntryType;
|
||||
#startTime: DOMHighResTimeStamp;
|
||||
#duration: DOMHighResTimeStamp;
|
||||
// We don't use private fields because they're significantly slower to
|
||||
// initialize on construction and to access.
|
||||
// We also need these to be protected so they can be initialized in subclasses
|
||||
// where we avoid calling `super()` for performance reasons.
|
||||
__name: string;
|
||||
__entryType: PerformanceEntryType;
|
||||
__startTime: DOMHighResTimeStamp;
|
||||
__duration: DOMHighResTimeStamp;
|
||||
|
||||
constructor(init: {
|
||||
name: string,
|
||||
@@ -40,34 +44,34 @@ export class PerformanceEntry {
|
||||
startTime: DOMHighResTimeStamp,
|
||||
duration: DOMHighResTimeStamp,
|
||||
}) {
|
||||
this.#name = init.name;
|
||||
this.#entryType = init.entryType;
|
||||
this.#startTime = init.startTime;
|
||||
this.#duration = init.duration;
|
||||
this.__name = init.name;
|
||||
this.__entryType = init.entryType;
|
||||
this.__startTime = init.startTime;
|
||||
this.__duration = init.duration;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this.#name;
|
||||
return this.__name;
|
||||
}
|
||||
|
||||
get entryType(): PerformanceEntryType {
|
||||
return this.#entryType;
|
||||
return this.__entryType;
|
||||
}
|
||||
|
||||
get startTime(): DOMHighResTimeStamp {
|
||||
return this.#startTime;
|
||||
return this.__startTime;
|
||||
}
|
||||
|
||||
get duration(): DOMHighResTimeStamp {
|
||||
return this.#duration;
|
||||
return this.__duration;
|
||||
}
|
||||
|
||||
toJSON(): PerformanceEntryJSON {
|
||||
return {
|
||||
name: this.#name,
|
||||
entryType: this.#entryType,
|
||||
startTime: this.#startTime,
|
||||
duration: this.#duration,
|
||||
name: this.__name,
|
||||
entryType: this.__entryType,
|
||||
startTime: this.__startTime,
|
||||
duration: this.__duration,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@ export type PerformanceMeasureInit = {
|
||||
duration: DOMHighResTimeStamp,
|
||||
};
|
||||
|
||||
export class PerformanceMark extends PerformanceEntry {
|
||||
#detail: DetailType;
|
||||
class PerformanceMarkTemplate extends PerformanceEntry {
|
||||
// We don't use private fields because they're significantly slower to
|
||||
// initialize on construction and to access.
|
||||
_detail: DetailType;
|
||||
|
||||
// This constructor isn't really used. See `PerformanceMark` below.
|
||||
constructor(markName: string, markOptions?: PerformanceMarkOptions) {
|
||||
super({
|
||||
name: markName,
|
||||
@@ -41,18 +44,46 @@ export class PerformanceMark extends PerformanceEntry {
|
||||
});
|
||||
|
||||
if (markOptions) {
|
||||
this.#detail = markOptions.detail;
|
||||
this._detail = markOptions.detail;
|
||||
}
|
||||
}
|
||||
|
||||
get detail(): DetailType {
|
||||
return this.#detail;
|
||||
return this._detail;
|
||||
}
|
||||
}
|
||||
|
||||
export class PerformanceMeasure extends PerformanceEntry {
|
||||
#detail: DetailType;
|
||||
// This is the real value we're exporting where we define the class a function
|
||||
// so we don't need to call `super()` and we can avoid the performance penalty
|
||||
// of the current code transpiled with Babel.
|
||||
// We should remove this when we have built-in support for classes in the
|
||||
// runtime.
|
||||
export const PerformanceMark: typeof PerformanceMarkTemplate =
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
function PerformanceMark(
|
||||
this: PerformanceMarkTemplate,
|
||||
markName: string,
|
||||
markOptions?: PerformanceMarkOptions,
|
||||
) {
|
||||
this.__name = markName;
|
||||
this.__entryType = 'mark';
|
||||
this.__startTime = markOptions?.startTime ?? performance.now();
|
||||
this.__duration = 0;
|
||||
|
||||
if (markOptions) {
|
||||
this._detail = markOptions.detail;
|
||||
}
|
||||
};
|
||||
|
||||
// $FlowExpectedError[prop-missing]
|
||||
PerformanceMark.prototype = PerformanceMarkTemplate.prototype;
|
||||
|
||||
class PerformanceMeasureTemplate extends PerformanceEntry {
|
||||
// We don't use private fields because they're significantly slower to
|
||||
// initialize on construction and to access.
|
||||
_detail: DetailType;
|
||||
|
||||
// This constructor isn't really used. See `PerformanceMeasure` below.
|
||||
constructor(measureName: string, measureOptions: PerformanceMeasureInit) {
|
||||
super({
|
||||
name: measureName,
|
||||
@@ -62,11 +93,32 @@ export class PerformanceMeasure extends PerformanceEntry {
|
||||
});
|
||||
|
||||
if (measureOptions) {
|
||||
this.#detail = measureOptions.detail;
|
||||
this._detail = measureOptions.detail;
|
||||
}
|
||||
}
|
||||
|
||||
get detail(): DetailType {
|
||||
return this.#detail;
|
||||
return this._detail;
|
||||
}
|
||||
}
|
||||
|
||||
// We do the same here as we do for `PerformanceMark` for performance reasons.
|
||||
export const PerformanceMeasure: typeof PerformanceMeasureTemplate =
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
function PerformanceMeasure(
|
||||
this: PerformanceMeasureTemplate,
|
||||
measureName: string,
|
||||
measureOptions: PerformanceMeasureInit,
|
||||
) {
|
||||
this.__name = measureName;
|
||||
this.__entryType = 'measure';
|
||||
this.__startTime = measureOptions.startTime;
|
||||
this.__duration = measureOptions.duration;
|
||||
|
||||
if (measureOptions) {
|
||||
this._detail = measureOptions.detail;
|
||||
}
|
||||
};
|
||||
|
||||
// $FlowExpectedError[prop-missing]
|
||||
PerformanceMeasure.prototype = PerformanceMeasureTemplate.prototype;
|
||||
|
||||
Reference in New Issue
Block a user