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

Reply via email to