jeffr_tech ([info]jeffr_tech) wrote,
@ 2007-09-18 00:54:00
Previous Entry  Add to memories!  Tell a Friend  Next Entry
A number of people called bs on my last post, some publicly, some privately. So I thought I'd explain why this is possible and how the system really responds under load in my own tests.


ULE uses two basic metrics to determine how a thread is run. One is % cpu used over the last few seconds. The other metric is time voluntarily slept over time ran. This does not include time waiting for the run queue. This is the interactivity score. The interactivity score is actually annoyingly not a simple fraction because we can't do floating point math in the kernel. Both metrics use a gradual decay algorithm that incorporates past history to a lesser degree than current history.

A thread is considered interactive if it sleeps a sufficiently long time on average compared to the amount of time it runs. Interactive threads have priorities greater than non-interactive threads. They are just below real-time threads. The priority of the thread is basically the interactivity score scaled evenly across the range of possible interactive priorities. Interactive threads are always run in strict priority order and are selected before non-interactive threads.

Non-interactive threads get priorities according to the %cpu used, modified by nice. They are put onto a calender-queue like mechanism that ensures fairness as I have discussed before. They are run as their queue slot advances to the head of the queue.

So really, you could have 1,000 threads running and as long as your shell or mozilla or whatever was considered interactive, they'd have no latency. In fact, even as interactive tasks they may be alloted more CPU time than normal timeshare tasks who are competing fairly for the remainder of the cpu. This does open up the possibility for attacks that sleep for exactly the amount required to be considered interactive, however, since this queue is run in interact score order, shells and likely even X11 would not suffer as a result.

So you can see that the slashdot post is entirely feasible. On my single processor 1.6ghz PentiumM with 2gb of ram and a single laptop ide drive I can run 64 compiles and browse the web etc without any jitter. I can play movies from the dvd drive with no skipping or jitter. However, if I play movies off of the hard drive they compete for io ops with the compiles. In this case, there is certainly skipping.

FreeBSD is certainly not a desktop panacea where nothing ever skips and your old crappy hardware performs as if it's brand new. I don't claim that. I'm certainly able to reproduce skipping due to disk latency under high load. However, it is entirely feasible, and reproduced by many people, that ULE maintains excellent interactive performance in the face of many cpu hogs.



(8 comments) - (Post a new comment)

Fair vs unfair scheduling
(Anonymous)
2007-09-18 11:36 am UTC (link)
So ULE is an unfair scheduler that tries to guess which tasks are more interactive, right? Yet it behaves great under load (or it behaves so good *because* of its unfair nature, would be better to say).

But in Linux they are giving up the also unfair 0(1) scheduler because it behaved very poorly under load, and they're substituting it for a Completely Fair Scheduler (that behaves much better in such circumstances).

How can this apparent contradiction be explained?

(Reply to this) (Thread)

Re: Fair vs unfair scheduling
[info]dga
2007-09-18 03:08 pm UTC (link)
There's no contradiction. "Unfairness" does not equal "good interactive performance." By and large, many interactive-critical things (mouse movement, showing keystrokes, dealing with menus, etc.) are not CPU hogs. As a result, it's quite possible to provide an approximately "fair" CPU allocation (averaged over a reasonable amount of time) while still letting the interactive processes run quickly _when they need to run_.

In other words: your UI widgets don't want to run and chew CPU all of the time. They want to run, do their job quickly, and go back to sleep waiting for something else to happen. This was observed decades ago...

ULE improves on life in two ways: (1) It scales pretty well under large #s of processes, etc. (2) The interactivity score appears to be a better overall metric for identifying interactive processes than the old % of CPU-based metric that BSD used to use. There's nothing magic in here, except that Jeff figured out a nice, efficient way of maintaining the two queues and doing the scheduling without a lot of grunky overhead.

(Reply to this) (Parent)(Thread)

Re: Fair vs unfair scheduling
[info]jeffr_tech
2007-09-18 09:16 pm UTC (link)
About UI widgets.. Someone should tell mozilla they don't want to chew up CPU all of the time. :-)

It was a real challenge to get X11 and and media players etc to stay 'interactive' when they periodically chew up 100% cpu. The ULE interactivity history actually starts out small when it is inherited from its parent and grows to a couple of seconds worth of history so it can absorb periodic bursts.

(ps, hi dave!)

(Reply to this) (Parent)(Thread)

Re: Fair vs unfair scheduling
[info]dga
2007-09-19 01:08 am UTC (link)
Hi, Jeff. :)

And yes, someone should tell Mozilla. Of course, I solved the problem by switching to Safari... ahem.

I believe it about media players. It'd be nice if said programs were structured with an interactive UI thread, a CPU-hungry batch decoding thread, and a third lightweight "feed the decoded sound data to the sound device" thread. With a little help, they could be easy to schedule -- but, of course, they're probably not going to get built that way just for your convenience. :)

(Reply to this) (Parent)

Re: Fair vs unfair scheduling
[info]dga
2007-09-19 01:08 am UTC (link)
Oh, btw - thanks for providing a fun example to use when talking briefly about thread scheduling in class the other day. :)

(Reply to this) (Parent)

Re: Fair vs unfair scheduling
[info]jeffr_tech
2007-09-18 08:51 pm UTC (link)
Linux chose different interactivity metrics and still did time slice scaling and a couple of other things which I had abandoned. So you really can't compare one facet of the scheduler and ignore the rest as well. Actually, the whole design of concurrency and synchronization in the two kernels is moderately different and all of these decisions have an impact on latency as a whole.

(Reply to this) (Parent)


[info]loganb
2007-09-18 05:13 pm UTC (link)
To your point about the sleep attack (and with much hand-waving): Is there a reason you chose to have threads fall into buckets of "interactive" or "non-interactive"? Why not let priority be a continuous function of interactivity score? Since there's no threshold that instantly elevates a thread, it seems like it would be more robust to CPU stealing. It also would eliminate any meta-stable oscillations that come from threads bouncing in and out of "interactivity" (although that may not really be a problem).

(Reply to this) (Thread)


[info]jeffr_tech
2007-09-18 09:10 pm UTC (link)
Because good interactive performance under heavy load demands that interactive and non-interactive tasks are scheduled in different ways.

Since ULE 2.0 they are not even in the same queue. If they were, eventually non-interactive tasks would be scheduled before interactive ones. Rather than allow fairness between interactive and non-interactive, I learn quickly as threads abuse their interactive status and downgrade it.

All non-interactive timeshare threads are now in one queue with a rotating head pointer that ensures fairness. This queue is really an array of queues with one entry per-priority and a pointer to the current 'head' queue. Threads are inserted into the queue based on priority relative to the head. So a priority 0 will go in at the head, and 2, two slots after. The head pointer advances based on a timer so long as the queue is empty. Too much load will cause the advancement to slow which helps the system adapt under high loads.

This mechanism ensures that even nice +20 cpu hogs will eventually get some ticks. If interactive tasks weren't scheduled differently the nice +20 cpu hog would still be able to run before it, as eventually it'll be at the head position.

There is definitely a noticeable problem when a thread transitions as it now competes as a cpu hog. But basically this is how it would have behaved all along were there not a boost.

The timeshare algorithm approximates the behavior of the 4BSD scheduler which assigns time and runqueue order strictly based on cpu time. The only real difference between this and the CFS algorithm in effect is that CFS has fully sorted order and 4BSD and ULE's timeshare threads are only roughly sorted based on priority.

(Reply to this) (Parent)


(8 comments) - (Post a new comment)

Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…