New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: windows system clock resolution issue #8687

Open
defia opened this Issue Sep 9, 2014 · 65 comments

Comments

Projects
None yet
@defia

defia commented Sep 9, 2014

What does 'go version' print?

go version go1.3.1 windows/amd64

actually it's just the same as the issue in chrome:
https://code.google.com/p/chromium/issues/detail?id=153139

What steps reproduce the problem?

1.download and run system clock resolution tool provided by the link in
https://code.google.com/p/chromium/issues/detail?id=153139
it will show your current timer interval
2.if current timer interval smaller than 15.625ms,try to close running programs until it
goes to 15.625ms
3.run any program compiled by go

What happened?
current timer interval goes to 1ms when go compiled bins running,and goes to 15.625
after quiting

What should have happened instead?
current timer interval should not change

at least,with no time package used, the timer setting should not change, or there should
be a method to control this
@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Sep 9, 2014

Comment 1:

Labels changed: added repo-main, release-go1.4, os-windows.

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Sep 10, 2014

Comment 2:

I confirm what DefiaQ said.
The problem is that we call timeBeginPeriod Windows API. It was introduced as part of
pprof implementation
changeset:   9786:9c5c0cbadb4d                           
user:        Hector Chu <hectorchu@gmail.com>            
date:        Sat Sep 17 17:57:59 2011 +1000              
summary:     runtime: implement pprof support for windows
but I view it as "windows timer precision is very very low (about 15ms) comparing to
other OSes, so lets get best precision whenever we can (1ms)".
timeBeginPeriod has many problems. It does not work on some systems (it does not work on
my oldish windows xp). I suspect, it fails, if you don't have correct access rights on
the system (it works for administrator). When successfully set timeBeginPeriod will
drain laptop batteries faster (I read about it everywhere, I didn't have chance to
experience it on practice). But, I suspect, many other applications call timeBeginPeriod
too, so Go is not the only one at fault here.
Personally I don't care if timeBeginPeriod is set, but others might feel different.
Perhaps there is case here for some way to control it from within Go program.
ALternatively, people who care, can just remove timeBeginPeriod call in their own
runtime.
Alex
@defia

This comment has been minimized.

defia commented Sep 10, 2014

Comment 3:

it will succeed even without administrator privilege.
so far i didn't find any applications other than chrome change timer precesion
@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Sep 10, 2014

Comment 4:

Well, I am not running chrome but my timer interval is set to 1ms.
Also, why did you said "... if current timer interval smaller than 15.625ms,try to close
running programs until it goes to 15.625ms .. "? You should have said " ... close chrome
...".
Alex
@defia

This comment has been minimized.

defia commented Sep 10, 2014

Comment 5:

i also suspect other applications will change the setting , though i havn't found any so
far
@rsc

This comment has been minimized.

Contributor

rsc commented Sep 18, 2014

Comment 6:

Too late for 1.4. Maybe for a future release, if someone wants to do the work to prove
that it matters and that removing this is safe. I think without a clear report about bad
implications we won't do that work ourselves.

Labels changed: added release-none, removed release-go1.4.

Status changed to Accepted.

@defia defia added accepted labels Sep 18, 2014

@defia

This comment has been minimized.

defia commented Dec 9, 2014

i made a package to call timeEndPeriod to reset the timer to normal on initialization.

if the program doesn't need much performance, but runs on a windows laptop for a long time, just import the package.
i've using this for some days, everything seems to be well so far

@defia

This comment has been minimized.

defia commented Dec 11, 2014

I've done some benchmarks today.
At first I ran a cpu-heavy program and modified it to use hundreds of goroutines. To my surprise,it actually ran ~5% faster if I set the system clock resolution to 15.6ms(that is default value)

Then I ran this benchmark
I didn't look into the code, I guess it use only one goroutine, still pure cpu thing, and get still the same result.

Then I wrote a http benchmark here
This time it ran a lot faster(~50%) with 1ms system clock resolution.

So I guess, if your program don't use much IO (cause less context switches of goroutine), you can discard the system clock resoluton change.

@minux

This comment has been minimized.

Member

minux commented Dec 11, 2014

Could we use QueryPerformanceCounter for pprof support?

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Dec 11, 2014

And how are you going to use QueryPerformanceCounter in pprof support?

Alex

@mattn

This comment has been minimized.

Member

mattn commented Dec 12, 2014

cyclesPerSecond uses systime in os1_windows.go

https://github.com/golang/go/blob/master/src/runtime/os1_windows.go#L314-L316

https://github.com/golang/go/blob/master/src/runtime/os1_windows.go#L286-L306

This part is possible to be replaced with QueryUnbiasedInterruptTime (not QueryPerformanceCounter).

@rsc rsc removed the os-windows label Apr 10, 2015

@rsc rsc added this to the Unplanned milestone Apr 10, 2015

@rsc rsc added OS-Windows and removed release-none labels Apr 10, 2015

@randomascii

This comment has been minimized.

randomascii commented Jun 12, 2015

I've blogged about this issue in the past (https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/) and I wanted to share what I have learned:

As defia pointed out, having a higher timer frequency wastes CPU time and can make some CPU heavy programs run more slowly. This is because the OS is waking up ~15 times more frequently, consuming CPU time each time it does. It also means that other programs may wake up more frequently and waste CPU time - some software sits in Sleep(1) loops and will wake up ~15 times more frequently when the timer resolution is changed. So, raising the timer frequency can waste performance.

Raising the timer frequency will almost always waste some power and will harm battery life. This is the kicker. This is why no program should ever raise the timer frequency unless it needs the timer frequency raised. The timer frequency is a global setting and raising it without a good reason is poor citizenship.

If you have a work loop that relies on Sleep(1) or timeouts from WaitForSingleObject(handle, 1) then raising the timer frequency will make your loop run faster. Presumably that is why the http test ran faster. Most software should not be relying on timeouts, it should be running based on events. But, it is reasonable for specific go programs to raise the timer frequency if needed.

It is not reasonable for the go runtime to unilaterally raise timer frequency.

@randomascii

This comment has been minimized.

randomascii commented Jun 12, 2015

With the background information out of the way...

I discovered this issue because I noticed (using clockres.exe) that the timer frequency on my computer was always raised. I used "powercfg /energy /duration 5" to identify the culprit, a go program that was running in the background. This go program was monitoring system status and was idle about 99.9% of the time but because of the go runtime's decision to raise the timer frequency for all go programs this background program was measurably affecting power consumption and battery life. This 99.9% idle go program was affecting my computer more than a program that was actively running.

This was clearly a case where the program did not need a higher timer frequency. Raising the timer frequency is a reasonable option for the go runtime, but it is absolutely not a reasonable default for the go runtime.

Chrome has been fixed to not leave the timer frequency high, so has SDL, and so have many other products. Go needs to do likewise. Please.

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Sep 15, 2015

@randomascii

I appreciate and share your concerns. So I tried to measure the impact of removing timeBeginPeriod call on my windows 7 amd64 computer. (see bench.txt in https://gist.github.com/alexbrainman/914885b57518cb4a6e39 ). Unfortunately I can see quite significant slowdowns here and there. The change looks like a no go for me. Maybe gophers who know more about Go scheduler can explain the slowdowns and suggest ways to improve the situation.

For the moment, if someone cares about this, they can comment out call to timeBeginPeriod.

Alex

@randomascii

This comment has been minimized.

randomascii commented Sep 15, 2015

I know that some video applications (games, Chrome, a few others) have to leave the frequency high in order to accurately schedule tasks and meet frame rates. For other programs I don't understand the need to have the timer frequency high. I would imagine that tasks would typically be happening as quickly as possible (reading files, doing calculations, passing messages) and I'm curious as to what sort of timeouts, or waits, or sleeps are being affected by the timer frequency.

Anyway, if some Go programs need the timer frequency then those Go programs should raise the timer frequency. I have no problem with that. But if the Go runtime raises the timer frequency then that is a problem. We could debate whether raising the timer frequency should be opt-in (my strong vote) or opt-out, but it should be a choice. Otherwise long-running low-activity Go programs will be blacklisted by anybody who wants good battery life.

Go deserves better than that.

@alexbrainman

This comment has been minimized.

Member

alexbrainman commented Sep 15, 2015

I would imagine that tasks would typically be happening as quickly as possible ...

Things are never simple, it is always about trade-offs. But I don't know enough about Go runtime to give you explanation. And I am hopeful that other gophers will comment. All I can say is that Windows makes the task much harder when everything is measured in 15 milliseconds (comparing to nanoseconds on linux) - imagine running scheduler that performs equally well on both. But maybe Go runtime can be improved on windows, and we can disable timeBeginPeriod call.

Alex

@randomascii

This comment has been minimized.

randomascii commented Sep 15, 2015

FWIW, an ETW trace (see https://github.com/google/UIforETW/releases for the easiest way to record one) can be used to record, among other things, all context switches. This makes it possible to determine where applications are waiting.

The Windows timer resolution is a continuing source of frustration to be sure, but it only slows down code that calls Sleep(n) or WaitForSingleObject(handle, n) (where 'n' is not INFINITE).

Looking at bench.txt I see that *OneShotTimeout-2 runs slower with a lower timer frequency. That sounds like it is by-design and shouldn't be used to justify all Go programs running with a high timer frequency. Ditto with BenchmarkGoroutineIdle-2.

BenchmarkAppendGrowByte-2 runs 368% slower which suggests a benchmark bug. Either the code being tested is waiting (which it should not do) or the code that times is using timeGetTime and is stopping and starting the timer frequently and is therefore hitting aliasing errors.

I strongly suspect that the latter is the problem. timeGetTime's precision is increased when the timer frequency is increased. The benchmarks with the lower timer precision are mostly not slower they are just less accurate. The correct fix is not to raise the timer frequency (the timers will still be inaccurate) but to use accurate timers such as QueryPerformanceCounter.

It is a frustrating problem I'll agree, but raising the timer frequency so that benchmarks can measure the execution time better is not a great solution.

If it turns out that the benchmarks such as BenchmarkAppendGrowByte-2 are actually slower when the timer frequency is lower then I will be surprised and might actually investigate. Fixing slow benchmarks requires understanding why they are slow.

@gopherbot

This comment has been minimized.

gopherbot commented Mar 21, 2017

CL https://golang.org/cl/38403 mentions this issue.

@noblehng

This comment has been minimized.

noblehng commented Apr 1, 2017

Can the semasleep() be changed to adaptive to duration? If it is larger than the normal timer resolution, call WaitForSingleObject with the duration, otherwise call WaitForSingleObject with zero timeout (the waited event may also need to be changed to manual-reset event), and yield the thread and check for current time in a spin loop like what nanosleep() in linux does. This way we can have high resolution timer too.

To get high resolution time stamps in windows, QueryPerformanceCounter can be used, for newer version of windows there is GetSystemTimePreciseAsFileTime, it is like clock_gettime() in linux. Currently the Interrupt Time seems to be used in Go under windows, it is in assembly after this change e4371fb, so may be it can be changed to use high resolution time stamp like Go under linux does. Then time.Now() will also has high resolution under windows too. The other way is using rdtsc directly and synchronize it across cores.

@randomascii

This comment has been minimized.

randomascii commented Apr 3, 2017

Spinning for up to 15.6 ms (the default timer interval is 15.625 ms) seems like a bad idea. Most applications should not have a need to wake up on a ms or sub-ms precision timer. The few exceptions I can think of are VR and maybe some industrial control applications. Other applications should either be running fool speed (each task triggering the next) or being triggered by events such as disk I/O completions, network read completions, v-blank, etc.

tamird added a commit to tamird/cockroach that referenced this issue Apr 4, 2017

timeutil: add high precision clock on Windows
time.Now() has a precision of about 1ms on Windows[0], which is coarse
enough to fail some of our tests. This implementation has a precision
of about 10µs, which is fine enough to trip a hastily-written test in
storage; that test is also fixed here.

[0] golang/go#8687

tamird added a commit to tamird/cockroach that referenced this issue Apr 4, 2017

timeutil: add high precision clock on Windows
time.Now() has a precision of about 1ms on Windows[0], which is coarse
enough to fail some of our tests. This implementation has a precision
of about 10µs, which is fine enough to trip a hastily-written test in
storage; that test is also fixed here.

[0] golang/go#8687

tamird added a commit to tamird/cockroach that referenced this issue Apr 4, 2017

timeutil: add high precision clock on Windows
time.Now() has a precision of about 1ms on Windows[0], which is coarse
enough to fail some of our tests. This implementation has a precision
of about 10µs, which is fine enough to trip a hastily-written test in
storage; that test is also fixed here.

[0] golang/go#8687

Note that this implementation is significantly slower than time.Now:
```
name   old time/op  new time/op   delta
Now-2  14.0ns ± 3%  339.4ns ± 4%  +2332.32%  (p=0.000 n=9+9)
```
I suspect this is because of the explicit syscall required. time.Now
avoids the syscall by reading the system clock directly[1][2], so if
we need better performance, we can do something similar.

[1] golang/go@74b62b4
[2] https://github.com/golang/go/blob/go1.8/src/runtime/os_windows.go#L581:L624

tamird added a commit to tamird/cockroach that referenced this issue Apr 4, 2017

timeutil: add high precision clock on Windows
time.Now() has a precision of about 1ms on Windows[0], which is coarse
enough to fail some of our tests. This implementation has a precision
of about 10µs, which is fine enough to trip a hastily-written test in
storage; that test is also fixed here.

[0] golang/go#8687

Note that this implementation is significantly slower than time.Now:
```
name   old time/op  new time/op   delta
Now-2  14.0ns ± 3%  339.4ns ± 4%  +2332.32%  (p=0.000 n=9+9)
```
I suspect this is because of the explicit syscall required. time.Now
avoids the syscall by reading the system clock directly[1][2], so if
we need better performance, we can do something similar.

[1] golang/go@74b62b4
[2] https://github.com/golang/go/blob/go1.8/src/runtime/os_windows.go#L581:L624
@tamird

This comment has been minimized.

Contributor

tamird commented Apr 4, 2017

@noblehng unfortunately using GetSystemTimePreciseAsFileTime directly has a high performance penalty compared to the current hack of reading the time directly out of memory.

We needed this additional precision in CockroachDB, so we added a call to GetSystemTimePreciseAsFileTime in cockroachdb/cockroach@3fc5c81, but as the commit message says:

Note that this implementation is significantly slower than time.Now:

name   old time/op  new time/op   delta
Now-2  14.0ns ± 3%  339.4ns ± 4%  +2332.32%  (p=0.000 n=9+9)

I suspect this is because of the explicit syscall required. time.Now
avoids the syscall by reading the system clock directly[1][2], so if
we need better performance, we can do something similar.

I believe the implementation of GetSystemTimePreciseAsFileTime is similar to that of KeQuerySystemTimePrecise, which is (maybe) described in enough detail to permit an implementation in Go that avoids a syscall.

@noblehng

This comment has been minimized.

noblehng commented Apr 5, 2017

@randomascii As most applications will not need high resolution timer, they will not trigger the spinning code path. And for those applications that do need a high resolution timer, they can be supported under the same interface. And if we are spinning around a yield to the kernel scheduler, we are not burning cpu that much. I'm not sure the overhead of calling yield in Go in a loop (it will be better if WaitForSingleObject support high resolution and do this inside the kernel directly), but I assume it will be good enough for the current Go scheduler, it seems to need a resolution of tens to hundreds of microseconds. The Go scheduler can still use a hybrid approach that relax the resolution when idle.

@tamird If syscall is the bottleneck, it seems that it can also be derived from the _KUSER_SHARED_DATA struct, virtualbox dose this. One can find the struct definition in ntddk.h, or the offsets of each Qpc* corresponding fields of each version of windows can be found here. And RtlQueryPerformanceCounter is basically doing a (rdtsc + QpcBias) >> QpcShift with a fallback of a syscall back to kernel, there is a disassembly analysis of the win7 version here. There is also the overhead of some hypervisors trapping rdtsc.

@randomascii

This comment has been minimized.

randomascii commented Apr 5, 2017

If the spinning only happens for applications that need high resolution timers then that seems reasonable. How would applications indicate that they need a high resolution timer? Right now the Go runtime assumes that all applications need high resolution timers (hence the decision to raise the system-global timer interrupt frequency) and I'd hate to see that repeated with spinning.

@noblehng

This comment has been minimized.

noblehng commented Apr 6, 2017

@randomascii A Go user program using timers will use a Go timer (or maybe runtime.SetCPUProfileRate too), which is tickless and doesn't directly map to OS timer, a handler routine will sleep till next timer expiry or new timer be added, that sleep is using semasleep() so WaitForSingleObject under windows. So an application can indicate whether they need a high resolution timer by using different duration when construct the Go timer, which will translate to duration passing to semasleep(), then we can chose to do spinning or not dynamically based on the duration. It seems that dotnet use a similar approach here, it use a set of thresholds to do spinning, yield(SwitchToThread and Sleep(0)) and wait with the regular timer dynamically, those thresholds may also be used in Go. The benefit here is that we don't need to touch the systemwide interrupt tick timer and we will only spin several times when needed.

The Go runtime problem here is a different one. As far as I can see, the main problem currently is that the Go scheduler itself need a somewhat 'high' resolution timer in the monitor thread to retake processor(s) blocking on syscall (and probably cgocall too) to run other user codes, as #14790 and here. Other things the monitor thread does are already working with a low resolution tick of at least 10ms, and syscalls inside the runtime are already working with completions or annotated. Problem is user code can still call arbitrary blocking syscall. The monitor thread try to dectect that by using a period between 20µs to 10ms, which is 1ms to 10ms under windows currently. I suspect that small sleep there can be replace with a few yield to the OS using some thresholds similar to above, and sleep larger than 1ms can just be 15.6ms under windows. Because small sleeps will only happen when not idle, so those few spinning with yields will only happen when not idle. Further improves are also possible, like tracking the number of processors in syscall then only do spinning when that become positive, or we can make the monitor thread sleep even longer by only wake it up when there are runerable goroutines or gc jobs or io waiters.

Edit:
Now I think it is a bad idea to do spinning in semasleep(), as Go doesn't coalesce timers, so two timer could fire very closely, then we will spin too often. It is better leave the choice to user to change the systemwide interrupt timer or do spinning if they need sub-millisecond resolution under windows. Linux achieves this by dynamic reprogramming the clock event device for the next firing timer, that is local APIC timer on x86.

@aclements

This comment has been minimized.

Member

aclements commented Apr 21, 2017

I'm still looking for feedback on whether https://go-review.googlesource.com/c/38403/ is an acceptable fix for Go always running the timer at high resolution. The 1.9 freeze is May 1st, so if it's going to go in, it should go in before that.

@tamird

This comment has been minimized.

Contributor

tamird commented Apr 21, 2017

@aclements has any consideration been given to using https://godoc.org/golang.org/x/sys/windows#GetSystemTimePreciseAsFileTime when available? I believe that wouldn't require changing the timer resolution, but perhaps I'm missing something.

That function is available starting with Windows 8.

@aclements

This comment has been minimized.

Member

aclements commented Apr 21, 2017

@aclements has any consideration been given to using https://godoc.org/golang.org/x/sys/windows#GetSystemTimePreciseAsFileTime when available?

My understanding (please correct me if I'm wrong; I don't have a good understanding of the Windows APIs) is that GetSystemTimePreciseAsFileTime is relatively expensive. Also, the problem that requires high timer resolution (at least in the runtime) isn't getting the precise time stamps, it's sleeping for short durations, which is currently done using either NtWaitForSingleObject (runtime.usleep2) or WaitForSingleObject (runtime.semasleep). If I understand correctly, these are limited by the system timer resolution.

@randomascii

This comment has been minimized.

randomascii commented Apr 21, 2017

@aclements your understanding is correct. Sleep(), NtWaitForSingleObject(), etc. will return either when the condition is satisfied (handle signaled) or when the timeout expires. However timeout expiry can only happen when a timer interrupt happens so their resolution is affected by the timer interrupt frequency.

If the problem was a need to get precise times then it is unlikely that GetSystemTimePreciseAsFileTime would help. Most functions in Windows that get the time ultimately rely on timeGetTime() or GetTickCount() and those both ultimately read from a counter that is incremented by (you guessed it) the timer interrupt. GetTickCount() is designed (for compatibility reasons) to only change its results ~64 times a second, regardless of the timer interrupt frequency, whereas timeGetTime() reads a timer value (basically an OS global variable) that is incremented by the timer interrupt and therefore has precision somewhere between 15.6 ms and 1.0 ms. I documented some of this behavior here:

https://randomascii.wordpress.com/2013/05/09/timegettime-versus-gettickcount/

In short, hunting for different functions to get the time is unlikely to help because they are mostly all driven by the same underlying timer interrupt.

If you want high-precision timer information that is not dependent on the timer interrupt then you probably want QueryPerformanceCounter(), which typically has < 0.1 microsecond precision. However this is generally not as long-term stable as the timer interrupt - precision and accuracy are not the same thing - so be careful when using it.

Time is hard.

lian added a commit to lian/gdax-bookmap that referenced this issue Apr 22, 2017

fix windows db sync system clock resolution bug
  finially figured out why windows builds fail to read the graph from database.

  golang/go#8687

  workaround using the same hack we used for gdax timestamps not being
nano second resolution

gopherbot pushed a commit that referenced this issue Apr 29, 2017

runtime: reduce Windows timer resolution when idle
Currently Go sets the system-wide timer resolution to 1ms the whole
time it's running. This has negative affects on system performance and
power consumption. Unfortunately, simply reducing the timer resolution
to the default 15ms interferes with several sleeps in the runtime
itself, including sysmon's ability to interrupt goroutines.

This commit takes a hybrid approach: it only reduces the timer
resolution when the Go process is entirely idle. When the process is
idle, nothing needs a high resolution timer. When the process is
non-idle, it's already consuming CPU so it doesn't really matter if
the OS also takes timer interrupts more frequently.

Updates #8687.

Change-Id: I0652564b4a36d61a80e045040094a39c19da3b06
Reviewed-on: https://go-review.googlesource.com/38403
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
@prasannavl

This comment has been minimized.

prasannavl commented Aug 6, 2017

Linking in a relevant comment here:
https://go-review.googlesource.com/c/38403#message-96d2d5bb23216c271f8f8c7bd7d471e0ce999fae

Windows does use separates linked lists internally for it's active threads, making the scheduler quite efficient - but it does take a toll, when it's running with higher resolution for the kernel's book keeping. It's evident from Chrome, which does the same trick, while Edge does a 4ms resolution only when needed, making it over 30-40% more energy efficient as per Microsoft's claims. On the other hand Chrome simply raises the resolution to 1ms every time - even if you just move the mouse around.

I think the goal should be to remove the reliance on multimedia timers entirely. While this likely may not be possible until UMS itself is implemented, it can be made consistent with other Windows programs on how it uses time.

The multimedia timers are for programs to decide on the resolution so they can choose effectively, not for the language runtime itself to make a decision. In the Windows world - where you don't have a very reliable nanosleep - it's generally accepted that time resolution is 15ms (unless you're doing realtime/multimedia application, in which case the program explicitly asks for it). So, it's not generally a surprise to the programmers that this is as low as you go, and it's consistent with the behavior of the platform.

And it's really not hard for programs that want to support windows to manually do the resolution switch if at all they need. So, I think Go tried too hard, and is now breaking things by forcing every Go program to not be a good citizen on the platform.

@randomascii

This comment has been minimized.

randomascii commented Dec 12, 2017

Any progress on this? The previous comment did a good job of explaining why the Go runtime should not raise the timer frequency by default. It needs to be an opt-in option.

@mkdthanga

This comment has been minimized.

mkdthanga commented Apr 7, 2018

I'm using Windows 10, how can I see my System Timer interval? Is there any application for it?

@randomascii

This comment has been minimized.

randomascii commented Apr 7, 2018

The sysinternals' tool clockres is the easiest way to display the current timer interval, if you're comfortable with using the command prompt. Another alternative is this GUI tool:
https://github.com/tebjan/TimerTool

More resources are available here:
https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/

@randomascii

This comment has been minimized.

randomascii commented Jun 19, 2018

If you can open an administrator command prompt then you can run this command:

powercfg /energy /duration 5

This will create a file called energy-report.html. If you load that into a web browser and search for "Outstanding Timer Request" you can see who has raised the timer interrupt frequency. That is how I initially found that the Go runtime was doing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment