New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 659324 link

Starred by 8 users

Issue metadata

Status: Assigned
Owner:
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

Chrome is not using "audio offload" on Windows 8.1+, leading to 2 kHz timer interrupt frequency

Project Member Reported by brucedaw...@chromium.org, Oct 25 2016

Issue description

Version: All versions
OS: Windows

TL;DR On many modern machines Chrome triggers power-wasting behavior in Windows when playing video.

What steps will reproduce the problem?
(1) Run Chrome on a SurfaceBook or one of many other notebooks
(2) Use clockres (https://technet.microsoft.com/en-us/sysinternals/bb897568.aspx) or TimerTool (https://github.com/tebjan/TimerTool) to verify the global system timer interval, which will probably be 1.0 ms, 4.0 ms, or 15.625, depending on what software is running.
(3) Play a youtube video. Note that (on the SurfaceBook for sure) the timer interval drops to 0.5 ms, representing a 2 kHz timer interrupt.
(4) Using the Services app to Stop and then Disable the Windows Audio service. Note that the timer interval returns to its previous level of 1.0 ms or higher.

So... Chrome seems to default to requesting AUDIO_PCM_LOW_LATENCY audio. This is designed for midi apps and perhaps games where ultra low latency is required. This mode is unnecessary for media playback where the most latency sensitive operation needed is stopping and starting of audio, where tens of ms of latency is acceptable.

This is a problem because (as shown above) requesting low-latency audio causes some audio drivers to raise the system timer resolution (using NtSetTimerResolution to get it to levels unobtainable with normal APIs like timeBeginPeriod) which can then affect power usage. Or at least, it is *presumed* that AUDIO_PCM_LOW_LATENCY is triggering the raised timer resolution - attempts to force it off have not correct the problem, so more investigation is needed.

The actual effect on power usage is fundamentally unpredictable as it depends on what other software is running on the system. For instance, on one laptop I owned there was DVD burning software that was constantly running, polling for work in a Sleep(1) loop. When the timer interval is lowered from 15.625 ms to 1.0 ms this software wakes up an extra 936 times per second, wasting CPU time and preventing the CPU from going to sleep.

<speculation>If multiple programs are using wait functions with timeouts (Sleep or WaitForSingleObject) then the 0.5 ms precision of the timer interrupt makes it less likely that these wakeups will be coalesced, spreading out CPU activity and making the idle gaps needed to get the CPU into a low-power state smaller.</speculation>

Some of this was discussed years ago in a tour-de-force expose blog post. On the one machine tested there was found to be a 0.3 W power cost from raising the timer frequency. Newer versions of Windows should be affected less, due to having a tickless kernel, but Microsoft still strongly recommends avoiding raising the timer frequency.

https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/


In order to have the best battery life possible Chrome should use DSound APIs or AudioGraph or other methods of playing audio that don't inadvertently trigger power-wasting behavior in the audio drivers that ship on Microsoft's flagship laptop. Further research is needed to understand exactly what triggers the raised timer frequency.

There is an API that controls how latency sensitive the WASAPI functions are, but this is, naturally, a UWP API and therefore inaccessible to normal Windows software:
https://msdn.microsoft.com/library/windows/apps/windows.media.audio.audiographsettings.quantumsizeselectionmode.aspx

Note: I would normally advise using "powercfg /energy /duration 5" to generate a report showing who has raised the timer frequency - that's how these investigations are supposed to go. However the audio driver raises the timer frequency in such a way that it is correctly attributed to Chrome, but no module stack is provide such that deriving the underlying cause required much guesswork.

 
Cc: olka@chromium.org tommi@chromium.org henrika@chromium.org
The latency provided at low latency isn't that low, only 10ms. Nominally we'd like to tell Windows to use a larger buffer size (and we do internally and on all other platforms use 20+ ms for media playback), but Windows is unique in that it will ignore the given size and continue to issue 10ms callbacks.

Is DirectSound even still a thing? I thought that died in 7 and is instead just WASAPI masquerading as DirectSound. AudioGraph is a means of construction WASAPI outputs I thought too, but wouldn't change the actual callback size. Even the API you link doesn't look like it actually changes the 10ms callback frequency. AFAIK, the only way to get a different frequency is with WASAPI exclusive mode.

My knowledge is arguable very rusty here, so +tommi,henrika who are the Windows experts here.
DirectSound may well be dead WASAPI may be inevitable.

I'm experimenting with sample code and reaching out to audio contacts to figure out what we have to do to avoid triggering the 0.5 ms timer interval, but if somebody else figures it out first I won't object.

It's possibly this code, https://cs.chromium.org/chromium/src/media/audio/win/audio_low_latency_output_win.cc?l=377

I forget the reasons behind adding that; possibly it was back for WebAudio live mode.
Here's another place to poke: https://cs.chromium.org/chromium/src/media/audio/win/audio_low_latency_output_win.cc?l=289

Dropping the REALTIME_AUDIO priority may help.
Thanks for the ideas. Unfortunately neither change helps - timer frequency is still raised to 2 kHz.

Still, lots of other options to follow-up on, including MSFT contacts.
As a whim, you might try --enable-exclusive-audio and --audio-buffer-size=<increasing multiples of 480> to see if there's any correlation to buffer size.
I have not worked on this code for a long time but when I first added it, the main goal was to reduce latency. Since then, others have done changes with a goal to reduce power consumption but it seems difficult to save anything on Windows due to it internal audio engine structure. AFAIK, using larger buffer sizes does not help.

Dale has already pointed at two (#3 and #4 above) possible things to try, and if removing them does not help, I can't come up with anything else to try. As mentioned in #6, exclusive mode can be tested but the code might not work perfectly since it has been behind a flag for a long time.

I don't know if it has any effect but today we use WASAPI in an event driven mode:

https://cs.chromium.org/chromium/src/media/audio/win/core_audio_util_win.cc?l=825

MSDN says: "After the stream starts, the audio engine will signal the event handle to notify the client each time a buffer becomes ready for the client to process. WASAPI supports event-driven buffering for both rendering and capture buffers. "

https://msdn.microsoft.com/en-us/library/windows/desktop/dd370791(v=vs.85).aspx

Perhaps (pure guess from my side), running in a "poll and sleep" mode instead (https://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx) could make a change. It is not a trivial change and it might be tricky to build
something that does not add audio glitches and keeps the latency low. Just an idea.

Comment 8 by mvanee...@gmail.com, Oct 26 2016

Hi, Windows Audio Quality guy here https://blogs.msdn.microsoft.com/matthew_van_eerde/

Poking around in the code a little, my suspicion is that the culprit is the timeBeginPeriod(1) call here

https://cs.chromium.org/chromium/src/third_party/webrtc/system_wrappers/source/clock.cc?q=timeBeginPeriod&sq=package:chromium&dr=C&l=167
I don't think that code is active in the normal video playback case, only when WebRTC sessions are active.
Also, that code does a timeEndPeriod at the end.

In my custom builds of Chrome I have removed all calls to timeBeginPeriod. Therefore, when I play a youtube video with the "Windows Audio" service disabled I get a system interrupt interval of 15.625 ms. When I re-enable the "Windows Audio" service while Chrome is playing audio the interval instantly jumps to 0.5 ms. This indicates to me that Chrome is not making any timeBeginPeriod calls.

Also, 0.5 ms is not a value that can be set with timeBeginPeriod - it requires a call to NtSetTimerResolution, which Chrome does not use.

Add to this the fact that powercfg doesn't show a module call stack for the raising of the interrupt frequency to 2 kHz (presumably because it happens in a driver) and it's clear that the audio system is raising the timer frequency. It's raising it on Chrome's behalf, and the question is just what Chrome is doing that is tickling this behavior.

I have recorded Chrome and Edge ETW traces on a Windows 10 SurfaceBook using these instructions:

https://blogs.msdn.microsoft.com/matthew_van_eerde/2015/08/11/taking-audio-glitch-traces-on-windows-10-desktop-edition/

I'm hopeful that the traces will reveal what is going on.

Comment 12 by henrika@webrtc.org, Oct 27 2016

Thanks for helping out mvaneerde@!

The code mentioned in #8 is AFAIK not used by WebRTC in Chrome today.
Cc: grunell@chromium.org
Summary: Chrome is not using "audio offload" on Windows 8.1+ (was: Chrome requests AUDIO_PCM_LOW_LATENCY when playing video)
Chrome on Windows 8.1 and above is using the low-latency/high-power audio subsystem, instead of the high-latency/low-power audio subsystem. This is because the low-power audio subsystem (aka "audio offload") is only available to store apps (UWP apps) - the original guess that AUDIO_PCM_LOW_LATENCY was the problem appears to be incorrect. It is being a Win32 app that is the problem.

Audio offload is discussed here:

https://blogs.msdn.microsoft.com/iliast/2015/02/18/windows-8-1-audio-streaming-part-2-power-savings-via-hw-offload/

The observed timer frequency raising is merely the most visible symptom of the issue. There is also additional CPU usage because the low-latency system which Chrome is forced to use does audio processing on the CPU, in audiodg.exe, instead of in hardware.

It is possible to force Edge to behave in the same way by turning off hardware acceleration in the SurfaceBook's audio driver. When this is done and a youtube video is played with Edge the timer frequency is raised to 2 kHz and the CPU usage of audiodg.exe goes from 0.0% to between 0.1 and 0.6%.

So, the inaccessibility of "audio offload" to Chrome is triggering increased CPU usage (in audiodg.exe) and an increased system timer frequency.
Components: Internals>Media>Audio
Cc: -dalecur...@chromium.org
Owner: dalecur...@chromium.org
Status: Assigned (was: Untriaged)
as per c#14, this seems a feature instead of bug. But leave to dale to confirm then close.
Owner: brucedaw...@chromium.org
Summary: Chrome is not using "audio offload" on Windows 8.1+, leading to 2 kHz timer interrupt frequency (was: Chrome is not using "audio offload" on Windows 8.1+)
After discussions with Microsoft I have confirmed that:

- Audio offload is not available to non-UWP apps, and it is not available on all hardware
- The raising of the timer frequency happens on Windows 10 anytime that audio is played without audio offload
- Microsoft believes that the raising of the timer frequency to 2 kHz will not harm power because timers will still be resolved at "just" a 1 kHz frequency, and because, hey, Chrome raises the frequency to 1 kHz all the time anyway. The second part of that is untrue, and also means that Microsoft is taking away our insensitive to avoid raising the timer frequency

Anyway. This should either be closed as ExternalDependency or assigned to me. I'm assigning it to me for now and I will continue working with Microsoft on the frequency-raising issue.
Ah, unfortunate to hear. I don't really understand why they need 2kHz timing at all since we're filling buffers in at 100Hz (10ms buffer sizes).
Agreed. And I've verified that audiodg.exe only wakes up every 10 ms also, so 2 kHz timer interrupts seem bizarre.

I've been told (lectured, practically) by Microsoft power experts about how audio shouldn't require raising the timer frequency because the audio hardware/drivers can just raise interrupts and thereby wake up the process whenever it is running low on audio data - sounds perfect. I don't know why they don't do that. I think it is because they are conflating low-latency audio (which might actually require high-frequency interrupts) with software audio.

Labels: Performance-Power
Still talking to Microsoft, slowly. Adding power label.
Don't see any MSDN traction on allowing offload, but IAudioClient3 seems to let us set a custom buffer size:

https://msdn.microsoft.com/en-us/library/windows/desktop/dn911487(v=vs.85).aspx

Any idea if this reduces the timer frequency? I can look into putting a patch together to test this.
I suspect that it won't affect the timer frequency, but I've been told that the timer frequency in this case is not really relevant because the CPU doesn't actually wake up on all the interrupts (hand wavy vague explanation plus acknowledgment of confusing tooling and messaging goes here).

That said, a larger buffer size could well help. I'm happy to test and see if it makes a difference to CPU usage or wake-up rates.

Okay, will try to put a patch together next week.
It appears that RS5 (Microsoft's not-yet-released spring 2018 update to Windows 10) changes the audio stack so that the timer frequency is not raised, or at least not raised to 2 kHz. Playing youtube video with Chrome used to raise the timer frequency to 2 kHz but now it is typically raised to 1 kHz (by Chrome) or to 250 Hz (by Chrome) depending on whether the laptop is plugged in or on battery power.

Additional testing could be done to see if the audio subsystem raises the timer interrupt frequency to 100 Hz, but until Chrome alters its task handling this is not particularly relevant.

Microsoft's claim was always that the 2 kHz timer interrupt frequency didn't affect power because of "reasons" so this may not actually make a power difference, but it at least makes it easier to tell what is going on.

The RS5 conclusion is based on testing on a Windows 10 insider build laptop. I have seen no official guidance about this change and I don't know when it actually happened.

Interesting, we'll see if our power tests end up showing a difference when this rolls out further.
For posterity: Today in a meeting with Microsoft folk, they said we should be able to use offload _without_ being a store app. Which is at odds with what we've been told previously.

I thought I even tried to do this and it didn't work, but will retry this week/next to see if I can invoke it on latest Win10.

They were skeptical that we'd see benefits at 20ms (which we have seen large power reductions on other platforms) versus a buffer size of say ~1s-2s (which is what they use in edge).
https://chromium-review.googlesource.com/c/chromium/src/+/1155964 should print if a system has offload capable audio or IAudioClient3 supports a > 10ms buffer size. 

None of my devices on my Z620 support offloading. Even if I force the driver for my Realtek device to be "High Definition Audio Device" which per discussion on issue 316908 is the only driver which supports a lower minimum period.

Bruce, can you test if that returns different information on your Surfacebook?
I get the impression that audio offload is only supported on laptops. I'll build with that patch and try a few laptops.
Thanks. Will dig out and charge a laptop to try Friday myself.

I updated the patch to try and set offload enabled regardless, on my machine I just get AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
Sorry for the delay but I just tried this on a SurfaceBook (Pro?). The output is here:

[6488:5444:0802/171303.526:ERROR:core_audio_util_win.cc(838)] GetSharedModeEnginePeriod() => pDefaultPeriodInFrames=480, pFundamentalPeriodInFrames=16, pMinPeriodInFrames=240, pMaxPeriodInFrames=480
[6488:5444:0802/171303.528:ERROR:core_audio_util_win.cc(847)] IsOffloadCapable() => pbOffloadCapable=0
[6488:5444:0802/171303.535:ERROR:core_audio_util_win.cc(853)] SetClientProperties() => hresult=0x-2004287454

That doesn't look too encouraging.

Yeah that's what's I got on my device: AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE, looks like I left the std::hex off the CL whoops.
Microsoft just confirmed that it is indeed still UWP only.

Sign in to add a comment