From f03268b896f933bf8ac46399baf81be74ff5e680 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Thu, 22 May 2025 10:16:45 -0700 Subject: [PATCH] Replace DOMHighResTimeStamp alias in ReactCommon with new abstractions (#51512) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51512 Pull Request resolved: https://github.com/facebook/react-native/pull/50585 # Changelog: [Internal] Replaces `DOMHighResTimeStamp` alias completely in `ReactCommon` with `HighResTimeStamp`. `DOMHighResTimeStamp` as a type is now expected to be used only in JavaScript. I didn't update places where we explcitly use `std::chrono::high_resolution_clock`, since it is platform-specific and there is no guarantee that `std::chrono::high_resolution_clock` == `std::chrono::steady_clock`. Also, places that are isolated and not part of the Web Performance APIs, such as Telemetry for Fabric, are not updates as part of this diff. Although these subsystems are also using `std::chrono::steady_clock` as a low-level representation, they are not sharing it with other parts of the React Native core. Reviewed By: rubennorte Differential Revision: D75185613 fbshipit-source-id: 889719368de163e6f529689df6cc16d816fde66c --- .../ReactCommon/cxxreact/JSExecutor.cpp | 6 +- .../ReactCommon/cxxreact/JSExecutor.h | 3 +- .../jsiexecutor/jsireact/JSIExecutor.cpp | 4 +- .../network/NetworkReporter.cpp | 10 +- .../network/NetworkReporter.h | 10 +- .../tracing/EventLoopReporter.cpp | 1 + .../tracing/PerformanceTracer.cpp | 1 + .../tracing/PerformanceTracer.h | 2 + .../react/bridging/tests/BridgingTest.cpp | 2 +- .../NativeIntersectionObserver.h | 2 +- .../webperformance/NativePerformance.cpp | 22 +- .../webperformance/NativePerformance.h | 36 +-- .../performance/timeline/PerformanceEntry.h | 21 +- .../timeline/PerformanceEntryBuffer.h | 4 +- .../timeline/PerformanceEntryReporter.cpp | 51 ++-- .../timeline/PerformanceEntryReporter.h | 40 +-- .../timeline/PerformanceObserver.h | 13 +- .../tests/PerformanceEntryReporterTest.cpp | 260 ++++++++++++---- .../tests/PerformanceObserverTest.cpp | 280 +++++++++++++++--- .../react/renderer/core/EventLogger.h | 3 +- .../react/renderer/core/RawEvent.h | 2 +- .../core/tests/EventQueueProcessorTest.cpp | 2 +- .../events/EventPerformanceLogger.cpp | 6 +- .../observers/events/EventPerformanceLogger.h | 10 +- .../intersection/IntersectionObserver.cpp | 8 +- .../intersection/IntersectionObserver.h | 12 +- .../IntersectionObserverManager.cpp | 6 +- .../IntersectionObserverManager.h | 7 +- .../RuntimeScheduler_Modern.cpp | 10 +- .../tests/RuntimeSchedulerTest.cpp | 12 +- .../renderer/uimanager/UIManagerMountHook.h | 4 +- .../ReactCommon/react/timing/primitives.h | 17 -- .../react/timing/tests/PrimitivesTest.cpp | 33 --- .../HermesPerfettoDataSource.cpp | 2 +- .../reactperflogger/ReactPerfetto.cpp | 16 +- .../reactperflogger/ReactPerfetto.h | 3 +- .../reactperflogger/ReactPerfettoLogger.cpp | 21 +- .../reactperflogger/ReactPerfettoLogger.h | 7 +- 38 files changed, 620 insertions(+), 329 deletions(-) diff --git a/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp b/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp index eeca4b3c929..ef6a44c0f12 100644 --- a/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp +++ b/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp @@ -10,10 +10,8 @@ #include "RAMBundleRegistry.h" #include -#include #include -#include namespace facebook::react { @@ -29,8 +27,8 @@ std::string JSExecutor::getSyntheticBundlePath( return buffer.data(); } -double JSExecutor::performanceNow() { - return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now()); +HighResTimeStamp JSExecutor::performanceNow() { + return HighResTimeStamp::now(); } jsinspector_modern::RuntimeTargetDelegate& diff --git a/packages/react-native/ReactCommon/cxxreact/JSExecutor.h b/packages/react-native/ReactCommon/cxxreact/JSExecutor.h index 469f4ca6fd7..775e3971f58 100644 --- a/packages/react-native/ReactCommon/cxxreact/JSExecutor.h +++ b/packages/react-native/ReactCommon/cxxreact/JSExecutor.h @@ -14,6 +14,7 @@ #include #include #include +#include #ifndef RN_EXPORT #define RN_EXPORT __attribute__((visibility("default"))) @@ -138,7 +139,7 @@ class RN_EXPORT JSExecutor { uint32_t bundleId, const std::string& bundlePath); - static double performanceNow(); + static HighResTimeStamp performanceNow(); /** * Get a reference to the \c RuntimeTargetDelegate owned (or implemented) by diff --git a/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index 6b226006768..39be0f0f055 100644 --- a/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -556,7 +556,9 @@ void bindNativePerformanceNow(Runtime& runtime) { [](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value* args, - size_t count) { return Value(JSExecutor::performanceNow()); })); + size_t /*count*/) { + return JSExecutor::performanceNow().toDOMHighResTimeStamp(); + })); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp index 1eb6e19f20f..9f207a0a236 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp @@ -78,7 +78,7 @@ void NetworkReporter::reportRequestStart( int encodedDataLength, const std::optional& redirectResponse) { if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { - double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + auto now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); // All builds: Annotate PerformanceResourceTiming metadata { @@ -127,7 +127,7 @@ void NetworkReporter::reportRequestStart( void NetworkReporter::reportConnectionTiming(const std::string& requestId) { if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { - double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + auto now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); // All builds: Annotate PerformanceResourceTiming metadata { @@ -168,7 +168,7 @@ void NetworkReporter::reportResponseStart( const ResponseInfo& responseInfo, int encodedDataLength) { if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { - double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + auto now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); // All builds: Annotate PerformanceResourceTiming metadata { @@ -205,7 +205,7 @@ void NetworkReporter::reportResponseStart( void NetworkReporter::reportDataReceived(const std::string& requestId) { if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { - double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + auto now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); // All builds: Annotate PerformanceResourceTiming metadata { @@ -233,7 +233,7 @@ void NetworkReporter::reportResponseEnd( const std::string& requestId, int encodedDataLength) { if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { - double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + auto now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); // All builds: Report PerformanceResourceTiming event { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h index 4227deca3df..90701c412bf 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h @@ -35,11 +35,11 @@ using FrontendChannel = std::function; */ struct ResourceTimingData { std::string url; - DOMHighResTimeStamp fetchStart; - DOMHighResTimeStamp requestStart; - std::optional connectStart; - std::optional connectEnd; - std::optional responseStart; + HighResTimeStamp fetchStart; + HighResTimeStamp requestStart; + std::optional connectStart; + std::optional connectEnd; + std::optional responseStart; std::optional responseStatus; }; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.cpp index 0c9ccba0208..535fe2af5f1 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.cpp @@ -8,6 +8,7 @@ #include "EventLoopReporter.h" #if defined(REACT_NATIVE_DEBUGGER_ENABLED) +#include #include "PerformanceTracer.h" #endif diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index 0197b82c7f0..837e484efb0 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -9,6 +9,7 @@ #include "Timing.h" #include +#include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index 9acc978dfc7..c01ff8b63f6 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -13,6 +13,8 @@ #include +#include + #include #include #include diff --git a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp index db7287e79d1..804d4a1f3f3 100644 --- a/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp +++ b/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp @@ -784,7 +784,7 @@ TEST_F(BridgingTest, highResTimeStampTest) { bridging::fromJs( rt, bridging::toJs(rt, timestamp), invoker)); - HighResDuration duration = HighResDuration::fromNanoseconds(1); + auto duration = HighResDuration::fromNanoseconds(1); EXPECT_EQ( duration, bridging::fromJs( diff --git a/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/NativeIntersectionObserver.h b/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/NativeIntersectionObserver.h index 9ea6487b9f7..bc6f9f5a478 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/NativeIntersectionObserver.h +++ b/packages/react-native/ReactCommon/react/nativemodule/intersectionobserver/NativeIntersectionObserver.h @@ -49,7 +49,7 @@ using NativeIntersectionObserverEntry = // isIntersectingAboveThresholds bool, // time - double>; + HighResTimeStamp>; template <> struct Bridging diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp index e8b60299d66..d6ab76d20d8 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp @@ -116,30 +116,31 @@ std::shared_ptr tryGetObserver( NativePerformance::NativePerformance(std::shared_ptr jsInvoker) : NativePerformanceCxxSpec(std::move(jsInvoker)) {} -double NativePerformance::now(jsi::Runtime& /*rt*/) { +HighResTimeStamp NativePerformance::now(jsi::Runtime& /*rt*/) { return JSExecutor::performanceNow(); } -double NativePerformance::markWithResult( +HighResTimeStamp NativePerformance::markWithResult( jsi::Runtime& rt, std::string name, - std::optional startTime) { + std::optional startTime) { auto entry = PerformanceEntryReporter::getInstance()->reportMark(name, startTime); return entry.startTime; } -std::tuple NativePerformance::measureWithResult( +std::tuple +NativePerformance::measureWithResult( jsi::Runtime& runtime, std::string name, - double startTime, - double endTime, - std::optional duration, + HighResTimeStamp startTime, + HighResTimeStamp endTime, + std::optional duration, std::optional startMark, std::optional endMark) { auto reporter = PerformanceEntryReporter::getInstance(); - DOMHighResTimeStamp startTimeValue = startTime; + HighResTimeStamp startTimeValue = startTime; // If the start time mark name is specified, it takes precedence over the // startTime parameter, which can be set to 0 by default from JavaScript. if (startMark) { @@ -151,7 +152,7 @@ std::tuple NativePerformance::measureWithResult( } } - DOMHighResTimeStamp endTimeValue = endTime; + HighResTimeStamp endTimeValue = endTime; // If the end time mark name is specified, it takes precedence over the // startTime parameter, which can be set to 0 by default from JavaScript. if (endMark) { @@ -345,7 +346,8 @@ void NativePerformance::observe( return; } - auto durationThreshold = options.durationThreshold.value_or(0.0); + auto durationThreshold = + options.durationThreshold.value_or(HighResDuration::zero()); // observer of type multiple if (options.entryTypes.has_value()) { diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.h b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.h index 1a23499f988..d2602b0b78b 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.h +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.h @@ -32,7 +32,7 @@ using NativePerformancePerformanceObserverObserveOptions = // buffered std::optional, // durationThreshold - std::optional>; + std::optional>; template <> struct Bridging { @@ -52,21 +52,21 @@ struct Bridging { struct NativePerformanceEntry { std::string name; PerformanceEntryType entryType; - DOMHighResTimeStamp startTime; - DOMHighResTimeStamp duration; + HighResTimeStamp startTime; + HighResDuration duration; // For PerformanceEventTiming only - std::optional processingStart; - std::optional processingEnd; + std::optional processingStart; + std::optional processingEnd; std::optional interactionId; // For PerformanceResourceTiming only - std::optional fetchStart; - std::optional requestStart; - std::optional connectStart; - std::optional connectEnd; - std::optional responseStart; - std::optional responseEnd; + std::optional fetchStart; + std::optional requestStart; + std::optional connectStart; + std::optional connectEnd; + std::optional responseStart; + std::optional responseEnd; std::optional responseStatus; }; @@ -86,23 +86,23 @@ class NativePerformance : public NativePerformanceCxxSpec { #pragma mark - DOM Performance (High Resolution Time) (https://www.w3.org/TR/hr-time-3/#dom-performance) // https://www.w3.org/TR/hr-time-3/#now-method - double now(jsi::Runtime& rt); + HighResTimeStamp now(jsi::Runtime& rt); #pragma mark - User Timing Level 3 functions (https://w3c.github.io/user-timing/) // https://w3c.github.io/user-timing/#mark-method - double markWithResult( + HighResTimeStamp markWithResult( jsi::Runtime& rt, std::string name, - std::optional startTime); + std::optional startTime); // https://w3c.github.io/user-timing/#measure-method - std::tuple measureWithResult( + std::tuple measureWithResult( jsi::Runtime& rt, std::string name, - double startTime, - double endTime, - std::optional duration, + HighResTimeStamp startTime, + HighResTimeStamp endTime, + std::optional duration, std::optional startMark, std::optional endMark); diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h index e794902f85d..887af0d1a07 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h @@ -8,6 +8,7 @@ #pragma once #include + #include #include #include @@ -28,8 +29,8 @@ enum class PerformanceEntryType { struct AbstractPerformanceEntry { std::string name; - DOMHighResTimeStamp startTime; - DOMHighResTimeStamp duration = 0; + HighResTimeStamp startTime; + HighResDuration duration = HighResDuration::zero(); }; struct PerformanceMark : AbstractPerformanceEntry { @@ -43,8 +44,8 @@ struct PerformanceMeasure : AbstractPerformanceEntry { struct PerformanceEventTiming : AbstractPerformanceEntry { static constexpr PerformanceEntryType entryType = PerformanceEntryType::EVENT; - DOMHighResTimeStamp processingStart; - DOMHighResTimeStamp processingEnd; + HighResTimeStamp processingStart; + HighResTimeStamp processingEnd; PerformanceEntryInteractionId interactionId; }; @@ -57,13 +58,13 @@ struct PerformanceResourceTiming : AbstractPerformanceEntry { static constexpr PerformanceEntryType entryType = PerformanceEntryType::RESOURCE; /** Aligns with `startTime`. */ - DOMHighResTimeStamp fetchStart; - DOMHighResTimeStamp requestStart; - std::optional connectStart; - std::optional connectEnd; - std::optional responseStart; + HighResTimeStamp fetchStart; + HighResTimeStamp requestStart; + std::optional connectStart; + std::optional connectEnd; + std::optional responseStart; /** Aligns with `duration`. */ - std::optional responseEnd; + std::optional responseEnd; std::optional responseStatus; }; diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryBuffer.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryBuffer.h index 96a137ace14..c3ce79d3412 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryBuffer.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryBuffer.h @@ -14,7 +14,7 @@ namespace facebook::react { // Default duration threshold for reporting performance entries (0 means "report // all") -constexpr double DEFAULT_DURATION_THRESHOLD = 0.0; +constexpr HighResDuration DEFAULT_DURATION_THRESHOLD = HighResDuration::zero(); /** * Abstract performance entry buffer with reporting flags. @@ -22,7 +22,7 @@ constexpr double DEFAULT_DURATION_THRESHOLD = 0.0; */ class PerformanceEntryBuffer { public: - double durationThreshold{DEFAULT_DURATION_THRESHOLD}; + HighResDuration durationThreshold = DEFAULT_DURATION_THRESHOLD; size_t droppedEntriesCount{0}; explicit PerformanceEntryBuffer() = default; diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp index 441409b8571..01a7591ccef 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp @@ -37,10 +37,6 @@ std::vector getSupportedEntryTypesInternal() { return supportedEntryTypes; } -double performanceNow() { - return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now()); -} - #if defined(__clang__) #define NO_DESTROY [[clang::no_destroy]] #else @@ -83,9 +79,9 @@ PerformanceEntryReporter::PerformanceEntryReporter() #endif } -DOMHighResTimeStamp PerformanceEntryReporter::getCurrentTimeStamp() const { +HighResTimeStamp PerformanceEntryReporter::getCurrentTimeStamp() const { return timeStampProvider_ != nullptr ? timeStampProvider_() - : performanceNow(); + : HighResTimeStamp::now(); } std::vector @@ -173,7 +169,7 @@ void PerformanceEntryReporter::clearEntries( PerformanceMark PerformanceEntryReporter::reportMark( const std::string& name, - const std::optional& startTime) { + const std::optional& startTime) { // Resolve timings auto startTimeVal = startTime ? *startTime : getCurrentTimeStamp(); const auto entry = PerformanceMark{{.name = name, .startTime = startTimeVal}}; @@ -193,11 +189,11 @@ PerformanceMark PerformanceEntryReporter::reportMark( PerformanceMeasure PerformanceEntryReporter::reportMeasure( const std::string& name, - DOMHighResTimeStamp startTime, - DOMHighResTimeStamp endTime, + HighResTimeStamp startTime, + HighResTimeStamp endTime, const std::optional& trackMetadata) { - DOMHighResTimeStamp duration = endTime - startTime; + HighResDuration duration = endTime - startTime; const auto entry = PerformanceMeasure{ {.name = std::string(name), @@ -217,7 +213,7 @@ PerformanceMeasure PerformanceEntryReporter::reportMeasure( return entry; } -std::optional PerformanceEntryReporter::getMarkTime( +std::optional PerformanceEntryReporter::getMarkTime( const std::string& markName) const { std::shared_lock lock(buffersMutex_); @@ -231,10 +227,10 @@ std::optional PerformanceEntryReporter::getMarkTime( void PerformanceEntryReporter::reportEvent( std::string name, - DOMHighResTimeStamp startTime, - DOMHighResTimeStamp duration, - DOMHighResTimeStamp processingStart, - DOMHighResTimeStamp processingEnd, + HighResTimeStamp startTime, + HighResDuration duration, + HighResTimeStamp processingStart, + HighResTimeStamp processingEnd, uint32_t interactionId) { eventCounts_[name]++; @@ -260,8 +256,8 @@ void PerformanceEntryReporter::reportEvent( } void PerformanceEntryReporter::reportLongTask( - DOMHighResTimeStamp startTime, - DOMHighResTimeStamp duration) { + HighResTimeStamp startTime, + HighResDuration duration) { const auto entry = PerformanceLongTaskTiming{ {.name = std::string{"self"}, .startTime = startTime, @@ -277,12 +273,12 @@ void PerformanceEntryReporter::reportLongTask( PerformanceResourceTiming PerformanceEntryReporter::reportResourceTiming( const std::string& url, - DOMHighResTimeStamp fetchStart, - DOMHighResTimeStamp requestStart, - std::optional connectStart, - std::optional connectEnd, - DOMHighResTimeStamp responseStart, - DOMHighResTimeStamp responseEnd, + HighResTimeStamp fetchStart, + HighResTimeStamp requestStart, + std::optional connectStart, + std::optional connectEnd, + HighResTimeStamp responseStart, + HighResTimeStamp responseEnd, const std::optional& responseStatus) { const auto entry = PerformanceResourceTiming{ {.name = url, .startTime = fetchStart}, @@ -313,9 +309,7 @@ void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const { auto [trackName, eventName] = parseTrackName(entry.name); if (performanceTracer.isTracing()) { - performanceTracer.reportMark( - entry.name, - HighResTimeStamp::fromDOMHighResTimeStamp(entry.startTime)); + performanceTracer.reportMark(entry.name, entry.startTime); } if (ReactPerfettoLogger::isTracing()) { @@ -339,10 +333,7 @@ void PerformanceEntryReporter::traceMeasure( trackMetadata = {.track = trackName.value()}; } performanceTracer.reportMeasure( - eventName, - HighResTimeStamp::fromDOMHighResTimeStamp(entry.startTime), - HighResDuration::fromDOMHighResTimeStamp(entry.duration), - trackMetadata); + eventName, entry.startTime, entry.duration, trackMetadata); } if (ReactPerfettoLogger::isTracing()) { diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index 549fe6f591a..2eb67ceca03 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -27,7 +27,8 @@ constexpr size_t EVENT_BUFFER_SIZE = 150; constexpr size_t LONG_TASK_BUFFER_SIZE = 200; constexpr size_t RESOURCE_TIMING_BUFFER_SIZE = 250; -constexpr DOMHighResTimeStamp LONG_TASK_DURATION_THRESHOLD_MS = 50.0; +constexpr HighResDuration LONG_TASK_DURATION_THRESHOLD = + HighResDuration::fromMilliseconds(50); class PerformanceEntryReporter { public: @@ -66,9 +67,9 @@ class PerformanceEntryReporter { PerformanceEntryType entryType, const std::string& entryName); - DOMHighResTimeStamp getCurrentTimeStamp() const; + HighResTimeStamp getCurrentTimeStamp() const; - void setTimeStampProvider(std::function provider) { + void setTimeStampProvider(std::function provider) { timeStampProvider_ = std::move(provider); } @@ -80,37 +81,38 @@ class PerformanceEntryReporter { return eventCounts_; } - std::optional getMarkTime(const std::string& markName) const; + std::optional getMarkTime( + const std::string& markName) const; PerformanceMark reportMark( const std::string& name, - const std::optional& startTime = std::nullopt); + const std::optional& startTime = std::nullopt); PerformanceMeasure reportMeasure( const std::string& name, - double startTime, - double endTime, + HighResTimeStamp startTime, + HighResTimeStamp endTime, const std::optional& trackMetadata = std::nullopt); void reportEvent( std::string name, - double startTime, - double duration, - double processingStart, - double processingEnd, + HighResTimeStamp startTime, + HighResDuration duration, + HighResTimeStamp processingStart, + HighResTimeStamp processingEnd, uint32_t interactionId); - void reportLongTask(double startTime, double duration); + void reportLongTask(HighResTimeStamp startTime, HighResDuration duration); PerformanceResourceTiming reportResourceTiming( const std::string& url, - DOMHighResTimeStamp fetchStart, - DOMHighResTimeStamp requestStart, - std::optional connectStart, - std::optional connectEnd, - DOMHighResTimeStamp responseStart, - DOMHighResTimeStamp responseEnd, + HighResTimeStamp fetchStart, + HighResTimeStamp requestStart, + std::optional connectStart, + std::optional connectEnd, + HighResTimeStamp responseStart, + HighResTimeStamp responseEnd, const std::optional& responseStatus); private: @@ -126,7 +128,7 @@ class PerformanceEntryReporter { std::unordered_map eventCounts_; - std::function timeStampProvider_ = nullptr; + std::function timeStampProvider_ = nullptr; const inline PerformanceEntryBuffer& getBuffer( PerformanceEntryType entryType) const { diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceObserver.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceObserver.h index a79d8d0a99f..8b387ec753c 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceObserver.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceObserver.h @@ -7,12 +7,15 @@ #pragma once +#include "PerformanceEntryBuffer.h" +#include "PerformanceObserverRegistry.h" + +#include + #include #include #include #include -#include "PerformanceEntryBuffer.h" -#include "PerformanceObserverRegistry.h" namespace facebook::react { @@ -27,7 +30,7 @@ using PerformanceObserverCallback = std::function; * https://w3c.github.io/performance-timeline/#performanceobserverinit-dictionary */ struct PerformanceObserverObserveMultipleOptions { - double durationThreshold = 0.0; + HighResDuration durationThreshold = DEFAULT_DURATION_THRESHOLD; }; /** @@ -38,7 +41,7 @@ struct PerformanceObserverObserveMultipleOptions { */ struct PerformanceObserverObserveSingleOptions { bool buffered = false; - double durationThreshold = 0.0; + HighResDuration durationThreshold = DEFAULT_DURATION_THRESHOLD; }; /** @@ -124,7 +127,7 @@ class PerformanceObserver PerformanceObserverEntryTypeFilter observedTypes_; /// https://www.w3.org/TR/event-timing/#sec-modifications-perf-timeline - double durationThreshold_{DEFAULT_DURATION_THRESHOLD}; + HighResDuration durationThreshold_ = DEFAULT_DURATION_THRESHOLD; std::vector buffer_; bool didScheduleFlushBuffer_ = false; bool requiresDroppedEntries_ = false; diff --git a/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceEntryReporterTest.cpp b/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceEntryReporterTest.cpp index 5217debd97d..330eb185f44 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceEntryReporterTest.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceEntryReporterTest.cpp @@ -58,8 +58,10 @@ namespace facebook::react { [&](const auto& entryDetails) -> std::ostream& { os << "{ .name = \"" << entryDetails.name << "\"" << ", .entryType = " << entryTypeNames[static_cast(entryDetails.entryType) - 1] - << ", .startTime = " << entryDetails.startTime - << ", .duration = " << entryDetails.duration << " }"; + << ", .startTime = " + << entryDetails.startTime.toDOMHighResTimeStamp() + << ", .duration = " << entryDetails.duration.toDOMHighResTimeStamp() + << " }"; return os; }, entry); @@ -77,60 +79,116 @@ std::vector toSorted( TEST(PerformanceEntryReporter, PerformanceEntryReporterTestReportMarks) { auto reporter = PerformanceEntryReporter::getInstance(); + auto timeOrigin = HighResTimeStamp::now(); reporter->clearEntries(); - reporter->reportMark("mark0", 0); - reporter->reportMark("mark1", 1); - reporter->reportMark("mark2", 2); + reporter->reportMark("mark0", timeOrigin); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromMilliseconds(1)); + reporter->reportMark( + "mark2", timeOrigin + HighResDuration::fromMilliseconds(2)); // Report mark0 again - reporter->reportMark("mark0", 3); + reporter->reportMark( + "mark0", timeOrigin + HighResDuration::fromMilliseconds(3)); const auto entries = toSorted(reporter->getEntries()); ASSERT_EQ(4, entries.size()); const std::vector expected = { - PerformanceMark{{.name = "mark0", .startTime = 0, .duration = 0}}, - PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}}, - PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}, - PerformanceMark{{.name = "mark0", .startTime = 3, .duration = 0}}}; + PerformanceMark{ + {.name = "mark0", + .startTime = timeOrigin, + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark0", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(3), + .duration = HighResDuration::zero()}}}; ASSERT_EQ(expected, entries); } TEST(PerformanceEntryReporter, PerformanceEntryReporterTestReportMeasures) { auto reporter = PerformanceEntryReporter::getInstance(); + auto timeOrigin = HighResTimeStamp::now(); reporter->clearEntries(); - reporter->reportMark("mark0", 0); - reporter->reportMark("mark1", 1); - reporter->reportMark("mark2", 2); + reporter->reportMark("mark0", timeOrigin); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromMilliseconds(1)); + reporter->reportMark( + "mark2", timeOrigin + HighResDuration::fromMilliseconds(2)); - reporter->reportMeasure("measure0", 0, 2); - reporter->reportMeasure("measure1", 0, 3); + reporter->reportMeasure( + "measure0", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(2)); + reporter->reportMeasure( + "measure1", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(3)); - reporter->reportMark("mark3", 2.5); - reporter->reportMeasure("measure2", 2.0, 2.0); - reporter->reportMark("mark4", 3.0); + reporter->reportMark( + "mark3", timeOrigin + HighResDuration::fromNanoseconds(2.5 * 1e6)); + reporter->reportMeasure( + "measure2", + timeOrigin + HighResDuration::fromMilliseconds(2), + timeOrigin + HighResDuration::fromMilliseconds(2)); + reporter->reportMark( + "mark4", timeOrigin + HighResDuration::fromMilliseconds(3)); const auto entries = toSorted(reporter->getEntries()); const std::vector expected = { - PerformanceMark{{.name = "mark0", .startTime = 0, .duration = 0}}, - PerformanceMeasure{{.name = "measure0", .startTime = 0, .duration = 2}}, - PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}}, - PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}}, - PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}, - PerformanceMeasure{{.name = "measure2", .startTime = 2, .duration = 0}}, - PerformanceMark{{.name = "mark3", .startTime = 2.5, .duration = 0}}, - PerformanceMark{{.name = "mark4", .startTime = 3, .duration = 0}}}; + PerformanceMark{ + {.name = "mark0", + .startTime = timeOrigin, + .duration = HighResDuration::zero()}}, + PerformanceMeasure{ + {.name = "measure0", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(2)}}, + PerformanceMeasure{ + {.name = "measure1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(3)}}, + PerformanceMark{ + {.name = "mark1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}, + PerformanceMeasure{ + {.name = "measure2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark3", + .startTime = + timeOrigin + HighResDuration::fromNanoseconds(2.5 * 1e6), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark4", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(3), + .duration = HighResDuration::zero()}}}; ASSERT_EQ(expected, entries); } TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { auto reporter = PerformanceEntryReporter::getInstance(); + auto timeOrigin = HighResTimeStamp::now(); reporter->clearEntries(); { @@ -138,27 +196,61 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { ASSERT_EQ(0, entries.size()); } - reporter->reportMark("common_name", 0); - reporter->reportMark("mark1", 1); - reporter->reportMark("mark2", 2); + reporter->reportMark("common_name", timeOrigin); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromMilliseconds(1)); + reporter->reportMark( + "mark2", timeOrigin + HighResDuration::fromMilliseconds(2)); - reporter->reportMeasure("common_name", 0, 2); - reporter->reportMeasure("measure1", 0, 3); - reporter->reportMeasure("measure2", 1, 6); - reporter->reportMeasure("measure3", 1.5, 2); + reporter->reportMeasure( + "common_name", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(2)); + reporter->reportMeasure( + "measure1", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(3)); + reporter->reportMeasure( + "measure2", + timeOrigin + HighResDuration::fromMilliseconds(1), + timeOrigin + HighResDuration::fromMilliseconds(6)); + reporter->reportMeasure( + "measure3", + timeOrigin + HighResDuration::fromNanoseconds(1.5 * 1e6), + timeOrigin + HighResDuration::fromMilliseconds(2)); { const auto allEntries = toSorted(reporter->getEntries()); const std::vector expected = { - PerformanceMark{{.name = "common_name", .startTime = 0, .duration = 0}}, + PerformanceMark{ + {.name = "common_name", + .startTime = timeOrigin, + .duration = HighResDuration::zero()}}, PerformanceMeasure{ - {.name = "common_name", .startTime = 0, .duration = 2}}, - PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}}, - PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}}, - PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 5}}, + {.name = "common_name", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(2)}}, PerformanceMeasure{ - {.name = "measure3", .startTime = 1.5, .duration = 0.5}}, - PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}}; + {.name = "measure1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(3)}}, + PerformanceMark{ + {.name = "mark1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::zero()}}, + PerformanceMeasure{ + {.name = "measure2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::fromMilliseconds(5)}}, + PerformanceMeasure{ + {.name = "measure3", + .startTime = + timeOrigin + HighResDuration::fromNanoseconds(1.5 * 1e6), + .duration = HighResDuration::fromNanoseconds(0.5 * 1e6)}}, + PerformanceMark{ + {.name = "mark2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}}; ASSERT_EQ(expected, allEntries); } @@ -166,9 +258,18 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { const auto marks = toSorted(reporter->getEntries(PerformanceEntryType::MARK)); const std::vector expected = { - PerformanceMark{{.name = "common_name", .startTime = 0, .duration = 0}}, - PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}}, - PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}}; + PerformanceMark{ + {.name = "common_name", + .startTime = timeOrigin, + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}}; ASSERT_EQ(expected, marks); } @@ -177,17 +278,28 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { toSorted(reporter->getEntries(PerformanceEntryType::MEASURE)); const std::vector expected = { PerformanceMeasure{ - {.name = "common_name", .startTime = 0, .duration = 2}}, - PerformanceMeasure{{.name = "measure1", .startTime = 0, .duration = 3}}, - PerformanceMeasure{{.name = "measure2", .startTime = 1, .duration = 5}}, + {.name = "common_name", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(2)}}, PerformanceMeasure{ - {.name = "measure3", .startTime = 1.5, .duration = 0.5}}}; + {.name = "measure1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(3)}}, + PerformanceMeasure{ + {.name = "measure2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::fromMilliseconds(5)}}, + PerformanceMeasure{ + {.name = "measure3", + .startTime = + timeOrigin + HighResDuration::fromNanoseconds(1.5 * 1e6), + .duration = HighResDuration::fromNanoseconds(0.5 * 1e6)}}}; ASSERT_EQ(expected, measures); } { const std::vector expected = { - PerformanceMark{{.name = "common_name", .startTime = 0}}}; + PerformanceMark{{.name = "common_name", .startTime = timeOrigin}}}; const auto commonName = reporter->getEntries(PerformanceEntryType::MARK, "common_name"); ASSERT_EQ(expected, commonName); @@ -195,7 +307,9 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { { const std::vector expected = {PerformanceMeasure{ - {.name = "common_name", .startTime = 0, .duration = 2}}}; + {.name = "common_name", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(2)}}}; const auto commonName = reporter->getEntries(PerformanceEntryType::MEASURE, "common_name"); ASSERT_EQ(expected, commonName); @@ -204,26 +318,52 @@ TEST(PerformanceEntryReporter, PerformanceEntryReporterTestGetEntries) { TEST(PerformanceEntryReporter, PerformanceEntryReporterTestClearMarks) { auto reporter = PerformanceEntryReporter::getInstance(); + auto timeOrigin = HighResTimeStamp::now(); reporter->clearEntries(); - reporter->reportMark("common_name", 0); - reporter->reportMark("mark1", 1); - reporter->reportMark("mark1", 2.1); - reporter->reportMark("mark2", 2); + reporter->reportMark("common_name", timeOrigin); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromMilliseconds(1)); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromNanoseconds(2.1 * 1e6)); + reporter->reportMark( + "mark2", timeOrigin + HighResDuration::fromMilliseconds(2)); - reporter->reportMeasure("common_name", 0, 2); - reporter->reportMeasure("measure1", 0, 3); - reporter->reportMeasure("measure2", 1, 6); - reporter->reportMeasure("measure3", 1.5, 2); + reporter->reportMeasure( + "common_name", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(2)); + reporter->reportMeasure( + "measure1", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(3)); + reporter->reportMeasure( + "measure2", + timeOrigin + HighResDuration::fromMilliseconds(1), + timeOrigin + HighResDuration::fromMilliseconds(6)); + reporter->reportMeasure( + "measure3", + timeOrigin + HighResDuration::fromNanoseconds(1.5 * 1e6), + timeOrigin + HighResDuration::fromMilliseconds(2)); reporter->clearEntries(PerformanceEntryType::MARK, "common_name"); { auto entries = toSorted(reporter->getEntries(PerformanceEntryType::MARK)); std::vector expected = { - PerformanceMark{{.name = "mark1", .startTime = 1, .duration = 0}}, - PerformanceMark{{.name = "mark2", .startTime = 2, .duration = 0}}, - PerformanceMark{{.name = "mark1", .startTime = 2.1, .duration = 0}}, + PerformanceMark{ + {.name = "mark1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(1), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(2), + .duration = HighResDuration::zero()}}, + PerformanceMark{ + {.name = "mark1", + .startTime = + timeOrigin + HighResDuration::fromNanoseconds(2.1 * 1e6), + .duration = HighResDuration::zero()}}, }; ASSERT_EQ(expected, entries); } diff --git a/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceObserverTest.cpp b/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceObserverTest.cpp index 06cf2c3e09b..0a954958288 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceObserverTest.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/tests/PerformanceObserverTest.cpp @@ -49,12 +49,14 @@ TEST(PerformanceObserver, PerformanceObserverTestObserveFlushes) { bool callbackCalled = false; auto observer = PerformanceObserver::create( reporter->getObserverRegistry(), [&]() { callbackCalled = true; }); + auto timeOrigin = HighResTimeStamp::now(); observer->observe(PerformanceEntryType::MARK); // buffer is empty ASSERT_FALSE(callbackCalled); - reporter->reportMark("test", 10); + reporter->reportMark( + "test", timeOrigin + HighResDuration::fromMilliseconds(10)); ASSERT_TRUE(callbackCalled); observer->disconnect(); @@ -64,10 +66,12 @@ TEST(PerformanceObserver, PerformanceObserverTestFilteredSingle) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto observer = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); observer->observe(PerformanceEntryType::MEASURE); - reporter->reportMark("test", 10); + reporter->reportMark( + "test", timeOrigin + HighResDuration::fromMilliseconds(10)); // wrong type ASSERT_EQ(observer->takeRecords().size(), 0); @@ -79,15 +83,34 @@ TEST(PerformanceObserver, PerformanceObserverTestFilterMulti) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto callbackCalled = false; auto observer = PerformanceObserver::create( reporter->getObserverRegistry(), [&]() { callbackCalled = true; }); observer->observe( {PerformanceEntryType::MEASURE, PerformanceEntryType::MARK}); - reporter->reportEvent("test1", 10, 10, 0, 0, 0); - reporter->reportEvent("test2", 10, 10, 0, 0, 0); - reporter->reportEvent("test3", 10, 10, 0, 0, 0); + reporter->reportEvent( + "test1", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test2", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test3", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); ASSERT_EQ(observer->takeRecords().size(), 0); ASSERT_FALSE(callbackCalled); @@ -101,11 +124,14 @@ TEST( auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto callbackCalled = false; auto observer = PerformanceObserver::create( reporter->getObserverRegistry(), [&]() { callbackCalled = true; }); observer->observe(PerformanceEntryType::MEASURE); - reporter->reportMark("test", 10); + + reporter->reportMark( + "test", timeOrigin + HighResDuration::fromMilliseconds(10)); ASSERT_FALSE(callbackCalled); @@ -116,14 +142,34 @@ TEST(PerformanceObserver, PerformanceObserverTestFilterMultiCallbackNotCalled) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto callbackCalled = false; auto observer = PerformanceObserver::create( reporter->getObserverRegistry(), [&]() { callbackCalled = true; }); observer->observe( {PerformanceEntryType::MEASURE, PerformanceEntryType::MARK}); - reporter->reportEvent("test1", 10, 10, 0, 0, 0); - reporter->reportEvent("test2", 10, 10, 0, 0, 0); - reporter->reportEvent("off3", 10, 10, 0, 0, 0); + + reporter->reportEvent( + "test1", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test2", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "off3", + timeOrigin + HighResDuration::fromMilliseconds(10), + HighResDuration::fromMilliseconds(10), + timeOrigin, + timeOrigin, + 0); ASSERT_FALSE(callbackCalled); @@ -134,18 +180,32 @@ TEST(PerformanceObserver, PerformanceObserverTestObserveTakeRecords) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto observer = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); observer->observe(PerformanceEntryType::MARK); - reporter->reportMark("test1", 10); - reporter->reportMeasure("off", 10, 20); - reporter->reportMark("test2", 20); - reporter->reportMark("test3", 30); + + reporter->reportMark( + "test1", timeOrigin + HighResDuration::fromMilliseconds(10)); + reporter->reportMeasure( + "off", + timeOrigin + HighResDuration::fromMilliseconds(10), + timeOrigin + HighResDuration::fromMilliseconds(20)); + reporter->reportMark( + "test2", timeOrigin + HighResDuration::fromMilliseconds(20)); + reporter->reportMark( + "test3", timeOrigin + HighResDuration::fromMilliseconds(30)); const std::vector expected = { - PerformanceMark{{.name = "test1", .startTime = 10}}, - PerformanceMark{{.name = "test2", .startTime = 20}}, - PerformanceMark{{.name = "test3", .startTime = 30}}, + PerformanceMark{ + {.name = "test1", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(10)}}, + PerformanceMark{ + {.name = "test2", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(20)}}, + PerformanceMark{ + {.name = "test3", + .startTime = timeOrigin + HighResDuration::fromMilliseconds(30)}}, }; ASSERT_EQ(expected, observer->takeRecords()); @@ -157,19 +217,66 @@ TEST(PerformanceObserver, PerformanceObserverTestObserveDurationThreshold) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto observer = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); - observer->observe(PerformanceEntryType::EVENT, {.durationThreshold = 50}); - reporter->reportEvent("test1", 0, 50, 0, 0, 0); - reporter->reportEvent("test2", 0, 100, 0, 0, 0); - reporter->reportEvent("off1", 0, 40, 0, 0, 0); - reporter->reportMark("off2", 100); - reporter->reportEvent("test3", 0, 60, 0, 0, 0); + observer->observe( + PerformanceEntryType::EVENT, + {.durationThreshold = HighResDuration::fromMilliseconds(50)}); + + reporter->reportEvent( + "test1", + timeOrigin, + HighResDuration::fromMilliseconds(50), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test2", + timeOrigin, + HighResDuration::fromMilliseconds(100), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "off1", + timeOrigin, + HighResDuration::fromMilliseconds(40), + timeOrigin, + timeOrigin, + 0); + reporter->reportMark( + "off2", timeOrigin + HighResDuration::fromMilliseconds(100)); + reporter->reportEvent( + "test3", + timeOrigin, + HighResDuration::fromMilliseconds(60), + timeOrigin, + timeOrigin, + 0); const std::vector expected = { - PerformanceEventTiming{{.name = "test1", .duration = 50}, 0, 0, 0}, - PerformanceEventTiming{{.name = "test2", .duration = 100}, 0, 0, 0}, - PerformanceEventTiming{{.name = "test3", .duration = 60}, 0, 0, 0}, + PerformanceEventTiming{ + {.name = "test1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(50)}, + timeOrigin, + timeOrigin, + 0}, + PerformanceEventTiming{ + {.name = "test2", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(100)}, + timeOrigin, + timeOrigin, + 0}, + PerformanceEventTiming{ + {.name = "test3", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(60)}, + timeOrigin, + timeOrigin, + 0}, }; ASSERT_EQ(expected, observer->takeRecords()); @@ -181,23 +288,65 @@ TEST(PerformanceObserver, PerformanceObserverTestObserveBuffered) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); - reporter->reportEvent("test1", 0, 50, 0, 0, 0); - reporter->reportEvent("test2", 0, 100, 0, 0, 0); - reporter->reportEvent("test3", 0, 40, 0, 0, 0); - reporter->reportEvent("test4", 0, 100, 0, 0, 0); + auto timeOrigin = HighResTimeStamp::now(); + reporter->reportEvent( + "test1", + timeOrigin, + HighResDuration::fromMilliseconds(50), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test2", + timeOrigin, + HighResDuration::fromMilliseconds(100), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test3", + timeOrigin, + HighResDuration::fromMilliseconds(40), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "test4", + timeOrigin, + HighResDuration::fromMilliseconds(100), + timeOrigin, + timeOrigin, + 0); auto observer = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); observer->observe( - PerformanceEntryType::EVENT, {.buffered = true, .durationThreshold = 50}); + PerformanceEntryType::EVENT, + {.buffered = true, + .durationThreshold = HighResDuration::fromMilliseconds(50)}); const std::vector expected = { PerformanceEventTiming{ - {.name = "test1", .startTime = 0, .duration = 50}, 0, 0, 0}, + {.name = "test1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(50)}, + timeOrigin, + timeOrigin, + 0}, PerformanceEventTiming{ - {.name = "test2", .startTime = 0, .duration = 100}, 0, 0, 0}, + {.name = "test2", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(100)}, + timeOrigin, + timeOrigin, + 0}, PerformanceEventTiming{ - {.name = "test4", .startTime = 0, .duration = 100}, 0, 0, 0}, + {.name = "test4", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(100)}, + timeOrigin, + timeOrigin, + 0}, }; ASSERT_EQ(expected, observer->takeRecords()); @@ -209,26 +358,71 @@ TEST(PerformanceObserver, PerformanceObserverTestMultiple) { auto reporter = PerformanceEntryReporter::getInstance(); reporter->clearEntries(); + auto timeOrigin = HighResTimeStamp::now(); auto observer1 = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); auto observer2 = PerformanceObserver::create(reporter->getObserverRegistry(), [&]() {}); - observer1->observe(PerformanceEntryType::EVENT, {.durationThreshold = 50}); - observer2->observe(PerformanceEntryType::EVENT, {.durationThreshold = 80}); + observer1->observe( + PerformanceEntryType::EVENT, + {.durationThreshold = HighResDuration::fromMilliseconds(50)}); + observer2->observe( + PerformanceEntryType::EVENT, + {.durationThreshold = HighResDuration::fromMilliseconds(80)}); - reporter->reportMeasure("measure", 0, 50); - reporter->reportEvent("event1", 0, 100, 0, 0, 0); - reporter->reportEvent("event2", 0, 40, 0, 0, 0); - reporter->reportMark("mark1", 100); - reporter->reportEvent("event3", 0, 60, 0, 0, 0); + reporter->reportMeasure( + "measure", + timeOrigin, + timeOrigin + HighResDuration::fromMilliseconds(50)); + reporter->reportEvent( + "event1", + timeOrigin, + HighResDuration::fromMilliseconds(100), + timeOrigin, + timeOrigin, + 0); + reporter->reportEvent( + "event2", + timeOrigin, + HighResDuration::fromMilliseconds(40), + timeOrigin, + timeOrigin, + 0); + reporter->reportMark( + "mark1", timeOrigin + HighResDuration::fromMilliseconds(100)); + reporter->reportEvent( + "event3", + timeOrigin, + HighResDuration::fromMilliseconds(60), + timeOrigin, + timeOrigin, + 0); const std::vector expected1 = { - PerformanceEventTiming{{.name = "event1", .duration = 100}, 0, 0, 0}, - PerformanceEventTiming{{.name = "event3", .duration = 60}, 0, 0, 0}, + PerformanceEventTiming{ + {.name = "event1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(100)}, + timeOrigin, + timeOrigin, + 0}, + PerformanceEventTiming{ + {.name = "event3", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(60)}, + timeOrigin, + timeOrigin, + 0}, }; const std::vector expected2 = { - PerformanceEventTiming{{.name = "event1", .duration = 100}, 0, 0, 0}, + PerformanceEventTiming{ + {.name = "event1", + .startTime = timeOrigin, + .duration = HighResDuration::fromMilliseconds(100)}, + timeOrigin, + timeOrigin, + 0}, }; ASSERT_EQ(expected1, observer1->takeRecords()); diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h b/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h index 4e5d211e463..1d7526ec471 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventLogger.h @@ -34,8 +34,7 @@ class EventLogger { virtual EventTag onEventStart( std::string_view name, SharedEventTarget target, - std::optional eventStartTimeStamp = - std::nullopt) = 0; + std::optional eventStartTimeStamp = std::nullopt) = 0; /* * Called when event starts getting dispatched (processed by the handlers, if diff --git a/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h b/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h index 817512e5c75..d78cbd11860 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h +++ b/packages/react-native/ReactCommon/react/renderer/core/RawEvent.h @@ -87,7 +87,7 @@ struct RawEvent { // The client may specify a platform-specific timestamp for the event start // time, for example when MotionEvent was triggered on the Android native // side. - std::optional eventStartTimeStamp = std::nullopt; + std::optional eventStartTimeStamp = std::nullopt; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp b/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp index 467689efddb..b6ebd6f4905 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/tests/EventQueueProcessorTest.cpp @@ -25,7 +25,7 @@ class MockEventLogger : public EventLogger { EventTag onEventStart( std::string_view /*name*/, SharedEventTarget /*target*/, - std::optional /*eventStartTimeStamp*/) override { + std::optional /*eventStartTimeStamp*/) override { return EMPTY_EVENT_TAG; } void onEventProcessingStart(EventTag /*tag*/) override {} diff --git a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp index 6dc9680b232..f3665ddddaf 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.cpp @@ -105,7 +105,7 @@ EventPerformanceLogger::EventPerformanceLogger( EventTag EventPerformanceLogger::onEventStart( std::string_view name, SharedEventTarget target, - std::optional eventStartTimeStamp) { + std::optional eventStartTimeStamp) { auto performanceEntryReporter = performanceEntryReporter_.lock(); if (performanceEntryReporter == nullptr) { return EMPTY_EVENT_TAG; @@ -123,7 +123,7 @@ EventTag EventPerformanceLogger::onEventStart( // The event start timestamp may be provided by the caller in order to // specify the platform specific event start time. - DOMHighResTimeStamp timeStamp = eventStartTimeStamp + HighResTimeStamp timeStamp = eventStartTimeStamp ? *eventStartTimeStamp : performanceEntryReporter->getCurrentTimeStamp(); { @@ -213,7 +213,7 @@ void EventPerformanceLogger::dispatchPendingEventTimingEntries( void EventPerformanceLogger::shadowTreeDidMount( const RootShadowNode::Shared& rootShadowNode, - double mountTime) noexcept { + HighResTimeStamp mountTime) noexcept { auto performanceEntryReporter = performanceEntryReporter_.lock(); if (performanceEntryReporter == nullptr) { return; diff --git a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h index ffa1d01fb79..2addb87f1d6 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/events/EventPerformanceLogger.h @@ -32,7 +32,7 @@ class EventPerformanceLogger : public EventLogger, EventTag onEventStart( std::string_view name, SharedEventTarget target, - std::optional eventStartTimeStamp = + std::optional eventStartTimeStamp = std::nullopt) override; void onEventProcessingStart(EventTag tag) override; void onEventProcessingEnd(EventTag tag) override; @@ -47,15 +47,15 @@ class EventPerformanceLogger : public EventLogger, void shadowTreeDidMount( const RootShadowNode::Shared& rootShadowNode, - double mountTime) noexcept override; + HighResTimeStamp mountTime) noexcept override; private: struct EventEntry { std::string_view name; SharedEventTarget target{nullptr}; - DOMHighResTimeStamp startTime; - std::optional processingStartTime; - std::optional processingEndTime; + HighResTimeStamp startTime; + std::optional processingStartTime; + std::optional processingEndTime; bool isWaitingForMount{false}; diff --git a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp index bc8d0c875a5..f7cc94f2a32 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp @@ -104,7 +104,7 @@ static Float getHighestThresholdCrossed( std::optional IntersectionObserver::updateIntersectionObservation( const RootShadowNode& rootShadowNode, - double time) { + HighResTimeStamp time) { const auto layoutableRootShadowNode = dynamic_cast(&rootShadowNode); @@ -169,7 +169,7 @@ IntersectionObserver::updateIntersectionObservation( std::optional IntersectionObserver::updateIntersectionObservationForSurfaceUnmount( - double time) { + HighResTimeStamp time) { return setNotIntersectingState(Rect{}, Rect{}, Rect{}, time); } @@ -180,7 +180,7 @@ IntersectionObserver::setIntersectingState( const Rect& intersectionRect, Float threshold, Float rootThreshold, - double time) { + HighResTimeStamp time) { auto newState = IntersectionObserverState::Intersecting(threshold, rootThreshold); @@ -206,7 +206,7 @@ IntersectionObserver::setNotIntersectingState( const Rect& rootBoundingRect, const Rect& targetBoundingRect, const Rect& intersectionRect, - double time) { + HighResTimeStamp time) { if (state_ != IntersectionObserverState::NotIntersecting()) { state_ = IntersectionObserverState::NotIntersecting(); IntersectionObserverEntry entry{ diff --git a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.h b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.h index d5dd24174af..9248c84132e 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.h @@ -25,9 +25,7 @@ struct IntersectionObserverEntry { Rect rootRect; Rect intersectionRect; bool isIntersectingAboveThresholds; - // TODO(T156529385) Define `DOMHighResTimeStamp` as an alias for `double` and - // use it here. - double time; + HighResTimeStamp time; bool sameShadowNodeFamily( const ShadowNodeFamily& otherShadowNodeFamily) const { @@ -48,10 +46,10 @@ class IntersectionObserver { // https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo std::optional updateIntersectionObservation( const RootShadowNode& rootShadowNode, - double time); + HighResTimeStamp time); std::optional - updateIntersectionObservationForSurfaceUnmount(double time); + updateIntersectionObservationForSurfaceUnmount(HighResTimeStamp time); IntersectionObserverObserverId getIntersectionObserverId() const { return intersectionObserverId_; @@ -72,13 +70,13 @@ class IntersectionObserver { const Rect& intersectionRect, Float threshold, Float rootThreshold, - double time); + HighResTimeStamp time); std::optional setNotIntersectingState( const Rect& rootBoundingRect, const Rect& targetBoundingRect, const Rect& intersectionRect, - double time); + HighResTimeStamp time); IntersectionObserverObserverId intersectionObserverId_; ShadowNodeFamily::Shared targetShadowNodeFamily_; diff --git a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.cpp b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.cpp index 4725628b898..57ada48e712 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.cpp @@ -280,14 +280,14 @@ void IntersectionObserverManager::updateIntersectionObservations( void IntersectionObserverManager::shadowTreeDidMount( const RootShadowNode::Shared& rootShadowNode, - double time) noexcept { + HighResTimeStamp time) noexcept { updateIntersectionObservations( rootShadowNode->getSurfaceId(), rootShadowNode.get(), time); } void IntersectionObserverManager::shadowTreeDidUnmount( SurfaceId surfaceId, - double time) noexcept { + HighResTimeStamp time) noexcept { updateIntersectionObservations(surfaceId, nullptr, time); } @@ -296,7 +296,7 @@ void IntersectionObserverManager::shadowTreeDidUnmount( void IntersectionObserverManager::updateIntersectionObservations( SurfaceId surfaceId, const RootShadowNode* rootShadowNode, - double time) { + HighResTimeStamp time) { TraceSection s("IntersectionObserverManager::updateIntersectionObservations"); std::vector entries; diff --git a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.h b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.h index 9331bcfef67..64317bd2d6e 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserverManager.h @@ -55,9 +55,10 @@ class IntersectionObserverManager final void shadowTreeDidMount( const RootShadowNode::Shared& rootShadowNode, - double time) noexcept override; + HighResTimeStamp time) noexcept override; - void shadowTreeDidUnmount(SurfaceId surfaceId, double time) noexcept override; + void shadowTreeDidUnmount(SurfaceId surfaceId, HighResTimeStamp time) noexcept + override; private: mutable std::unordered_map< @@ -92,7 +93,7 @@ class IntersectionObserverManager final void updateIntersectionObservations( SurfaceId surfaceId, const RootShadowNode* rootShadowNode, - double time); + HighResTimeStamp time); const IntersectionObserver& getRegisteredIntersectionObserver( SurfaceId surfaceId, diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp index 10fef3e9831..fcdb9565edb 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -460,12 +460,10 @@ void RuntimeScheduler_Modern::reportLongTasks( return; } - auto checkedDurationMs = - longestPeriodWithoutYieldingOpportunity_.toDOMHighResTimeStamp(); - if (checkedDurationMs >= LONG_TASK_DURATION_THRESHOLD_MS) { - auto durationMs = (endTime - startTime).toDOMHighResTimeStamp(); - auto startTimeMs = startTime.toDOMHighResTimeStamp(); - reporter->reportLongTask(startTimeMs, durationMs); + if (longestPeriodWithoutYieldingOpportunity_ >= + LONG_TASK_DURATION_THRESHOLD) { + auto duration = endTime - startTime; + reporter->reportLongTask(startTime, duration); } } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp index 0fdff1b072f..6251b5a552b 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1257,8 +1258,9 @@ TEST_P(RuntimeSchedulerTest, reportsLongTasks) { [startTime](const auto& entryDetails) { EXPECT_EQ(entryDetails.entryType, PerformanceEntryType::LONGTASK); EXPECT_EQ( - entryDetails.startTime, startTime.toDOMHighResTimeStamp() + 100); - EXPECT_EQ(entryDetails.duration, 50); + entryDetails.startTime.toDOMHighResTimeStamp(), + startTime.toDOMHighResTimeStamp() + 100); + EXPECT_EQ(entryDetails.duration, HighResDuration::fromMilliseconds(50)); }, entry); } @@ -1344,8 +1346,10 @@ TEST_P(RuntimeSchedulerTest, reportsLongTasksWithYielding) { [startTime](const auto& entryDetails) { EXPECT_EQ(entryDetails.entryType, PerformanceEntryType::LONGTASK); EXPECT_EQ( - entryDetails.startTime, startTime.toDOMHighResTimeStamp() + 100); - EXPECT_EQ(entryDetails.duration, 120); + entryDetails.startTime.toDOMHighResTimeStamp(), + startTime.toDOMHighResTimeStamp() + 100); + EXPECT_EQ( + entryDetails.duration, HighResDuration::fromMilliseconds(120)); }, entry); } diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerMountHook.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerMountHook.h index 3e5627b53eb..1a014411d85 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerMountHook.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerMountHook.h @@ -26,11 +26,11 @@ class UIManagerMountHook { */ virtual void shadowTreeDidMount( const RootShadowNode::Shared& rootShadowNode, - double mountTime) noexcept = 0; + HighResTimeStamp mountTime) noexcept = 0; virtual void shadowTreeDidUnmount( SurfaceId /*surfaceId*/, - double /*unmountTime*/) noexcept { + HighResTimeStamp /*unmountTime*/) noexcept { // Default no-op implementation for backwards compatibility. } diff --git a/packages/react-native/ReactCommon/react/timing/primitives.h b/packages/react-native/ReactCommon/react/timing/primitives.h index 91e5c33ed57..9c3be8990b8 100644 --- a/packages/react-native/ReactCommon/react/timing/primitives.h +++ b/packages/react-native/ReactCommon/react/timing/primitives.h @@ -11,23 +11,6 @@ namespace facebook::react { -// `DOMHighResTimeStamp` represents a time value in milliseconds (time point or -// duration), with sub-millisecond precision. -// On the Web, the precision can be reduced for security purposes, but that is -// not necessary in React Native. -using DOMHighResTimeStamp = double; - -inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp( - std::chrono::steady_clock::duration duration) { - return static_cast>(duration) - .count(); -} - -inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp( - std::chrono::steady_clock::time_point timePoint) { - return chronoToDOMHighResTimeStamp(timePoint.time_since_epoch()); -} - class HighResDuration; class HighResTimeStamp; diff --git a/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp b/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp index be1751c2bff..f94a379c036 100644 --- a/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp +++ b/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp @@ -11,39 +11,6 @@ namespace facebook::react { -using Clock = std::chrono::steady_clock; -using TimePoint = std::chrono::time_point; - -TEST(chronoToDOMHighResTimeStamp, withDurations) { - EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::nanoseconds(10)), 0.00001); - EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::microseconds(10)), 0.01); - EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::milliseconds(10)), 10.0); - EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::seconds(10)), 10000.0); - EXPECT_EQ( - chronoToDOMHighResTimeStamp( - std::chrono::seconds(1) + std::chrono::nanoseconds(20)), - 1000.000020); -} - -TEST(chronoToDOMHighResTimeStamp, withTimePoints) { - EXPECT_EQ( - chronoToDOMHighResTimeStamp(TimePoint(std::chrono::nanoseconds(10))), - 0.00001); - EXPECT_EQ( - chronoToDOMHighResTimeStamp(TimePoint(std::chrono::microseconds(10))), - 0.01); - EXPECT_EQ( - chronoToDOMHighResTimeStamp(TimePoint(std::chrono::milliseconds(10))), - 10.0); - EXPECT_EQ( - chronoToDOMHighResTimeStamp(TimePoint(std::chrono::seconds(10))), - 10000.0); - EXPECT_EQ( - chronoToDOMHighResTimeStamp( - TimePoint(std::chrono::seconds(1) + std::chrono::nanoseconds(20))), - 1000.000020); -} - TEST(HighResDuration, CorrectlyConvertsToDOMHighResTimeStamp) { EXPECT_EQ( HighResDuration::fromNanoseconds(10).toDOMHighResTimeStamp(), 0.00001); diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/HermesPerfettoDataSource.cpp b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/HermesPerfettoDataSource.cpp index c51d6a7a778..1ae283d7f50 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/HermesPerfettoDataSource.cpp +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/HermesPerfettoDataSource.cpp @@ -113,7 +113,7 @@ void HermesPerfettoDataSource::OnStart(const StartArgs&) { "react-native", perfetto::DynamicString{"Profiling Started"}, getPerfettoWebPerfTrackSync("JS Sampling"), - performanceNowToPerfettoTraceTime(0)); + perfetto::TrackEvent::GetTraceTimeNs()); } void HermesPerfettoDataSource::OnFlush(const FlushArgs&) { diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.cpp b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.cpp index a0786d4c649..c280ad4ca08 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.cpp +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.cpp @@ -80,13 +80,17 @@ perfetto::Track getPerfettoWebPerfTrackAsync(const std::string& trackName) { } // Perfetto's monotonic clock seems to match the std::chrono::steady_clock we -// use in JSExecutor::performanceNow on Android platforms, but if that +// use in HighResTimeStamp on Android platforms, but if that // assumption is incorrect we may need to manually offset perfetto timestamps. -uint64_t performanceNowToPerfettoTraceTime(double perfNowTime) { - if (perfNowTime == 0) { - return perfetto::TrackEvent::GetTraceTimeNs(); - } - return static_cast(perfNowTime * 1.e6); +uint64_t highResTimeStampToPerfettoTraceTime(HighResTimeStamp timestamp) { + auto chronoDurationSinceSteadyClockEpoch = + timestamp.toChronoSteadyClockTimePoint().time_since_epoch(); + auto nanoseconds = std::chrono::duration_cast( + chronoDurationSinceSteadyClockEpoch); + + return std::chrono::duration_cast>( + nanoseconds) + .count(); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.h b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.h index c01ac604c89..c46ff051c53 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.h +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfetto.h @@ -10,6 +10,7 @@ #ifdef WITH_PERFETTO #include +#include #include #include @@ -20,7 +21,7 @@ void initializePerfetto(); perfetto::Track getPerfettoWebPerfTrackSync(const std::string& trackName); perfetto::Track getPerfettoWebPerfTrackAsync(const std::string& trackName); -uint64_t performanceNowToPerfettoTraceTime(double perfNowTime); +uint64_t highResTimeStampToPerfettoTraceTime(HighResTimeStamp timestamp); } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp index c895334b7c5..d021a912d49 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp @@ -13,8 +13,6 @@ #include #endif -#include - namespace facebook::react { namespace { @@ -30,10 +28,9 @@ std::string toPerfettoTrackName( : PERFETTO_DEFAULT_TRACK_NAME; } #elif defined(WITH_FBSYSTRACE) -int64_t getDeltaNanos(double jsTime) { - auto now = std::chrono::steady_clock::now().time_since_epoch(); - return static_cast(jsTime * 1.e6) - - std::chrono::duration_cast(now).count(); +int64_t getDeltaNanos(HighResTimeStamp jsTime) { + auto now = HighResTimeStamp::now(); + return (jsTime - now).toNanoseconds(); } #endif @@ -51,8 +48,8 @@ int64_t getDeltaNanos(double jsTime) { /* static */ void ReactPerfettoLogger::measure( const std::string_view& eventName, - double startTime, - double endTime, + HighResTimeStamp startTime, + HighResTimeStamp endTime, const std::optional& trackName) { #if defined(WITH_PERFETTO) if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) { @@ -61,9 +58,9 @@ int64_t getDeltaNanos(double jsTime) { "react-native", perfetto::DynamicString(eventName.data(), eventName.size()), track, - performanceNowToPerfettoTraceTime(startTime)); + highResTimeStampToPerfettoTraceTime(startTime)); TRACE_EVENT_END( - "react-native", track, performanceNowToPerfettoTraceTime(endTime)); + "react-native", track, highResTimeStampToPerfettoTraceTime(endTime)); } #elif defined(WITH_FBSYSTRACE) static int cookie = 0; @@ -77,7 +74,7 @@ int64_t getDeltaNanos(double jsTime) { /* static */ void ReactPerfettoLogger::mark( const std::string_view& eventName, - double startTime, + HighResTimeStamp startTime, const std::optional& trackName) { #if defined(WITH_PERFETTO) if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) { @@ -85,7 +82,7 @@ int64_t getDeltaNanos(double jsTime) { "react-native", perfetto::DynamicString(eventName.data(), eventName.size()), getPerfettoWebPerfTrackSync(toPerfettoTrackName(trackName)), - performanceNowToPerfettoTraceTime(startTime)); + highResTimeStampToPerfettoTraceTime(startTime)); } #elif defined(WITH_FBSYSTRACE) static const char* kTrackName = "# Web Performance: Markers"; diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h index a85d06a5b39..7206d679fab 100644 --- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h +++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -24,13 +25,13 @@ class ReactPerfettoLogger { static void mark( const std::string_view& eventName, - double startTime, + HighResTimeStamp startTime, const std::optional& trackName); static void measure( const std::string_view& eventName, - double startTime, - double endTime, + HighResTimeStamp startTime, + HighResTimeStamp endTime, const std::optional& trackName); };