Re: Doubts about GPeriodic



If we say that "painting should have a higher priority than IO
completions and IPC" or "IO completions and IPC should have a higher
priority than painting, then we are talking a hard priority system. And
the fundamental rule of hard priority systems is that the stuff with
higher priority has to be well behaved. If PulseAudio uses real time
priorities to get itself scheduled ahead of everything else, then it
must make sure that it's not eating all the CPU.

Is painting well behaved? Inherently - no. We can easily get in
situations where we can spend all our time painting and no time doing
anything else. Once we add synchronization to a an external clock,
painting becomes *better behaved*. If we are able to paint at 80fps or
40fps, then that will be throttled to 60fps or 30fps and there will be
some time remaining. But we maybe we can inherently paint at 61fps? If
we make painting highest priority, we have to make provisions for other
stuff to progress.

Are IO completions and IPC well behaved? Well that's really up to the
application.... however, they have to be *somewhat* well behaved in any
case. If I have a GIO async callback that fills a treeview, there is one
pathology where my callback gets called so frequently that we never get
get to repaint. But what may happen instead is that I get so much data
in a *single* callback that I block the main loop for an unacceptably
long period of time. So we always will have the requirement that
callbacks from the main loop must be *individually* short. Making IO
completions and IPC highest priority makes this requirement a bit more
stringent - it means that callbacks from the main loop must be *in
aggregate* short. That callbacks from the mainloop aren't allowed to do
expensive stuff, but instead must queue it up for an idle at lower
priority.

While a two-part system like this sounds like a huge pain for
application writers - it does have the big advantage that everybody gets
a say. If we just cut things off after a fixed time and started
painting, then we could end up in a situation where we were just filling
the treeview and painting, and never processing D-Bus events at all. 

Well, sort of - the main loop algorithms are designed to protect against
this. *All* sources at the current priority are collected and dispatched
once before we check again for higher priority sources. But since
sources have different granularities and policies, the effect would be
some unpredictable. The behavior of the GDK event source is to unqueue
and dispatch one X event source per pass of the main loop, D-Bus and GIO
probably do different things.

Right now event compression in Clutter counts on events getting unqueued
from the X socket at the default priority and then stored in an internal
queue for compression and dispatching before painting. Going to a system
where painting was higher priority than normal stuff would actually
require 3 priorities: Event queueing, then painting, then everything
else. [*]

But can we say for sure that nothing coming in over D-Bus should be
treated like an event? Generally, anything where the bulk of the work is
compressible is better to handle before painting.

An example: If we have a change notification coming over D-Bus which is 
compressible - it's cheap other than a triggered repaint. Say updating
the text of label. And combine that with our GtkTreeView filler, then we
might have:

 Fill chunk of tree view
 Change notification
 Repaint tree view and label
 Fill chunk of tree view
 Change notification
 Repaint tree view and label
 Fill chunk of tree view
 Change notification
 Repaint tree view and label

Instead of:

 Queue stuff up for filling 
 Change notification
 Change notification
 Change notification
 Fill chunk of tree view
 Repaint tree view and label
 Fill chunk of tree view
 Repaint tree view
 Fill chunk of tree view
 Repaint tree view
 
On Thu, 2010-10-21 at 16:25 -0400, Havoc Pennington wrote:

[...]

> Re: frame-complete, it of course assumes working drivers... If you
> don't have the async frame completed signal you may be back to the 5ms
> thing, no? I guess with direct rendering you are just hosed in that
> case... with indirect you can use XCB to avoid blocking and then just
> dispatch for 5ms, which is what we do, but with direct rendering you
> might just have to block. Unless you're using fglrx which vsyncs but
> does not block to do so (at least with indirect, not totally sure on
> direct). Sigh. The driver workarounds rapidly proliferate. Maybe
> clutter team already debugged them all and the workarounds are in
> COGL. :-P

I think the basic assumption here is working drivers. If we know what we
want, define what we want precisely, implement what we want in the free
drivers, the proprietary drivers will eventually catch up. Of course,
for GTK+, it doesn't matter, since it's not directly vblank swapping.

[...]

> You were talking about handling incoming IPC at higher priority than
> repaint... it sort of depends on what the IPC is about. For example,
> we have some that is UI-related, similar to events, and other that is
> basically IO. If you have a flood of IO coming in (say downloading a
> big file) then I don't think it's acceptable to wait for that queue to
> drain before painting - it could be minutes, not seconds. If you think
> about something like the dbus main loop source, the dbus library
> doesn't know what the heck is going to be coming in, and you can't
> tune the main loop source depending on what kind of message it is.

Yep. But this cuts both ways - if we handle painting first and
everything else uniformly second, then we can't *up* the priority of
anything coming over D-Bus.

> Anything with a queue doesn't really have a bounded time within which
> its GSource won't be ready anymore. Threads only help if you can
> squish the queue in the thread... otherwise the unboundedness ends up
> in the main thread anyway. For example if you're reading a file, then
> if you can parse it and convert it to a small object in the thread,
> there's no potential paint starvation problem, but if you need to feed
> the whole unbounded dataset over into a TextView/TreeView, then there
> is (as you mention).

I'm not sure how well the model you are proposing here:

 - A single GSource dispatch cycle is a single queue element
 - Each queue element is individually short
 - All queue elements in aggregate may be long

Really corresponds to the reality of GSource usage.

> I feel like most stuff should be below paint priority, not above, and
> then each frame should have a window (either "while waiting for
> frame-completed" or "fixed time like 5ms" or whatever) in which things
> below paint priority are going to run. That way things more or less
> can't break, as long as each individual dispatch() is reasonably
> fast/bounded.

As from the above discussions, the requirements to make this work:

 - We can do a reasonable job scheduling painting versus everything
   else. (I think about the best you can do is to try and spend 50%
   of the time painting and 50% of the time doing everything else.
   Anything that's more sophisticated than that is tweaking around
   the edges.)

 - Each individual dispatch is "reasonable fast/bounded." (We can
   impose this by fiat, but is it easier for application authors
   than the two-part scheme of doing expensive stuff at "idle")

 - We never want to promote stuff *above* paint priority. 

> If most stuff is below paint priority (in order to ensure we keep up
> the frame rate), that could be implemented either by making most stuff
> an idle, or by making paint priority above default.
> 
> "Most stuff should be an idle" is weird to me - seems to make default
> priority kind of meaningless and render g_*_add() etc. APIs useless.
>
> Why not make paint priority greater than the default priority, and so
> most things should be default, and idle is reserved for things that
> it's acceptable to starve? 

I guess the basic question here is whether "most stuff" is updating the
text of a label or whether most stuff is adding 10000 elements to a
GtkTreeView. If most things that applications are doing in response to
timeouts/IO completions/IPC are cheap and take almost no time, then we
gain compression and efficiency by doing them before painting and we can
ask the applications to code specially for the rest.

If everything an application does is a potential latency trap, then that
gets really awkward and maybe it's better to just have applications
write things at a single priority and accept that filling a GtkTreeView
from the network can cause everything other than painting to slow to a
trickle. (One elephant, one orange, one elephant, one orange. The great
thing about preemption and threads is that you can take your elephant
in orange-sized pieces.)

- Owen

Note: All sorts of hybrids are possible here - we could have the D-Bus
socket and IO handled below paint priority but still offer a mechanism
to defer known expensive stuff to a lower priority. We could have the
D-Bus socket and IO handled below paint priority in the normal case but
"lose patience" and paint a frame if things blocked for longer than a
minimum frame rate.

Note: I've talked about "idles" above for deferring to the lower
priority, but something more sophisticated might be useful - anything
deferred should be making *some* progress - it shouldn't be completely
starved. It might also be useful to be able to do 
'while (!my_deferred_work_function_should_yield()) 
{ add row to tree view }.'

[*] An alternative is to have Clutter pull events from the X socket
actively before painting. Conceptually fine, but bad consequences for
the GTK+/Metacity/Clutter event juggling in Mutter, so I don't like it
much... :-)




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]