mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Define HighResTimeStamp, HighResDuration (#51254)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51254 # Changelog: [Internal] Defines single timestamp abstraction that will be used in C++ layer of React Native core: Key primitives include: - `HighResTimeStamp`: A class representing a specific point in time with high precision. - `HighResDuration`: A class representing a duration of time with high precision. Right now, all these just encapsulate `std::chrono::steady_clock` with no extra custom logic. We might revisit this in a future, once we decide to add support for [`Performance.timeOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin). This diff also adds a `README.md` documentation file with a brief summary of the `react/timing` module. Reviewed By: rubennorte Differential Revision: D72796412 fbshipit-source-id: 7a92b61202f5e527e020edea2b756b0063971242
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3e1b9a9182
commit
a882f19974
@@ -0,0 +1,90 @@
|
||||
# React Native Timing
|
||||
|
||||
[🏠 Home](../../../../../../__docs__/README.md)
|
||||
|
||||
This directory contains the shared C++ implementation of high-resolution timing
|
||||
primitives for React Native. These primitives provide precise time measurement
|
||||
capabilities that align with Web standards while being optimized for the React
|
||||
Native environment.
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
The timing primitives in this module are primarily used by internal React Native
|
||||
systems that require precise time measurements. The monotonic clock is used for
|
||||
these primitives: they should be used for measuring time intervals. These
|
||||
primitives should not be used for wall times. They are not expected to be used
|
||||
directly by application developers but serve as foundational components for
|
||||
various React Native features.
|
||||
|
||||
Key primitives include:
|
||||
|
||||
- `HighResTimeStamp`: A class representing a specific point in time with high
|
||||
precision.
|
||||
- `HighResDuration`: A class representing a duration of time with high
|
||||
precision.
|
||||
|
||||
These primitives support various operations:
|
||||
|
||||
```cpp
|
||||
// Getting the current high-resolution timestamp
|
||||
auto start = HighResTimeStamp::now();
|
||||
|
||||
// Creating durations
|
||||
auto duration = HighResDuration::fromNanoseconds(100);
|
||||
auto durationMs = HighResDuration::fromMilliseconds(100);
|
||||
|
||||
// Arithmetic operations
|
||||
auto later = start + duration;
|
||||
auto elapsed = later - start;
|
||||
|
||||
// Converting to absolute time units of highest precision
|
||||
auto end = HighResTimeStamp::now();
|
||||
int64_t nanoseconds = (end - start).toNanoseconds();
|
||||
|
||||
// Converting to DOMHighResTimeStamp (for JavaScript interoperability)
|
||||
double jsTimeValue = now.toDOMHighResTimeStamp();
|
||||
```
|
||||
|
||||
## 📐 Design
|
||||
|
||||
The timing primitives are designed to align with Web standards while leveraging
|
||||
C++'s type system and the performance characteristics of native code. The
|
||||
implementation uses `std::chrono` internally but provides a more specialized
|
||||
interface tailored to React Native's needs.
|
||||
|
||||
### HighResTimeStamp
|
||||
|
||||
This class represents a specific point in time with high precision. It
|
||||
encapsulates a `std::chrono::steady_clock::time_point` and provides methods to:
|
||||
|
||||
- Convert to `DOMHighResTimeStamp` for JavaScript interoperability.
|
||||
- Perform arithmetic operations with durations.
|
||||
- Compare with other timestamps.
|
||||
|
||||
### HighResDuration
|
||||
|
||||
This class represents a duration of time with high precision. It encapsulates a
|
||||
`std::chrono::duration` and provides methods to:
|
||||
|
||||
- Convert to `DOMHighResTimeStamp` for JavaScript interoperability.
|
||||
- Convert to an absolute number of nanoseconds.
|
||||
- Perform arithmetic operations.
|
||||
- Compare with other durations.
|
||||
|
||||
## 🔗 Relationship with other systems
|
||||
|
||||
### Used by
|
||||
|
||||
- [Event Loop](../../renderer/runtimescheduler/__docs__/README.md): Uses timing
|
||||
primitives for measuring task execution times and scheduling.
|
||||
- Web Performance API: Timing primitives are used to implement performance
|
||||
measurement APIs like `PerformanceObserver` entries (e.g., `longtask` and
|
||||
`event`).
|
||||
- React Native DevTools: The timing primitives integrate with the React Native
|
||||
DevTools tracing infrastructure to report the timing of tasks and events.
|
||||
|
||||
### Related to
|
||||
|
||||
- Web timing APIs: The timing primitives are designed to be compatible with Web
|
||||
timing concepts, making it easier to implement Web-compatible APIs in React
|
||||
Native.
|
||||
@@ -30,4 +30,274 @@ inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp(
|
||||
return chronoToDOMHighResTimeStamp(timePoint.time_since_epoch());
|
||||
}
|
||||
|
||||
class HighResDuration;
|
||||
class HighResTimeStamp;
|
||||
|
||||
/*
|
||||
* A class representing a duration of time with high precision.
|
||||
*
|
||||
* @see __docs__/README.md for more information.
|
||||
*/
|
||||
class HighResDuration {
|
||||
friend class HighResTimeStamp;
|
||||
friend constexpr HighResDuration operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResTimeStamp& rhs);
|
||||
friend constexpr HighResTimeStamp operator+(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs);
|
||||
friend constexpr HighResTimeStamp operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs);
|
||||
|
||||
public:
|
||||
constexpr HighResDuration()
|
||||
: chronoDuration_(std::chrono::steady_clock::duration()) {}
|
||||
|
||||
static constexpr HighResDuration zero() {
|
||||
return HighResDuration(std::chrono::steady_clock::duration::zero());
|
||||
}
|
||||
|
||||
static constexpr HighResDuration fromChrono(
|
||||
std::chrono::steady_clock::duration chronoDuration) {
|
||||
return HighResDuration(chronoDuration);
|
||||
}
|
||||
|
||||
static constexpr HighResDuration fromNanoseconds(int64_t units) {
|
||||
return HighResDuration(std::chrono::nanoseconds(units));
|
||||
}
|
||||
|
||||
static constexpr HighResDuration fromMilliseconds(int64_t units) {
|
||||
return HighResDuration(std::chrono::milliseconds(units));
|
||||
}
|
||||
|
||||
constexpr int64_t toNanoseconds() const {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(chronoDuration_)
|
||||
.count();
|
||||
}
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
|
||||
static constexpr HighResDuration fromDOMHighResTimeStamp(double units) {
|
||||
auto nanoseconds = static_cast<int64_t>(units * 1e6);
|
||||
return fromNanoseconds(nanoseconds);
|
||||
}
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
|
||||
constexpr double toDOMHighResTimeStamp() const {
|
||||
return static_cast<std::chrono::duration<double, std::milli>>(
|
||||
chronoDuration_)
|
||||
.count();
|
||||
}
|
||||
|
||||
constexpr HighResDuration operator+(const HighResDuration& rhs) const {
|
||||
return HighResDuration(chronoDuration_ + rhs.chronoDuration_);
|
||||
}
|
||||
|
||||
constexpr HighResDuration operator+(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return HighResDuration(chronoDuration_ + rhs);
|
||||
}
|
||||
|
||||
constexpr HighResDuration operator-(const HighResDuration& rhs) const {
|
||||
return HighResDuration(chronoDuration_ - rhs.chronoDuration_);
|
||||
}
|
||||
|
||||
constexpr HighResDuration operator-(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return HighResDuration(chronoDuration_ - rhs);
|
||||
}
|
||||
|
||||
constexpr HighResDuration& operator+=(const HighResDuration& rhs) {
|
||||
chronoDuration_ += rhs.chronoDuration_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr HighResDuration& operator+=(
|
||||
const std::chrono::steady_clock::duration& rhs) {
|
||||
chronoDuration_ += rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr HighResDuration& operator-=(const HighResDuration& rhs) {
|
||||
chronoDuration_ -= rhs.chronoDuration_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr HighResDuration& operator-=(
|
||||
const std::chrono::steady_clock::duration& rhs) {
|
||||
chronoDuration_ -= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ == rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator==(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ == rhs;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ != rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ != rhs;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ < rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator<(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ < rhs;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ <= rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ <= rhs;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ > rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator>(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ > rhs;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const HighResDuration& rhs) const {
|
||||
return chronoDuration_ >= rhs.chronoDuration_;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(
|
||||
const std::chrono::steady_clock::duration& rhs) const {
|
||||
return chronoDuration_ >= rhs;
|
||||
}
|
||||
|
||||
constexpr operator std::chrono::steady_clock::duration() const {
|
||||
return chronoDuration_;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit constexpr HighResDuration(
|
||||
std::chrono::steady_clock::duration chronoDuration)
|
||||
: chronoDuration_(chronoDuration) {}
|
||||
|
||||
std::chrono::steady_clock::duration chronoDuration_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A class representing a specific point in time with high precision.
|
||||
*
|
||||
* @see __docs__/README.md for more information.
|
||||
*/
|
||||
class HighResTimeStamp {
|
||||
friend constexpr HighResDuration operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResTimeStamp& rhs);
|
||||
friend constexpr HighResTimeStamp operator+(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs);
|
||||
friend constexpr HighResTimeStamp operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs);
|
||||
|
||||
public:
|
||||
HighResTimeStamp() noexcept
|
||||
: chronoTimePoint_(std::chrono::steady_clock::now()) {}
|
||||
|
||||
static constexpr HighResTimeStamp now() noexcept {
|
||||
return HighResTimeStamp(std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
static constexpr HighResTimeStamp min() noexcept {
|
||||
return HighResTimeStamp(std::chrono::steady_clock::time_point::min());
|
||||
}
|
||||
|
||||
static constexpr HighResTimeStamp max() noexcept {
|
||||
return HighResTimeStamp(std::chrono::steady_clock::time_point::max());
|
||||
}
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
|
||||
static constexpr HighResTimeStamp fromDOMHighResTimeStamp(double units) {
|
||||
auto nanoseconds = static_cast<int64_t>(units * 1e6);
|
||||
return HighResTimeStamp(std::chrono::steady_clock::time_point(
|
||||
std::chrono::nanoseconds(nanoseconds)));
|
||||
}
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
|
||||
constexpr double toDOMHighResTimeStamp() const {
|
||||
return HighResDuration(chronoTimePoint_.time_since_epoch())
|
||||
.toDOMHighResTimeStamp();
|
||||
}
|
||||
|
||||
constexpr bool operator==(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ == rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ != rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ < rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ <= rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ > rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const HighResTimeStamp& rhs) const {
|
||||
return chronoTimePoint_ >= rhs.chronoTimePoint_;
|
||||
}
|
||||
|
||||
constexpr HighResTimeStamp& operator+=(const HighResDuration& rhs) {
|
||||
chronoTimePoint_ += rhs.chronoDuration_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr HighResTimeStamp& operator-=(const HighResDuration& rhs) {
|
||||
chronoTimePoint_ -= rhs.chronoDuration_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit constexpr HighResTimeStamp(
|
||||
std::chrono::steady_clock::time_point chronoTimePoint)
|
||||
: chronoTimePoint_(chronoTimePoint) {}
|
||||
|
||||
std::chrono::steady_clock::time_point chronoTimePoint_;
|
||||
};
|
||||
|
||||
inline constexpr HighResDuration operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResTimeStamp& rhs) {
|
||||
return HighResDuration(lhs.chronoTimePoint_ - rhs.chronoTimePoint_);
|
||||
}
|
||||
|
||||
inline constexpr HighResTimeStamp operator+(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs) {
|
||||
return HighResTimeStamp(lhs.chronoTimePoint_ + rhs.chronoDuration_);
|
||||
}
|
||||
|
||||
inline constexpr HighResTimeStamp operator-(
|
||||
const HighResTimeStamp& lhs,
|
||||
const HighResDuration& rhs) {
|
||||
return HighResTimeStamp(lhs.chronoTimePoint_ - rhs.chronoDuration_);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -44,4 +44,88 @@ TEST(chronoToDOMHighResTimeStamp, withTimePoints) {
|
||||
1000.000020);
|
||||
}
|
||||
|
||||
TEST(HighResDuration, CorrectlyConvertsToDOMHighResTimeStamp) {
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromNanoseconds(10).toDOMHighResTimeStamp(), 0.00001);
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromNanoseconds(10 * 1e3).toDOMHighResTimeStamp(), 0.01);
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromNanoseconds(10 * 1e6).toDOMHighResTimeStamp(), 10.0);
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromNanoseconds(10 * 1e9).toDOMHighResTimeStamp(),
|
||||
10000.0);
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromNanoseconds(1e9 + 20).toDOMHighResTimeStamp(),
|
||||
1000.000020);
|
||||
|
||||
EXPECT_EQ(HighResDuration::fromMilliseconds(0).toDOMHighResTimeStamp(), 0);
|
||||
EXPECT_EQ(
|
||||
HighResDuration::fromMilliseconds(10).toDOMHighResTimeStamp(), 10.0);
|
||||
}
|
||||
|
||||
TEST(HighResDuration, ComparisonOperators) {
|
||||
auto duration1 = HighResDuration::fromNanoseconds(10);
|
||||
auto duration2 = HighResDuration::fromNanoseconds(20);
|
||||
auto duration3 = HighResDuration::fromNanoseconds(10);
|
||||
|
||||
EXPECT_TRUE(duration1 == duration3);
|
||||
EXPECT_FALSE(duration1 == duration2);
|
||||
|
||||
EXPECT_TRUE(duration1 != duration2);
|
||||
EXPECT_FALSE(duration1 != duration3);
|
||||
|
||||
EXPECT_TRUE(duration1 < duration2);
|
||||
EXPECT_FALSE(duration2 < duration1);
|
||||
EXPECT_FALSE(duration1 < duration3);
|
||||
|
||||
EXPECT_TRUE(duration1 <= duration2);
|
||||
EXPECT_TRUE(duration1 <= duration3);
|
||||
EXPECT_FALSE(duration2 <= duration1);
|
||||
|
||||
EXPECT_TRUE(duration2 > duration1);
|
||||
EXPECT_FALSE(duration1 > duration2);
|
||||
EXPECT_FALSE(duration1 > duration3);
|
||||
|
||||
EXPECT_TRUE(duration2 >= duration1);
|
||||
EXPECT_TRUE(duration1 >= duration3);
|
||||
EXPECT_FALSE(duration1 >= duration2);
|
||||
}
|
||||
|
||||
TEST(HighResDuration, ArithmeticOperators) {
|
||||
auto duration1 = HighResDuration::fromChrono(std::chrono::nanoseconds(100));
|
||||
auto duration2 = HighResDuration::fromChrono(std::chrono::nanoseconds(50));
|
||||
|
||||
EXPECT_EQ(duration1 + duration2, std::chrono::nanoseconds(150));
|
||||
EXPECT_EQ(duration1 - duration2, std::chrono::nanoseconds(50));
|
||||
EXPECT_EQ(duration2 - duration1, std::chrono::nanoseconds(-50));
|
||||
}
|
||||
|
||||
TEST(HighResTimeStamp, ComparisonOperators) {
|
||||
auto now = HighResTimeStamp::now();
|
||||
auto later = now + HighResDuration::fromNanoseconds(1);
|
||||
auto nowCopy = now;
|
||||
|
||||
EXPECT_TRUE(now == nowCopy);
|
||||
EXPECT_FALSE(now == later);
|
||||
|
||||
EXPECT_TRUE(now != later);
|
||||
EXPECT_FALSE(now != nowCopy);
|
||||
|
||||
EXPECT_TRUE(now < later);
|
||||
EXPECT_FALSE(later < now);
|
||||
EXPECT_FALSE(now < nowCopy);
|
||||
|
||||
EXPECT_TRUE(now <= later);
|
||||
EXPECT_TRUE(now <= nowCopy);
|
||||
EXPECT_FALSE(later <= now);
|
||||
|
||||
EXPECT_TRUE(later > now);
|
||||
EXPECT_FALSE(now > later);
|
||||
EXPECT_FALSE(now > nowCopy);
|
||||
|
||||
EXPECT_TRUE(later >= now);
|
||||
EXPECT_TRUE(now >= nowCopy);
|
||||
EXPECT_FALSE(now >= later);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
Reference in New Issue
Block a user