The behavior of the Dwelling windows scheduler changed vastly in Dwelling windows 10 2004, in a formulation that will rupture a pair of functions, and there seems to were no announcement, and the documentation has no longer been updated. This isn’t the first time this has befell, nonetheless this commerce seems bigger than last time.
The short model is that calls to timeBeginPeriod from one job now maintain an value on assorted processes much less than they outdated to, nonetheless there is unexcited an attain.
I feel the contemporary behavior is an development, nonetheless it completely’s uncommon, and it deserves to be documented. Elegant warning – all I if truth be told maintain are the outcomes of experiments I if truth be told maintain hurry, so I will perfect speculate about the quirks and targets of this commerce. If any of my conclusions are execrable then please let me know and I could replace this.
Timer interrupts and their raison d’être
First, a diminutive of working-map make context. It’s tidy for a program so that you just can proceed to sleep and then rating up a diminutive while later. This genuinely shouldn’t be carried out very assuredly – threads must unexcited assuredly be ready on occasions in desire to timers – nonetheless it completely is once in some time main. And so we’ve got the Dwelling windows Sleep characteristic – proceed it the desired dimension of your nap in milliseconds and it wakes you up later, love this:
It’s price pausing for a moment to take into yarn how right here is utilized. Ideally the CPU goes to sleep when Sleep(1) is named, in picture to assign energy, so how does the working map (OS) wake your thread if the CPU is drowsing? The reply is hardware interrupts. The OS programs a timer chip that then triggers an interrupt that wakes up the CPU and the OS can then time table your thread.
The WaitForSingleObject and WaitForMultipleObjects functions additionally maintain timeout values and these timeouts are utilized the use of the same mechanism.
If there are hundreds threads all ready on timers then the OS could per chance per chance presumably program the timer chip with particular person wakeup times for every thread, nonetheless this tends to consequence in threads waking up at random times and the CPU never getting to maintain a prolonged nap. CPU energy effectivity is strongly tied to how prolonged the CPU can quit asleep (8+ ms is outwardly a graceful number), and random wakeups work against that. If a pair of threads can synchronize or coalesce their timer waits then the map turns into more energy ambiance suitable.
There are lots of ways to coalesce wakeups nonetheless the major mechanism outdated by Dwelling windows is to maintain a world timer interrupt that ticks at a in model rate. When a thread calls Sleep(n) then the OS will time table the thread to hurry when the first timer interrupt fires after the time has elapsed. This suggests that the thread could per chance per chance presumably just cease up waking up a diminutive late, nonetheless Dwelling windows will not be any longer a accurate-time OS and it genuinely cannot allege a particular wakeup time (there could per chance per chance presumably just no longer be a CPU core accessible at that time anyway) so waking up a diminutive late must unexcited be ravishing.
The interval between timer interrupts depends on the Dwelling windows model and for your hardware nonetheless on every machine I if truth be told maintain outdated just no longer too prolonged ago the default interval has been 15.625 ms (1,000 ms divided by 64). That formulation that in case you call Sleep(1) at some random time then you definately will seemingly be woken sometime between 1.0 ms and 16.625 ms in the slay, every time the subsequent interrupt fires (or the one after that if the subsequent interrupt is just too soon).
In short, it’s the character of timer delays that (except a busy wait is outdated, and please don’t busy wait) the OS can perfect rating up threads at a particular time by the use of timer interrupts, and a extraordinary timer interrupt is what Dwelling windows uses.
Some programs (WPF, SQL Server, Quartz, PowerDirector, Chrome, the Rush Runtime, many video games, and plenty of others.) find this significant variance in wait delays laborious to take care of nonetheless fortunately there is a characteristic that permits them to control this. timeBeginPeriod lets a program search files from a smaller timer interrupt interval by passing in a requested timer interrupt interval. There’s additionally NtSetTimerResolution which allows setting the interval with sub-millisecond precision nonetheless that is rarely outdated and never wished so I obtained’t mention it once more.
Decades of madness
Here’s the loopy factor: timeBeginPeriod will seemingly be called by any program and it modifications the timer interrupt interval, and the timer interrupt is a world handy resource.
Let’s imagine that Path of A is sitting in a loop calling Sleep(1). It shouldn’t be doing this, nonetheless it completely is, and by default it’s miles waking up every 15.625 ms, or 64 times a 2nd. Then Path of B comes along and calls timeBeginPeriod(2). This makes the timer interrupt fire more often and all of sudden Path of A is waking up 500 times a 2nd in space of 64 times a 2nd. That’s loopy! Nonetheless that’s how Dwelling windows has continuously worked.
At this level if Path of C came along and called timeBeginPeriod(4) this wouldn’t commerce the relaxation – Path of A would proceed to rating up 500 times a 2nd. It’s no longer last-call-gadgets-the-principles, it’s lowest-search files from-gadgets-the-principles.
To be more particular, no matter unexcited working program has specified the smallest timer interrupt duration in an famend call to timeBeginPeriod will get to plan the arena timer interrupt interval. If that program exits or calls timeEndPeriod then the contemporary minimal takes over. If a single program called timeBeginPeriod(1) then that is the timer interrupt interval for all of the map. If one program called timeBeginPeriod(1) and one other program then called timeBeginPeriod(4) then the one ms timer interrupt interval could per chance be the law of the land.
This issues on yarn of a high timer interrupt frequency – and the related high-frequency of thread scheduling – can extinguish primary energy, as discussed right here.
timeGetTime (no longer to be at a loss for words with GetTickCount) is a characteristic that returns the present time, as updated by the timer interrupt. CPUs maintain traditionally no longer been graceful at holding graceful time (their clocks deliberately fluctuate to book fine of being FM transmitters, and for assorted reasons) so they assuredly rely upon separate clock chips to abet graceful time. Finding out from these clock chips is dear so Dwelling windows maintains a 64-bit counter of the time, in milliseconds, as updated by the timer interrupt. This timer is stored in shared reminiscence so any job can cheaply be taught the present time from there, with out having to chat to the timer chip. timeGetTime calls ReadInterruptTick which at its core factual reads this 64-bit counter. Straightforward!
Since this counter is updated by the timer interrupt we are in a position to video display it and find the timer interrupt frequency.
The contemporary undocumented reality
With the Dwelling windows 10 2004 (April 2020 free up) some of this quietly changed, nonetheless in a genuinely complicated formulation. I first heard about this via reports that timeBeginPeriod didn’t work anymore. The very fact became once more complicated than this.
A diminutive little bit of experimentation gave complicated results. When I ran a program that called timeBeginPeriod(2) then clockres showed that the timer interval became once 2.0 ms, nonetheless a separate test program with a Sleep(1) loop became once perfect waking up about 64 times a 2nd in space of the 500 times a 2nd that it can per chance presumably maintain woken up under old variations of Dwelling windows.
It’s time to abet out science
I then wrote a pair of programs which revealed what became once going on. One program (change_interval.cpp) factual sits in a loop calling timeBeginPeriod with intervals starting from 1 to 15 ms. It holds every timer interval search files from for four seconds, and then goes to the subsequent one, wrapping round when it’s miles completed. It’s fifteen traces of code. Easy.
The various program (measure_interval.cpp) runs some assessments to see how significant its behavior is altered by the behavior of change_interval.cpp. It does this by gathering three items of files.
- It asks the OS what the present world timer resolution is, the use of NtQueryTimerResolution.
- It measures the precision of timeGetTime by calling it in a loop except its return price modifications. When it modifications then the amount it changed by is its precision.
- It measures the prolong of Sleep(1) by calling it in a loop for a 2nd and counting what number of calls it can per chance presumably make. The in model prolong is factual the reciprocal of the selection of iterations.
@FelixPetriconi ran the assessments for me on Dwelling windows 10 1909 and I ran the assessments on Dwelling windows 10 2004. The outcomes (cleaned up to capture randomness) are shown right here:
What this procedure is that timeBeginPeriod unexcited gadgets the arena timer interrupt interval, on all variations of Window. We are in a position to clarify from the outcomes of timeGetTime() that the interrupt fires on a minimal of one CPU core at that rate, and the time is updated. Display additionally that the 2.0 on row one for 1909 became once 2.0 on Dwelling windows XP, then 1.0 on Dwelling windows 7/8, and is outwardly lend a hand to 2.0? I train?
On the opposite hand the scheduler behavior modifications dramatically in Dwelling windows 10 2004. Beforehand the prolong for Sleep(1) in any job became once merely the same as the timer interrupt interval (with an exception for timeBeginPeriod(1)), giving a graph love this:
In Dwelling windows 10 2004 the mapping between timeBeginPeriod and the sleep prolong in a single other job (one which didn’t call timeBeginPeriod) is odd:
The actual shape of the left facet of the graph is unclear nonetheless it completely positively slopes in the reverse route from forward of!
That is all very uncommon, and I don’t stamp the reason. Maybe it’s a bug, nonetheless I doubt it. I feel that there is advanced backwards compatibility good judgment at the lend a hand of this. Nonetheless, the most highly fantastic formulation to book fine of compatibility problems is to doc your modifications, preferably in scheme, and this seems to were slipped in with out anybody being notified.
It seems to me that it can per chance presumably were more fantastic to factual assert that the scheduler in Path of A would now be unaffected (as significant as that it’s possible you’ll per chance presumably glean) by the timer interrupt interval, except Path of A had called timeBeginPeriod. Nonetheless they didn’t assign a question to me.
Most programs will seemingly be unaffected. If a job desires a sooner timer interrupt then it can per chance presumably unexcited be calling timeBeginPeriod itself. That talked about, listed below are the issues that this could per chance per chance presumably trigger:
- A program could per chance per chance presumably by chance resolve that Sleep(1) and timeGetTime maintain an analogous resolutions, and that assumption is broken now. Nonetheless, such an assumption seems unlikely.
- A program could per chance per chance presumably rely upon a quickly timer resolution and fail to search files from it. There were a pair of claims that some video games maintain this self-discipline and there is a instrument called Dwelling windows Scheme Timer Scheme and one other called TimerResolution 1.2 that “fix” these video games by elevating the timer interrupt frequency. These fixes presumably obtained’t work anymore, or a minimal of no longer as effectively. Maybe this could per chance per chance force these video games to abet out a exact fix, nonetheless except then this commerce is a backwards compatibility self-discipline.
- A multi-job program could per chance per chance presumably just want its grasp adjust program elevate the timer interrupt frequency and then request that this could per chance maintain an value on the scheduling of its diminutive one processes. This outdated to be an inexpensive make decision, and now it doesn’t work. That is how I became once alerted to this self-discipline. The product in query now calls timeBeginPeriod in all of their processes so they are ravishing, thanks for asking, nonetheless their instrument became once misbehaving for several months without a explanation.
The change_interval.cpp test program perfect works if nothing has requested a elevated timer interrupt frequency. Since both Chrome and Visible Studio maintain a behavior of doing this I needed to abet out most of my experimentation without a rating admission to to the glean while writing code in notepad. Any individual suggested Emacs nonetheless wading into that debate is more than I’m titillating to abet out.
I’d love to listen to more about this from Microsoft, along with any corrections to my diagnosis.