|
I think I may have accidentally fixed the intermittent high CPU usage in WorkShelf under Windows 11, even when it was idle. Finally!
This has actually been a months-long search.
On my Windows 11 system with a 12900K processor, WorkShelf would normally sit at around 0.3% CPU or less, but from time to time it would suddenly climb to roughly 1.7% and remain there for a long time.
That may not sound like much, but it represents approximately 25% of one logical processor running at 5 GHz, which is far too much for an application doing absolutely nothing. Restarting WorkShelf did not fix the issue either.
Because the problem only seemed to happen under Windows 11, and never under Windows 10 or earlier, my initial suspicion was that it might have something to do with the way Windows 11 schedules work between the Performance and Efficiency cores of the 12900K.
Perhaps WorkShelf was occasionally being moved to an E-core and therefore appearing to use more CPU while performing exactly the same amount of work. The fact that the CPU usage could suddenly appear or disappear without any obvious change also seemed to support that possibility (I eventually ruled that theory out by forcing WorkShelf to run only on the 12900K’s P-cores through processor affinity, excluding the E-cores).
That sent the investigation in several wrong directions and made the true origin of the activity extremely difficult to identify.
Over the following months, I repeatedly combed through several areas of the WorkShelf code that seemed like possible culprits. Timers, system monitoring, animations, window handling, modules and other background operations were examined over and over again.
Along the way, I made many of those areas as lean and fast as possible, which was useful in itself, but none of those changes actually eliminated the mysterious CPU usage.
What made the problem particularly difficult to track down was that closing every module, dock and shelf did not make the CPU usage go away. WorkShelf releases resources when nothing is using them, so there was no obvious feature left running that could explain the load.
The culprit now appears to have been related to the system tray code. This also explains why the high CPU usage only happened under Windows 11.
Under Windows 10, Winstep applications can still access and display the real system tray icons. Windows 11 no longer allows this, so WorkShelf must instead use "cloned" tray icons for battery, network and volume, which mimic the behavior of the genuine Windows icons.
To display the same information as the real icons, WorkShelf has to retrieve that information itself. This means checking the available network adapters to update the network icon, and enumerating the audio devices to determine the current playback device, volume level and mute state.
Retrieving the battery state, on the other hand, is quick and easy, requiring only a single Windows API call.
Until now, this information was obtained by polling Windows once per second for as long as the tray was visible.
Polling is never as elegant or efficient as having Windows notify WorkShelf whenever something changes, but given the complexity and risks involved in working with COM objects and callbacks, it is much simpler and safer to implement. Notification callbacks are also not always available, or may not even exist for the particular information being monitored.
I had already improved the network tray icon code by polling it only once every 10 seconds, unless a change in IP address was detected (checking the IP address is quick and inexpensive) but I had otherwise left the rest of the tray polling code alone.
While monitoring WorkShelf with Process Explorer for an unrelated reason, I noticed that audio endpoint enumeration kept appearing once every second.
This finally pushed me to replace the audio polling code with Core Audio device-change notifications. If I could cache the relevant audio objects, there would be no need to enumerate them again until Windows reported an actual change, leaving Process Explorer with almost nothing to show during normal idle operation - always a good thing.
However, before doing that, and while looking through the tray code again, I noticed a potential bug in the tray timer usage counter, which determines whether the timer should continue running or be stopped.
The bug meant that the tray timer continued firing once per second after the last tray object had been closed, repeatedly triggering audio device enumeration even though nothing was using the tray anymore.
This explains why I could never find the culprit by closing every object one by one - the real culprit continued running in the background!
The timer itself was not particularly expensive, but every time it fired it caused WorkShelf to enumerate the Windows audio devices in order to update the cloned volume tray icon even when this was not being shown anywhere.
And THIS appears to have been what caused the high CPU usage.
NextSTART too was probably affected by the same repeated audio device enumeration, it simply uses so little CPU when idle in comparison to WorkShelf (which is constantly monitoring modules, updating information and performing other background housekeeping) that the additional load never became obvious.
It also explains why the problem was so difficult to catch in the debugger. The CPU usage was not being caused by one long-running section of WorkShelf code. Instead, it consisted of many short bursts of work inside Windows and the audio drivers.
By the time I interrupted execution, the actual audio enumeration had usually already finished, so the debugger would stop somewhere harmless-looking rather than inside the code responsible for the load.
It was only some time after fixing the timer bug and implementing the Core Audio callback that I noticed WorkShelf was no longer occasionally peaking at 1.7% CPU usage while idle.
I may still be speaking too soon, but in several days since these changes were made I have not seen it get anywhere near 1% (it stays well below 0.3%).
After months of searching through all the places that seemed most likely, it would be rather fitting if the actual solution came from accidentally stumbling across the problem while working on something else.
_________________ Jorge Coelho Winstep Xtreme - Xtreme Power! https://www.winstep.net - Winstep Software Technologies
|