Closed Bug 1826224 Opened 11 months ago Closed 4 months ago

Increase timer resolution on Windows (when not on battery) in Nightly

Categories

(Core :: Performance Engineering, enhancement)

All
Windows 11
enhancement

Tracking

()

RESOLVED FIXED
121 Branch
Tracking Status
firefox121 --- fixed

People

(Reporter: jlink, Assigned: jlink)

References

(Blocks 6 open bugs, Regressed 1 open bug)

Details

(Whiteboard: [sp3])

Attachments

(2 files)

Firefox (usually) doesn't do anything to adjust the timer resolution (see https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod / https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timeendperiod) on Windows.

On older versions of Windows this means that we inherit the resolution that other processes/applications have set (see https://randomascii.wordpress.com/2020/10/04/windows-timer-resolution-the-great-rule-change/) but with newer versions it means that we end up with the default resolution of 16ms.

This resolution affects the resolution at which a thread can wake up which can affect performance of behaviors that involve dependent timer usage. (Experiments have shown significant improvements in SP2 and SP3 scores when using a 1ms timer resolution.)

The purpose of this work is to explore options for when/how to increase the timer resolution. A good starting point seems to be to increase the resolution when not running on battery power.

Assignee: nobody → jlink
Whiteboard: [sp3]

Moving to appropriate component.

Component: Performance → Performance Engineering
Depends on: 1831014

Do you plan to look at CREATE_WAITABLE_TIMER_HIGH_RESOLUTION as part of this?

Flags: needinfo?(jlink)

(In reply to Jeff Muizelaar [:jrmuizel] from comment #2)

Do you plan to look at CREATE_WAITABLE_TIMER_HIGH_RESOLUTION as part of this?

I've got to admit that I kind of forgot about that. I'll have to take a look at that to remind myself how that works. If I am remembering correctly, this would involve a more significant change to the way that TimerThread works than what I had in mind for this bug. I'll try to refresh my memory on this but my inclination is to think that a change like that would be a separate piece of work.

Flags: needinfo?(jlink)

Previously Doug and I were both able to reproduce noticeable improvements in SP3 scores locally when increasing the resolution (and very little/none in CI, for the reasons alluded to in bug 1814951.

Something has changed in the meantime to make SP3 scores less sensitive to the timer resolution as, in recent testing, both Doug and I saw much more modest improvements in score (<= 1.0%, possibly zero) and, as expected, still virtually no change in CI.

This change still seems worth making, however, even if the performance improvement is small (or zero) as it will improve the experience for users. There are a number of bugs (seeing linked "blocking" bugs) related to website behavior where a page doesn't work correctly because they are, in some way, relying on high resolution timer behavior (which shouldn't be expected and isn't guaranteed but it's out there). With this change, those sites will now consistently work correctly (and other sites might be more responsive even if they "worked" before).

Attachment #9355666 - Attachment description: Bug 1826224: Enable high-precision timers on Windows when not on battery power r=smaug,florian → Bug 1826224: Enable high-precision timers on Windows for foreground processes when not on battery power r=smaug,florian
Status: NEW → ASSIGNED

Rust and Python have both now switched to using CREATE_WAITABLE_TIMER_HIGH_RESOLUTION.

Can you elaborate on how the TimerThread would need to change to use that instead?

(In reply to Justin Link from comment #5)

With this change, those sites will now consistently work correctly (and other sites might be more responsive even if they "worked" before).

Do they only work correctly if the user is not on battery?

Flags: needinfo?(jlink)

(In reply to Jeff Muizelaar [:jrmuizel] from comment #6)

Can you elaborate on how the TimerThread would need to change to use that instead?

I can not. :) I have seen that this is available but I haven't looked at it too closely.

From what I can tell, CreateWaitableTimerEx(CREATE_WAITABLE_TIMER_HIGH_RESOLUTION) allows you to create a waitable timer that will trigger precisely, even for small amounts of time, without using the timeBeginPeriod/timeEndPeriod functions.

What I don't understand, and haven't been about to find documentation on, is how this actually works under the hood. My assumption has been that it must still reprogram the underlying hardware resource (the timer chip) with a high frequency in order to do this. If that is the case, this way of doing timers might just be providing the benefit of convenience, not necessarily efficiency.

Do you know anything more/specific about timers created with this flag?

Mixing the two ways of doing timers might also limit/counteract the efficiency improvements that we're trying to make in certain situations. Bruce Dawson says here:

"When using our current mechanism (WaitForSingleObject/MultipleObject/Sleep) with a timeout our waits are synchronized to the regular timer interrupt so they are automatically aligned/coalesced - that is my understanding. This is particularly true when running on battery where we run the timer interrupt at a maximum of 125 Hz. There should be ~7-8 ms periods where all of our threads are sleeping, which Intel has said is crucial for reducing power draw.

If each thread (and each process) is using high-resolution waitable timers to ask the OS to wake them up in, say, 8 ms then the actual wakeup times could well end up being staggered such that the CPU never gets to sleep."

Incidentally, the current page for the Multimedia Timer Functions recommends against using these timer functions any more and instead using the Multimedia Class Scheduler Service.

(In reply to Justin Link from comment #5)

With this change, those sites will now consistently work correctly (and other sites might be more responsive even if they "worked" before).

Do they only work correctly if the user is not on battery?

Yes, that is correct, and a bit unfortunate. Or, at least it's unfortunate that they have been implemented in a way that relies on something that is not guaranteed. Perhaps by supporting this better we are becoming enablers? I'm not sure of the current status but, at least at one point, Chrome would also stay at a lower timer resolution when on battery power.

Flags: needinfo?(jlink)
Pushed by jlink@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/88295a805fb9
Enable high-precision timers on Windows for foreground processes when not on battery power r=smaug

Backed out for causing bustage on TimerThread.cpp

[task 2023-10-31T04:21:23.589Z] 04:21:23    ERROR -  /builds/worker/checkouts/gecko/xpcom/threads/TimerThread.cpp(49,7): error: use of undeclared identifier 'hal'
[task 2023-10-31T04:21:23.589Z] 04:21:23     INFO -     49 |       hal::PROCESS_PRIORITY_FOREGROUND;
[task 2023-10-31T04:21:23.589Z] 04:21:23     INFO -        |       ^
[task 2023-10-31T04:21:23.589Z] 04:21:23     INFO -  1 error generated.
[task 2023-10-31T04:21:23.589Z] 04:21:23    ERROR -  gmake[4]: *** [/builds/worker/checkouts/gecko/config/rules.mk:688: TimerThread.obj] Error 1
Flags: needinfo?(jlink)
Pushed by jlink@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/955d8904ddef
Enable high-precision timers on Windows for foreground processes when not on battery power r=smaug
Status: ASSIGNED → RESOLVED
Closed: 4 months ago
Resolution: --- → FIXED
Target Milestone: --- → 121 Branch

(In reply to Justin Link from comment #7)

(In reply to Jeff Muizelaar [:jrmuizel] from comment #6)

Can you elaborate on how the TimerThread would need to change to use that instead?

Do you know anything more/specific about timers created with this flag?

I ran Win10 under qemu with the following qemu patch:

diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index 6998094233..635d4e28eb 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -355,12 +355,25 @@ static const VMStateDescription vmstate_hpet = {
 
 static void hpet_arm(HPETTimer *t, uint64_t ticks)
 {
+    static int count;
+    static uint64_t last_ns;
     if (ticks < ns_to_ticks(INT64_MAX / 2)) {
+        uint64_t ns = ticks_to_ns(ticks);
+        uint64_t diff;
+        if (last_ns > ns)
+                diff = last_ns - ns;
+        else
+                diff = ns - last_ns;
+
+        if (count++ % 11 == 0 || diff > 100*1000) {
+            printf("hpet(%d) arm %fms\n", t->tn, ticks_to_ns(ticks)/(1000*1000.));
+        }
         timer_mod(t->qemu_timer,
                   qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks_to_ns(ticks));
     } else {
         timer_del(t->qemu_timer);
     }
+    last_ns = ticks_to_ns(ticks);
 }
 
 /*

This makes it pretty easy to see how Windows is interacting with the HPET hardware and the impact that timeBeginPeriod has.

I then did some tests with attached timer.cc program. Under normal circumstances, the HPET is firing every 15ms. If I ask for a CREATE_WAITABLE_TIMER_HIGH_RESOLUTION timer to fire every 800.5ms the HPET will continue to fire every 15ms up until the around the deadline for the for 800.5ms timer. Then it seems like it's adjusted so that we wake up close to the deadline.

If I run with timeBeginPeriod(1), I see HPET firing every 1ms.

This suggests that using CREATE_WAITABLE_TIMER_HIGH_RESOLUTION can be more efficient than timeBeginPeriod(1) because we only need to do an extra wakeup at the timer deadline.

Attached file timers.cc
Attachment #9361487 - Attachment mime type: application/octet-stream → text/plain
Regressions: 1862539
Blocks: 1872786
Regressions: 1873229
Summary: Increase timer resolution on Windows (when not on battery) → Increase timer resolution on Windows (when not on battery) in Nightly
Flags: needinfo?(jlink)
Blocks: 1881627
You need to log in before you can comment on or make changes to this bug.