Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 0d7053c9c316e7b7281a24794163e2c33bf35b64
https://github.com/WebKit/WebKit/commit/0d7053c9c316e7b7281a24794163e2c33bf35b64
Author: Mark Lam <[email protected]>
Date: 2026-04-27 (Mon, 27 Apr 2026)
Changed paths:
M Source/WTF/WTF.xcodeproj/project.pbxproj
M Source/WTF/wtf/CMakeLists.txt
M Source/WTF/wtf/ClockType.cpp
M Source/WTF/wtf/ClockType.h
M Source/WTF/wtf/CurrentTime.cpp
M Source/WTF/wtf/TimeWithDynamicClockType.cpp
M Source/WTF/wtf/TimeWithDynamicClockType.h
A Source/WTF/wtf/UnbarrieredMonotonicTime.cpp
A Source/WTF/wtf/UnbarrieredMonotonicTime.h
Log Message:
-----------
Introduced UnbarrieredMonotonicTime.
https://bugs.webkit.org/show_bug.cgi?id=313432
rdar://175676940
Reviewed by Yusuke Suzuki and Marcus Plutowski.
UnbarrieredMonotonicTime has the timer resolution of MonotonicTime, but is
optimized for speed,
and does not do use any instruction barriers. As a result, a read of
UnbarrieredMonotonicTime
may be re-ordered by the CPU around adjacent instructions. This may reduce
fidelity in
accuracy for use cases like some profiling uses. However, for clients like the
Opportunistic
Task Scheduler, where we may want to sample the time more frequently, and are
only concerned
about expired time in the order of milliseconds, such slight loss of fidelity
in accuracy is
inconsequential.
At present, UnbarrieredMonotonicTime is only implemented with these new
semantics for ARM64 +
OS(DARWIN) only, and will fall back to MonotonicTime for all other ports.
Since UnbarrieredMonotonicTime is faster than ApproximateTime (whose sole
purpose is speed),
and has higher fidelity than ApproximateTime, we'll have ApproximateTime just
use
UnbarrieredMonotonicTime for the ARM64 + OS(DARWIN) port.
Implementation Details
======================
On ARM64 + OS(DARWIN), UnbarrieredMonotonicTime is implemented using the
CNTVCT_EL0 register [1],
and is calibrated with the CNTFRQ_EL0 [3]. According to ARM's architecture
description of Generic
Timers [4], specifically on the counter (CNTPCT_EL0) and frequency [5]:
The CNTPCT_EL0 system register reports the current system count value.
CNTFRQ_EL0 reports the frequency of the system count.
And, CNTVCT_EL0 [1] is in sync with CNTPCT_EL0 [2]:
The virtual count value is equal to the physical count value minus the
virtual offset visible in CNTVOFF_EL2.
UnbarrieredMonotonicTime computes monotonic time by recording the value of
MonotonicTime::now()
and CNTVCT_EL0 during an initial calibration, and then adding the time
displacement since that
initial point. The time displacement is computed as the counter displacement
(ticks) divided by
the counter frequency (ticks / second).
Timer Resolution
================
On ARM64 + OS(DARWIN), CNTFRQ_EL0 appears to be 1 GHz, which means each tick of
CNTVCT_EL0 measures
1 ns. However, this is a virtualized value. The actual timer resolution is
expected to be based
on OS(DARWIN)'s 24 MHz timer tick. This means CNTVCT_EL0 will only increment
once every 41.6667 ns.
Local microbenchmarks for timer resolution (averaged over 100000000 iterations)
measured the
resolution to be:
M5 Max MacBook Pro MacBook Neo
MonotonicTime: ~20.3 ns resolution ~14.0 ns resolution
ApproximateTime: ~12157.5 ns resolution ~23165.5 ns resolution
UnbarrieredMonotonicTime: ~23.5 ns resolution ~19.6 ns resolution
The measured resolution values are subject to noise, and should not be taken
literally. However,
their orders of magnitude matches expected timer resolutions.
Timer Speed
===========
Local microbenchmarks for timer speed (averaged over 100000000 iterations)
shows:
M5 Max MacBook Pro MacBook Neo
MonotonicTime: 8.68 ns/call 11.32 ns/call
ApproximateTime: 1.44 ns/call 2.22 ns/call
UnbarrieredMonotonicTime: 0.40 ns/call 0.41 ns/call
Timer Drift
===========
I ran a local test that runs 10000000000 iterations, with a 1s sleep every
1000000 iterations.
Each iteration samples MonotonicTime::now(), and
UnbarrieredMonotonicTime::now(), and compares
the difference between them. I ran the test for multiple hours (4+), and
observed the following:
1. UnbarrieredMonotonicTime sometimes "jump" ahead of MonotonicTime by
significant amount
(e.g. as high as 250+ us has been ovbserved). This is because MonotonicTime
is implemented
on top of mach_absolute_time(), and mach_absolute_time() is reading a
software counter value
that is updated by the kernel. The jumps are presumably due to the kernel
updates not firing
in time sometimes, probably around context switches.
The jumps are always such that UnbarrieredMonotonicTime is ahead of
MonotonicTime, never in
the opposite direction. This makes sense since UnbarrieredMonotonicTime is
based on the
hardware counter CNTVCT_EL0, and hence will be more accurate, and not be
impacted by needing
a kernel update of the value.
2. The difference between UnbarrieredMonotonicTime and MonotonicTime always
return to under 100ns
on subsequent samples, and remain under 100ns on average if we disregard the
"jump"s in (1).
This confirms that (1) is due to some lag in the kernel updating
mach_absolute_time, and
mach_absolute_time eventually catches up (within 1 sample based on my
observations).
This also confirms that UnbarrieredMonotonicTime is generally in sync with
MonotonicTime with
NO observable drift.
Tested only on M5 Max MacBook Pro.
Timer Monotonicity
==================
While running the hours of Timer Drift test, the test also check for
monotonicity and confirmed
that UnbarrieredMonotonicTime was never observed to go backward.
Tested on M5 Max MacBook Pro, and for a much smaller amount of time on MacBook
Neo.
ref:
[1]
https://developer.arm.com/documentation/ddi0601/2021-12/AArch64-Registers/CNTVCT-EL0--Counter-timer-Virtual-Count-register
[2]
https://developer.arm.com/documentation/ddi0601/2026-03/AArch64-Registers/CNTPCT-EL0--Counter-timer-Physical-Count-Register
[3]
https://developer.arm.com/documentation/ddi0601/2026-03/AArch64-Registers/CNTFRQ-EL0--Counter-timer-Frequency-Register
[4]
https://developer.arm.com/documentation/102379/0104/What-is-the-Generic-Timer-
[5]
https://developer.arm.com/documentation/102379/0104/The-processor-timers/Count-and-frequency
In addition to the above local testing, UnbarrieredMonotonicTime is also
covered by existing
tests to the extent that they exercise ApproximateTime.
* Source/WTF/WTF.xcodeproj/project.pbxproj:
* Source/WTF/wtf/CMakeLists.txt:
* Source/WTF/wtf/ClockType.cpp:
(WTF::printInternal):
* Source/WTF/wtf/ClockType.h:
* Source/WTF/wtf/CurrentTime.cpp:
* Source/WTF/wtf/TimeWithDynamicClockType.cpp:
(WTF::TimeWithDynamicClockType::now):
(WTF::TimeWithDynamicClockType::unbarrieredMonotonicTime const):
(WTF::TimeWithDynamicClockType::approximateWallTime const):
(WTF::TimeWithDynamicClockType::approximateMonotonicTime const):
* Source/WTF/wtf/TimeWithDynamicClockType.h:
* Source/WTF/wtf/UnbarrieredMonotonicTime.cpp: Added.
(WTF::UnbarrieredMonotonicTime::calibrate):
(WTF::UnbarrieredMonotonicTime::approximateWallTime const):
(WTF::UnbarrieredMonotonicTime::approximateMonotonicTime const):
(WTF::UnbarrieredMonotonicTime::approximateContinuousTime const):
(WTF::UnbarrieredMonotonicTime::dump const):
* Source/WTF/wtf/UnbarrieredMonotonicTime.h: Added.
(WTF::UnbarrieredMonotonicTime::readCounter):
(WTF::UnbarrieredMonotonicTime::now):
(WTF::MarkableTraits<UnbarrieredMonotonicTime>::isEmptyValue):
(WTF::MarkableTraits<UnbarrieredMonotonicTime>::emptyValue):
Canonical link: https://commits.webkit.org/312153@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications