Thread-based or event-based?
Kevents have finally become part of the discussion, however, resulting in an interesting exchange between kevent hacker Evgeniy Polyakov, threadlet (and everything else) hacker Ingo Molnar, and several others as well. Benchmarks have been thrown around to illustrate the performance characteristics of both approaches, but the real question is this: what is the best way to allow user-space applications to juggle multiple simultaneous operations in a scalable manner?
Evgeniy's core claim appears to be that an event-oriented approach is inherently more scalable than using threads. He says:
In other words, using threads for event management is simply too slow. David Miller has also argued that threads are inherently wrong for network-oriented tasks. One of the big advantages behind the threadlet approach is that it is very fast in the non-blocking case, which is expected to be the situation much of the time. In networking, however, one normally expects to block. As a result, a highly multi-threaded networking application could create massive numbers of threads in short order. Networking is inherently an event-oriented activity.
Ingo challenges the notion that using threads and the scheduler will be slower than maintaining lists of jobs which turn into events:
Now look at kevents as the queueing model. It does not queue 'tasks', it lets user-space queue requests in essence, in various states. But it's still the same conceptual thing: a memory buffer with some state associated to it. Yes, it has no legacies, it has no priorities and other queueing concepts attached to it ... yet. If kevents got mainstream, it would get the same kind of pressure to grow 'more advanced' event queueing and event scheduling capabilities. Prioritization would be needed, etc.
The point here is that the scheduler has been brutally optimized over the course of many years. The actual overhead of switching contexts is quite small - perhaps less than that of a system call to manage events. The only real difference is that the memory overhead of maintaining threads is quite a bit higher than the overhead of kevents. But, says Ingo, with proper programming that should not be an insurmountable problem.
The real issue, though, tends to be one of ease of programming - on both the kernel and the user sides. In user space, the classic pattern for an event-based application involves a central loop which only blocks when it is waiting for events. Any actual work done within the loop must happen in a non-blocking manner; should the loop block, events will pile up while the application is doing nothing. Blocking in the wrong place can kill performance. But avoiding blocking in all situations is tricky at best, and sometimes impossible. The threadlet model lets the application developer stop worrying about blocking; if an operation blocks, the application simply continues to run in a newly-created thread.
More generally, programs written as state machines - the style necessitated by event-driven models - tend to be hard for people to understand. And there are a number of kernel operations (opening a file, for example) which can block in any of a number of places, and which are just about impossible to code in a state-machine style. Multi-threaded programs present their own challenges for developers who are not prepared to think about concurrency issues, but they still tend to be easier for most to understand. Threadlets, by making any sequence of calls easily implementable in a threaded model, should be relatively easy to program. At least, that's how the argument goes.
That argument applies to kernel space as well. The struggle to bring event-based asynchronous I/O to Linux has occupied a number of highly-capable kernel developers for years - and the job is still far from complete. It requires the addition of an entirely new infrastructure and the application of state-machine techniques to inherently sequential series of events. The complexity of the retry-based asynchronous buffered file I/O patch set is a case in point: this code has seen work (on and off) for years, and it still hasn't found its way into the mainline. It still depends on worker threads for some of its operation as well. Threadlets, it is argued, allow for any system call to be invoked asynchronously, with almost no added complexity or overhead at all.
Eventually the discussion reached a point where Linus jumped in to express a bit of frustration. His position is that it's not a matter of choosing between event-based and thread-based mechanisms, since there is a place for both:
In this view, it's not a matter of picking one or the other, but providing
both so that the right tool can be used for each job.  It seems likely that
this opinion is fairly widespread, meaning that some sort of thread-based
asynchronous mechanism will probably find its way into the mainline before
too long.  Event-based interfaces will continue to be supported as well; the big
question there is whether the existing interfaces (epoll in particular) are
sufficient, or whether the addition of kevents is called for.
      Posted Mar 1, 2007 9:29 UTC (Thu)
                               by wahern (subscriber, #37304)
                              [Link] (1 responses)
       
With a state machine, typically done w/ a switch statement and/or function table, you can very clearly see all the possible points of execution in a small amount of code. In terms of maintainability it cannot be beat, at least not in the imperative and OOP world. 
OTOH, state machines require careful thought at the outset. And sometimes you connect state machines which regular procedural flow (i.e. one state machine in a stream filter, effectively embedded in a large state machine doing something more application oriented). 
I use state machines and event-oriented programming mostly. But I also have a threaded task library (basically a thread pool which dispatches from and returns to the main thread using callbacks) which I use so I can "plug-in" certain blocking tasks into a state machine. And of course I often have multiple state machines arranged like mentioned previously. 
As to whether we need kevent? Of course we do. epoll() is a hack, like dnotify was in relation to inotify; in this case too narrowly concerned w/ sockets. Actually, we really need kqueue, and maybe that's exactly what Igno doesn't want to see. Which is a shame, because the kqueue interface very nicely unifies the hodge-podge components of the Unix resource and process model--hodge-podge, yes, but not necessarily ill-gotten, and entirely redeemed w/ kqueue. 
 
      
           
     
    
      Posted Mar 1, 2007 20:03 UTC (Thu)
                               by oak (guest, #2786)
                              [Link] 
       
     
      Posted Mar 1, 2007 15:16 UTC (Thu)
                               by zooko (guest, #2589)
                              [Link] (8 responses)
       
One notable success in that area is BitTorrent.  You might be interested in Mark Miller's dissertation, http://erights.org/talks/thesis/index.html, which gives a principled account of why event-based programming can be strictly safer and strictly more secure than other concurrency paradigms. 
You might also be interested in the latest salvo in an academic contest to write the fastest web server.  The current winner, according to this paper, is event-based http://bcr2.uwaterloo.ca/~brecht/papers/getpaper.php?file... also the Twisted Python project http://twistedmatrix.com. 
If the kernel hackers could make user-space code like Twisted Python faster or simpler (at the level that it interfaces with the system, I mean), then I would be pleased.  Likewise if the kernel could facilitate user space code such as Twisted to use asynchronous file I/O then that would be nice. 
     
    
      Posted Mar 1, 2007 18:22 UTC (Thu)
                               by bronson (subscriber, #4806)
                              [Link] (7 responses)
       
Personally, I've found that an event-based model is great when you're doing trivial operations (say, a transparent proxy), but tends to fall apart when you're doing more complex stuff (say, a caching proxy with arbitrary, dynamic L7 rewrites).  Get that state machine right on the first try!  Even a small change tends to turn into a huge rippling modification if you didn't anticipate it.  In my experience, avoiding state machines tends to be a win in the long run.  But that's got a serious performance cost. 
A hybrid scheme would be great.  I have to agree with the naysayers though: in networking, everything blocks.  A threadlet scheme will just wind up running as a thread-per-connection scheme.  I hope the kernel guys can find a good way to work around this. 
I'm happy that kevent finally got a bit of well-deserved recognition.  An article comparing kqueue, epoll, kevent, and syslets/threadlets would be fascinating.  I'm afraid that would be more like an academic paper, though...  Anyone interested in doing a masters dissertation?  :) 
      
           
     
    
      Posted Mar 1, 2007 18:30 UTC (Thu)
                               by zooko (guest, #2589)
                              [Link] (5 responses)
       
If you are writing in Twisted Python it is easier still, and if you are writing in E it is easier still. 
C++ and Java fall right between C and Python in terms of the difficulty of doing event-based -- I have written multithreaded code and event-based code in each of them. 
By the way, almost all GUI toolkits use the event-based paradigm, so many programmers are already familiar with the paradigm in at least one domain. 
Regards, 
Zooko 
      
           
     
    
      Posted Mar 1, 2007 20:38 UTC (Thu)
                               by bronson (subscriber, #4806)
                              [Link] (1 responses)
       
It seems like many modern GUI toolkits are using closures and continuations to hide the event loop.  So, while many programmers are familiar with it, I'm not sure they really like it.  :) 
     
    
      Posted Mar 12, 2007 17:19 UTC (Mon)
                               by zooko (guest, #2589)
                              [Link] 
       
It was satisfying enough.  Certainly the freedom from deadlock was worth it. 
     
      Posted Mar 2, 2007 17:34 UTC (Fri)
                               by krasic (guest, #4782)
                              [Link] (1 responses)
       
     
    
      Posted Mar 2, 2007 18:06 UTC (Fri)
                               by markh (subscriber, #33984)
                              [Link] 
       
     
      Posted Mar 2, 2007 18:17 UTC (Fri)
                               by shane (subscriber, #3335)
                              [Link] 
       
Of course, it's also easier to write your threaded code.:)
      
           
     
      Posted Mar 4, 2007 18:55 UTC (Sun)
                               by rwmj (subscriber, #5474)
                              [Link] 
       Personally, I've found that an event-based model is great when you're doing trivial 
operations (say, a transparent proxy), but tends to fall apart when you're doing more complex 
stuff (say, a caching proxy with arbitrary, dynamic L7 rewrites). Get that state machine right on 
the first try! Even a small change tends to turn into a huge rippling modification if you didn't 
anticipate it. 
It's actually possible to implement threads on top of events.  See for
example my implementation in
pthrlib.
At the syscall level, this is using poll(2) to poll for events.  However
the programming model is cooperative threads.
 
It's also possible to turn threads back into events, but (I'm guessing)
this won't be very efficient because threads are certainly heavier than
events.
 
So this is one contribution to the argument that the kernel should just
deliver up events, and allow the userspace to deal with those
directly, or turn them into threads a la pthrlib.
 
Rich.
 
     
    
      State machines aren't difficult. For complex tasks they really simplify things. As code flow branches into a gazillion function calls you eventually just get lost. Wrapping your head around things is near impossible.Thread-based or event-based?
      
      > State machines aren't difficult. For complex tasks they really simplify Thread-based or event-based?
      
> things. As code flow branches into a gazillion function calls you 
> eventually just get lost. Wrapping your head around things is near 
> impossible. 
 
And with something like SDL you can visualize & formally verify them... 
      
          
      A lot of people (including myself) have decided to write event-based code in user space.Thread-based or event-based?
      
      
          
      That's a fascinating paper.  Good find!  Here's a link that worked for me: http://bcr2.uwaterloo.ca/~brecht/papers/eurosys-2007.pdfThread-based or event-based?
      
      I think you are right inasmuch as you are writing in C code.  If you are writing in Python it is rather easier to write your event-based code.  Certainly when we rewrote Mojo Nation (a open source peer-to-peer filesharing app) from one-thread-per-connection to event-based it was easier to understand and debug afterward than before.  (By the way, that's the origin of BitTorrent's use of event-based -- Bram worked at Mojo Nation and re-used the event-based architecture that we developed there when he wrote BitTorrent.)Thread-based or event-based?
      
      What did you use to do event-based in C++?  Did you like it?Thread-based or event-based?
      
      
          
      I did event-based code in C++ in a few ways -- non-blocking I/O with my own event framework atop it, various GUI toolkits, and in a sense also using DEC Message Queue middleware.Thread-based or event-based?
      
      
          
      I know a bit about twisted, but I don't know about "E".   Could someone give a reference or pointer?Thread-based or event-based?
      
      
          
      > I know a bit about twisted, but I don't know about "E". Could someoneThread-based or event-based?
      
> give a reference or pointer?
      If you are writing in Python it is rather easier to write your 
event-based code.
Thread-based or event-based?
      Thread-based or event-based?
      
           