realtime: wake the interpreter from a dedicated timer thread - #218
realtime: wake the interpreter from a dedicated timer thread#218probonopd wants to merge 5 commits into
Conversation
|
Today I learned about I think the
So, another commit in the early category that should be added is something like this: The point of the early commits is to make the large commit more focused on the main feature being implemented. In In
I don't know for sure that |
3f5218d to
44fc709
Compare
|
Thanks for the detailed review! I've restructured the history and addressed all four points. 1. Split into smaller commits I rewrote the branch into focused commits:
2. You're right. 3. It's kept. It is constant during execution (written only at CPU init and on the 4. Restored - the removal was unrelated to this PR, and |
|
Very nice. For changes that affect the cpu execution, some before and after benchmarks would help characterize the changes as benign or not. There's the I made a benchmark that uses the time of day since that is mostly accurate in Mac OS 9 or earlier on DingusPPC regardless of mode. We might want a method to obtain host nanoseconds in the guest environment by creating an unused PPC special purpose register. Or by adding a special register in the emulated mac-io chip. Or by utilizing special guest CPU instructions that are normally illegal ops. That's something to think about for another time. A third possible benchmark is to measure boot time. Choose a bootable disk image that is easily obtainable such as one from the As for these commits, I would add them in this order (earliest to latest):
Maybe the last two should be combined. Or maybe not.The switch to using a dedicated timer thread for the realtime mode's event process triggering is a big change by itself. Did that dedicated timer thread change necessitate the other changes? Or were the other changes made as corrections/improvements to the previous commit? If the latter then perhaps those changes should be applied to the previous commit. |
I came across that topic and was wondering whether https://github.com/dingusdev/dingusppc/ could/should do the same. |
My fork has these commits (from latest to earliest):
Looks like I misspelled infinite-mac also uses |
|
The main issue I had with the MetaImgFile stuff was that it uses a header file from Apple directly. As it had no explicit source license, I opted to not include it. An open-source replacement would be accepted. |
What about versions from Darwin which have the APSL licence? I've made changes to the |
|
I think those versions would be covered by Apple's terms under "Larger Works", in which case it would be acceptable. We will also need to include a copy of the APSL license. |
exec_timer is written by force_cycle_counter_reload (called from the audio thread's DMA channel when it adds an immediate timer) and read by the emulation thread, and g_realtime / g_nanoseconds_base / g_idle_cpu_save are touched from more than one thread too, so make them all std::atomic. No behavior change; the upcoming dedicated realtime timer thread will write exec_timer from a third thread.
Peek at the next timer's expiry (in guest time) without firing it, returning 0 when no timer is pending. Pure addition, no behavior change; the realtime timer thread will use it to sleep until the next deadline.
Enable g_realtime mode from the command line instead of only via the Control-Alt-R shortcut.
In realtime mode the guest never halts (there is no PPC equivalent of the x86 HLT instruction), so at the desktop it keeps spinning in its idle path, burning a whole host core. Detect a settled idle state via a low-pass-filtered rate of guest memory-mapped I/O: boot and real work touch devices at hundreds of thousands of accesses per second, a settled idle desktop at a few thousand. Once the filtered rate has stayed low continuously for IDLE_CONFIRM_NS, sleep the guest for most of each 16 ms VBL period and run a 6 ms servicing burst so interrupt handling still completes. The MMIO rate during interaction also stays below IDLE_CONFIRM_RATE, so the host event poller marks input (mark_host_input) and guest_is_idle refuses to sleep shortly after an input event, keeping the guest responsive while the user interacts. The confirm/uptime gates are kept short (6 s / 8 s) so the throttle engages soon after the guest settles. The feature is opt-in via --idle-cpu-save so that default behavior is unchanged, and both realtime and non-realtime modes maintain the guest MMIO access counter.
In realtime mode guest time is the wall clock, so a timer's guest-time deadline is a fixed wall-clock instant. Instead of making the interpreter loop chase those deadlines through its instruction-count budget, a dedicated thread sleeps until the next deadline and then raises exec_timer, so the interpreter only wakes to process due timers. While the idle throttle is in its sleep/burst cycle it fires the due timers itself (the sleep is bounded by the next timer deadline, the burst by its budget), so while the throttle is active (g_idle_throttle_active) the timer thread polls no faster than the throttle's sleep cap instead of racing the idle decision, which would cut a servicing burst short or force full-speed slices.
44fc709 to
f0eb334
Compare
|
Reordered the history to your suggested order (earliest to latest) and folded the corrections into the throttle commit:
To answer your question: the dedicated timer thread change does necessitate two of the others. It is what makes the atomic conversion of I also corrected the wake commit message: the previous wording claimed a per-instruction realtime deadline check, which the final diff does not have - it is only the loop-condition reorder plus the dedicated thread. I kept the last two commits separate rather than combining them, since the throttle commit now stands alone and the timer thread is the orthogonal change. Regarding benchmarks: I have not added before/after numbers for |
|
I have added your commits (as they currently exist) into my fork just before @mihaip 's commits). I have these issues/questions (no testing has been done by me yet):
About this change in Doesn't the short-circuit evaluation of the The description in the comment is missing some details (I'm probably missing something)
You accidentally added a blank link here:
Why does this line use Maybe some of the last commits in my fork addresses this issue and some others? These are listed from earliest to latest (the relevant ones are marked with a bullet •):
These are untested.
What is DP3 in this comment? Mac OS X Developer Preview 3 ?
Unnecessary spaces. Spaces for alignment should only be used for lists and tables and similar/related statements.
These changes should maybe be grouped together? I understand they are defined in different source files but they are used for implementing a single feature. |
|
Alternative to #216 with the same goal (drop the idle desktop to ~3% host CPU in realtime mode), but instead of polling the host clock in the interpreter loop it uses a dedicated timer thread that sleeps until the next guest timer deadline and then raises exec_timer, waking the interpreter only to process due timers.
Additional differences from #216:
mark_host_inputandguest_is_idlerefuses to sleep right after an input event, so moving the mouse or typing never feels sluggish while throttled.g_idle_throttle_active, so the timer thread cannot cut a servicing burst short or race the idle decision (which caused full-speed slices and CPU flapping).exec_timer,g_realtime,g_nanoseconds_base,g_idle_cpu_save) are now atomic since they are touched by both the emulation thread and the timer thread.Tested end to end on the Power Macintosh G3 machine: normal boot to the desktop at full CPU, settled idle desktop at ~3% host CPU, and full-speed responsiveness while interacting.