realtime: throttle the idle guest to cut host CPU usage - #216
Draft
probonopd wants to merge 2 commits into
Draft
Conversation
In realtime mode the guest never halts, so at the desktop it spins in its idle path and burns a whole host core. Detect a settled idle state by low-pass filtering the guest's memory-mapped I/O rate (boot and real work touch devices at hundreds of thousands of accesses per second, a settled idle desktop at a few thousand), and once the filtered rate has stayed low for 60 s continuously, sleep the guest for most of each 16 ms VBL period and run a 6 ms servicing burst so its interrupt handling still completes. Input activity raises the rate again, which disengages the throttle within one window. Adds a --realtime flag and a guest MMIO access counter.
probonopd
marked this pull request as draft
August 17, 2026 06:06
Raise the idle threshold from 10k to 30k MMIO accesses per second so the settled desktop (4.5-9k/s, up to ~14k/s filtered) no longer sits right at the edge where its periodic activity disengages the throttle and sends the CPU back to 99%. Once engaged, the throttle now stays on through brief activity - the guest services input and momentary bursts during its run slices - and only yields after the rate has stayed high for a full second, resuming immediately once the rate drops. Cut the first confirmation from 60s to 15s so the throttle engages sooner, gate engagement on at least 15s of uptime, and reset all detector state on every boot so a guest restart is never throttled during its boot phase.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In realtime mode the guest never halts, because the PowerPC architecture has no equivalent of the x86 HLT instruction, so once a Mac OS X Developer Preview 3 guest reaches the desktop it keeps executing its idle path forever and burns an entire host core at about 94 to 99 percent CPU just sitting there. This change teaches the emulator to notice when the guest has settled into that idle spin and then to stop executing it for most of each VBL period, dropping the host CPU usage to roughly 5 to 10 percent while the guest remains fully responsive.
The challenge is distinguishing a settled idle desktop from a guest that is merely quiet for a moment while it is still busy booting or doing real work. Stalling guest execution while wall time, and therefore every time-based device, timer and driver timeout, keeps advancing is fatal if the guest is still in the middle of something important, and in testing any throttling during the boot phase derails the guest into a machine-check storm at the external-interrupt vector. The signal we settled on is the guest's memory-mapped I/O rate. Boot and real work touch devices at hundreds of thousands of accesses per second, while a settled idle desktop touches them at a steady few thousand per second. The rate is low-pass filtered so that a short burst of accesses an interrupt handler performs in a few microseconds cannot look like activity, and the throttle engages only once the filtered rate has stayed below ten thousand accesses per second continuously for sixty seconds, a duration that no boot phase, however long, can satisfy. A shorter ten second confirmation suffices after any interruption once the guest has provably reached its idle state before.
The filter is an exponential-decay accumulator on the access count, which adds each window's accesses and decays the running total by exp minus elapsed over the time constant, and this converges to rate times the time constant regardless of how often it is called.
Once engaged, the throttle sleeps until the next scheduled timer, capped at sixteen milliseconds, and then runs a short servicing burst so the guest can take its pending interrupts and poll its devices. The burst length proved important. Bursts of one to four milliseconds leave the guest unable to complete its interrupt servicing in the time it gets, so it ends up stuck in the machine-check storm at the external-interrupt vector even at the desktop, while a six millisecond burst per sixteen millisecond window keeps the guest healthy indefinitely. With that burst length the host CPU usage at an idle desktop drops from roughly 94 percent to about seven percent.
The throttle is also self-disengaging. Any interaction that raises the guest's memory-mapped I/O rate above the ten thousand per second threshold resets the low-rate timer immediately, so moving the mouse or typing runs the guest at full speed within a single filter window and the CPU only drops again after another ten seconds of confirmed idling. Keyboard and mouse input during throttled periods is received with at most one sleep interval of latency, which is imperceptible.
The change adds a command line flag to start the emulator in realtime mode, where guest time follows the wall clock, and a counter of guest memory-mapped device accesses that is incremented by the MMU read and write paths and read by the idle detector in the CPU execution loop. It was tested end to end with various boot CDs and the on the Power Macintosh G3 machine, confirming a normal boot to the desktop at full CPU, a settled idle desktop at about 3 percent CPU, continued guest stability for well over 8 minutes of sustained throttling, and full-speed responsiveness when the mouse is moved.