Increase timer resolution on Windows (when not on battery) in Nightly
Categories
(Core :: Performance Engineering, enhancement)
Tracking
()
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 | ||
Updated•2 years ago
|
Updated•2 years ago
|
Updated•2 years ago
|
Comment 1•2 years ago
|
||
Moving to appropriate component.
Comment 2•2 years ago
|
||
Do you plan to look at CREATE_WAITABLE_TIMER_HIGH_RESOLUTION as part of this?
Assignee | ||
Comment 3•2 years ago
|
||
(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.
Assignee | ||
Comment 4•1 year ago
|
||
Assignee | ||
Updated•1 year ago
|
Assignee | ||
Comment 5•1 year ago
•
|
||
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).
Updated•1 year ago
|
Assignee | ||
Updated•1 year ago
|
Comment 6•1 year ago
|
||
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?
Assignee | ||
Comment 7•1 year ago
|
||
(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.
Comment 9•1 year ago
|
||
Backed out for causing bustage on TimerThread.cpp
- backout: https://hg.mozilla.org/integration/autoland/rev/2c44d59dd29eb01ea0b484c9f3f8691861118073
- push: https://treeherder.mozilla.org/jobs?repo=autoland&group_state=expanded&selectedTaskRun=LVteRTN2QTy9uOBf4D8iyw.0&revision=88295a805fb918c5a40c57d9827608c50c65da1a
- failure log: https://treeherder.mozilla.org/logviewer?job_id=434376156&repo=autoland&lineNumber=122389
[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
Comment 10•1 year ago
|
||
Comment 11•1 year ago
|
||
bugherder |
Comment 12•1 year ago
|
||
(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.
Comment 13•1 year ago
|
||
Updated•1 year ago
|
Updated•11 months ago
|
Assignee | ||
Updated•11 months ago
|
Description
•