Implement Performance.measure support for RN (JS side)

Summary:
Changelog: [Internal]

This adds JS side implementation (including the API) for the `Performance.measure` functionality, [as described in the corresponding standard](https://www.w3.org/TR/user-timing/#measure-method).

The JS part is separated from the C++ implementation (further down the stack) to help the review being more focused.

Reviewed By: mdvacca

Differential Revision: D41733190

fbshipit-source-id: 72b69f6bb332aed4b9477a186b0e818b62009220
This commit is contained in:
Ruslan Shestopalyuk
2022-12-06 17:34:24 -08:00
committed by Facebook GitHub Bot
parent 7a9ed166aa
commit d8bcab2be7
6 changed files with 141 additions and 6 deletions
@@ -22,4 +22,21 @@ void NativePerformance::mark(
PerformanceEntryReporter::getInstance().mark(name, startTime, duration);
}
void NativePerformance::clearMarks(
jsi::Runtime &rt,
std::optional<std::string> markName) {}
void NativePerformance::measure(
jsi::Runtime &rt,
std::string name,
double startTime,
double endTime,
std::optional<double> duration,
std::optional<std::string> startMark,
std::optional<std::string> endMark) {}
void NativePerformance::clearMeasures(
jsi::Runtime &rt,
std::optional<std::string> measureName) {}
} // namespace facebook::react
@@ -27,6 +27,18 @@ class NativePerformance : public NativePerformanceCxxSpec<NativePerformance>,
void
mark(jsi::Runtime &rt, std::string name, double startTime, double duration);
void clearMarks(jsi::Runtime &rt, std::optional<std::string> markName);
void measure(
jsi::Runtime &rt,
std::string name,
double startTime,
double endTime,
std::optional<double> duration,
std::optional<std::string> startMark,
std::optional<std::string> endMark);
void clearMeasures(jsi::Runtime &rt, std::optional<std::string> measureName);
private:
};
@@ -14,6 +14,17 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+mark?: (name: string, startTime: number, duration: number) => void;
+clearMarks?: (markName?: string) => void;
+measure?: (
name: string,
startTime: number,
endTime: number,
duration?: number,
startMark?: string,
endMark?: string,
) => void;
+clearMeasures?: (measureName?: string) => void;
}
export default (TurboModuleRegistry.get<Spec>('NativePerformanceCxx'): ?Spec);
@@ -15,6 +15,7 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export const RawPerformanceEntryTypeValues = {
UNDEFINED: 0,
MARK: 1,
MEASURE: 2,
};
export type RawPerformanceEntryType = number;
+90 -1
View File
@@ -36,6 +36,30 @@ export class PerformanceMark extends PerformanceEntry {
}
}
export type TimeStampOrName = HighResTimeStamp | string;
export type PerformanceMeasureOptions = {
detail?: DetailType,
start?: TimeStampOrName,
end?: TimeStampOrName,
duration?: HighResTimeStamp,
};
export class PerformanceMeasure extends PerformanceEntry {
detail: DetailType;
constructor(measureName: string, measureOptions?: PerformanceMeasureOptions) {
super({
name: measureName,
entryType: 'measure',
startTime: 0,
duration: measureOptions?.duration ?? 0,
});
if (measureOptions !== undefined) {
this.detail = measureOptions.detail;
}
}
}
/**
* Partial implementation of the Performance interface for RN,
* corresponding to the standard in
@@ -50,8 +74,73 @@ export default class Performance {
NativePerformance?.mark?.(markName, mark.startTime, mark.duration);
return mark;
}
clearMarks(markName?: string): void {
NativePerformance?.clearMarks?.(markName);
}
clearMarks(markName?: string): void {}
measure(
measureName: string,
startMarkOrOptions?: string | PerformanceMeasureOptions,
endMark?: string,
): PerformanceMeasure {
let options;
let startMarkName,
endMarkName = endMark,
duration,
startTime = 0,
endTime = 0;
if (typeof startMarkOrOptions === 'string') {
startMarkName = startMarkOrOptions;
} else if (startMarkOrOptions !== undefined) {
options = startMarkOrOptions;
if (endMark !== undefined) {
throw new TypeError(
"Performance.measure: Can't have both options and endMark",
);
}
if (options.start === undefined && options.end === undefined) {
throw new TypeError(
'Performance.measure: Must have at least one of start/end specified in options',
);
}
if (
options.start !== undefined &&
options.end !== undefined &&
options.duration !== undefined
) {
throw new TypeError(
"Performance.measure: Can't have both start/end and duration explicitly in options",
);
}
if (typeof options.start === 'number') {
startTime = options.start;
} else {
startMarkName = options.start;
}
if (typeof options.end === 'number') {
endTime = options.end;
} else {
endMarkName = options.end;
}
duration = options.duration ?? duration;
}
const measure = new PerformanceMeasure(measureName, options);
NativePerformance?.measure?.(
measureName,
startTime,
endTime,
duration,
startMarkName,
endMarkName,
);
return measure;
}
clearMeasures(measureName?: string): void {
NativePerformance?.clearMeasures?.(measureName);
}
now(): HighResTimeStamp {
return getCurrentTimeStamp();
@@ -14,11 +14,12 @@ import type {
} from './NativePerformanceObserver';
import warnOnce from '../Utilities/warnOnce';
import NativePerformanceObserver from './NativePerformanceObserver';
import NativePerformanceObserver, {
RawPerformanceEntryTypeValues,
} from './NativePerformanceObserver';
export type HighResTimeStamp = number;
// TODO: Extend once new types (such as event) are supported.
export type PerformanceEntryType = 'mark';
export type PerformanceEntryType = 'mark' | 'measure';
export class PerformanceEntry {
name: string;
@@ -52,7 +53,11 @@ export class PerformanceEntry {
function rawToPerformanceEntryType(
type: RawPerformanceEntryType,
): PerformanceEntryType {
return 'mark';
if (type === RawPerformanceEntryTypeValues.MARK) {
return 'mark';
} else {
return 'measure';
}
}
function rawToPerformanceEntry(entry: RawPerformanceEntry): PerformanceEntry {
@@ -232,5 +237,5 @@ export default class PerformanceObserver {
static supportedEntryTypes: $ReadOnlyArray<PerformanceEntryType> =
// TODO: add types once they are fully supported
Object.freeze(['mark']);
Object.freeze(['mark', 'measure']);
}