[Rpcemu] Improving CPU usage when idle

Theo Markettos rpcemu at markettos.org.uk
Mon Apr 6 08:24:49 EDT 2009


On Sun, Apr 05, 2009 at 07:23:43PM +0100, Jake Waskett wrote:
> What we *ought* to do is to record (and queue) the event whenever
> we're notified that the button state changes.  We can then pass these
> state changes to RISC OS through simulated interrupts.  Fortunately
> the exact timing of the events doesn't matter too much, as long as the
> relative timing is preserved.  This shouldn't be too hard to do if we
> tag each queue entry with a timestamp.  Most GUIs give us these events
> (as well as keyboard events, which should be dealt with in a similar
> way) through their event queue -- X11 and Win32 both work this way --
> but Allegro is hiding this behind a sub-optimal abstraction which can
> lose information.  It's possible that we can make use of Allegro's
> mouse_callback (and keyboard_callback, I guess), however.

So I wonder if it would be feasible to have a separate user input and timer
thread?  This queues up mouse and keyboard events, and runs the timers. 
Each mouse/key event is supplied with a timestamp, which is delivered to the
emulator as an interrupt.

We have to be a bit careful, because in the real hardware there's no
timestamp supplied with events: the OS just reads the central timer sometime
after the interrupt is received.  So presumably we can cause the timer to
jump forward to the time of the click, but we have to keep on incrementing
it afterwards.  This means that things that check whether a long enough
period has elapsed between events (eg key auto-repeat) may fail if the timer
keeps getting incremented by the thread but the interrupt service routine on
the emulator hasn't got run because the machine is so busy that the CPU
thread hasn't got run yet.

> First, create a fairly simple thread-safe queue implementation.  Each
> entry needs to contain: an event type (initially mousebuttonup or
> mousebuttondown), some associated data (eventually keycode, mouse
> position, etc), and a timestamp.
> 
> Second, install a callback for mouse (and eventually keyboard).  These
> simply create an event and append them to our queue.
> 
> Third, modify pollmouse() to process the queue instead of polling the
> mouse directly.  (It'll also need to take account of the timestamps.)
> 
> Finally, the semantics of nanosleep() should be changed to something
> like:
> 
> * if our event queue is not empty, do nothing.
> * else, sleep until an event is posted to the queue, or a timeout
>   occurs (simplification: treat a timeout as a special kind of queued
>   event).  (This requires a small amount of platform-specific thread
>   sync code, unfortunately.)
> * as long as callbacks are invoked from a separate thread (as I think
>   they must be), then this should work.

That's a similar idea... but better thought out!  Looks feasible.

What do emulators of other hardware do regarding UI timing?  It must be a
common problem.

> It's not all that great: sleeping for a microsecond doesn't give the
> host's kernel much of a chance to do anything.  It can switch to
> another process, but it's too short an interval for it to be worth
> entering a power-saving state.

True, I hadn't thought about putting the CPU into idle, I was more assuming that
if the CPU load was reduced the (Centrino/whatever) CPU clock would be
reduced.  The system load would increase, but it would still get the jobs
done under the lower clock.

> It's not *exactly* what the Sleep program does.  The Sleep program polls
> the Wimp for messages, and invokes the NANOSLEEP SWI whenever the
> reason code is 0 (idle).  But I think this is a bit too aggressive,
> and I would expect it to interfere with the performance of other tasks
> (eg taskwindows) that use idle to perform background processing.  What
> we really want to know is if a) the Wimp is idle, *and* b) no other
> tasks are performing idle processing.  Most tasks will mask out reason
> code 0 if they don't intend to use it, so the Wimp should be able to
> detect this state.  Whether this helps us is of course another matter!

That's true.  Probably a Wimp filter would return you that information...
(Pity we can't access the Wimp's idle loop directly)

> The simplest method for guessing the system load is to measure elapsed
> time between Wimp_Polls that return reason code 0.  If the period is
> very small, then it's likely that not much else is happening in the
> system.  So, for example, if Wimp_Poll returns reason 0 ten times in a
> row, and each time is less than a millisecond after the previous
> event, we can probably assume that it's safe to nanosleep().  These
> figures are obviously guesses, and better values could probably be
> obtained through testing.

Sounds plausible.

> Now here's an interesting thing.  According to the documentation for
> the "Portable" module, there is a "Portable_Idle" SWI (0x42fc6), which
> is defined to basically stop the CPU until something interesting
> occurs.  In other words, exactly the same semantics as a select() on
> an XLib socket (or MsgWaitForMultipleObjects in Win32).
>  
> http://select.riscos.com/prm/hardware/portable.html
> 
[snip]
> Ah, looking at the ROOL Wimp sources, it seems that Portable_Idle is
> only called in one place, in Wimp03, in a conditional assembly block
> for Stork.  That's a shame.

I think it may exist in A9home, so might be reintroduced in later ROL OSs. 
Anyone with RO6 feel like grepping their Wimp module to see which Portable_
SWIs it calls?

> Portable_Speed, however, is certainly being called in 4.02, and would
> be perfect for gauging the system load.

It's only a binary switch though, but if the Wimp modulates it fast enough
it may be acceptable (eg once/second not once/minute).

> A possible approach, then:
> 
> * Implement (as a minimum) mouse event callbacks & queueing, as
>   described above, to avoid lost clicks & to ensure accurate timing
>   for double-clicks, etc.  This ought to make the BASIC "Sleep"
>   program usable by itself, but to get really good performance...
> 
> * Implement an RpcEmu-specific version of the "Portable" module (or
>   alternatively implement the SWIs in the emulator), the main purpose
>   of which is to capture the speed changes, from which we can
>   accurately infer system load.  Question: is there a way to ensure
>   that this is loaded before the Wimp initialises?
> 
> * Implement a trivial app (or module task, perhaps) that uses Wimp
>   Poll info, as well as the current 'speed', to selectively invoke
>   NANOSLEEP when CPU load is low enough.

Sounds good to me.  RISC OS code can be inserted in the emulator by building
it as a module and putting it into the 'poduleroms' directory.  The emulator
packs it all up into a ROM image which it inserts as an emulated podule. 
That code gets linked into the module chain quite early on in the RISC OS
boot sequence.

Another use for the Portable module is to tell the emulator about the host's
battery state.  The RO6 PRM describes how VRPC uses this - not much
different to what the A4 and A9home do.  Is there a cross-platform way for
the emulator to read the power management state?

Theo



More information about the Rpcemu mailing list