Return to BSD News archive
Newsgroups: comp.unix.bsd.bsdi.misc,comp.unix.advocacy Path: euryale.cc.adfa.oz.au!newshost.anu.edu.au!harbinger.cc.monash.edu.au!nntp.coast.net!howland.reston.ans.net!ix.netcom.com!netcom.com!bakul From: bakul@netcom.com (Bakul Shah) Subject: Re: multiple httpds vs threads vs ... (was BSDI Vs. NT...) Message-ID: <bakulDK7u6M.LrM@netcom.com> Organization: NETCOM On-line Communication Services (408 261-4700 guest) References: <taxfree.3.00C439A1@primenet.com> <4be592$6tb@madeline.ins.cwru.edu> <4bhfmp$gei@Mars.mcs.com> <DK5Crs.I77@metrics.com> <4bmsjp$7lv@elf.bsdi.com> Date: Tue, 26 Dec 1995 22:51:58 GMT Lines: 87 Sender: bakul@netcom22.netcom.com Xref: euryale.cc.adfa.oz.au comp.unix.bsd.bsdi.misc:1847 comp.unix.advocacy:12661 torek@elf.bsdi.com (Chris Torek) writes: >There is a third approach---in one way, kind of a compromise between >these two---and that is to provide a mechanism such as poll() or >select() by which a process can implement its own `threading' (except >that these threads can be even lighter-weight than kernel threads). Yet another approach is to use the `callback' model of programming. With each connection you associate an object and when something happens on the connection, you call back a procedure of the associated object. This callback model can be implemented by either select()/poll() or some sort of asynchronous notification capability. In practice this sort of calling back turns out to be lot more efficient, even if more complicated, than using simulated threads atop select() or using threads or processes. If you think about it, this is not surprising; any scheme that *avoids* context switching is likely to be quite a bit more efficient than one that *requires* context switching to serve multiple clients. Of course, callback is not free but its cost is typically on the order of a function call. The other extreme of using just one thread or process for serving everyone is also not a good idea. The one and only thread can end up spending lots of time on an expensive operation, thereby denying service to others (witness what happens under X windows when you try a new big scalable font or PEX on a slow machine). On an N processor machine that can concurrently run N threads, using 4N threads or some such small multiple of N seems to work out the best. Note that select()/poll() also have a couple of problems: a) they do not scale well (though poll() scales somewhat better) and b) using them such that _fair_ service is provided is not easy. a) If you are serving 1000 file-descriptors, every time select() is called the system has to examine all 1000 fds regardless of actual traffic (and regardless of how many threads you use; collectively you are still calling select on all 1000). Typically, fdsets to select do not change from one call to another and a different, more scalable interface can be designed to take advantage of that -- or perhaps a better implementation of the present interface will handle this. b) The problem of fair service is that people always walk fdsets starting with the 0th element and onwards (in fact I have not seen any code that does not do this). If two fds, N & N+K, are ready, N will always get serviced before N+K. If the dialog between the server and a client (connected via fds N & N+K) is fast and furious, client N+K will fall further and further behind of client N. Neither problem is critical for most applications but as we build more and more connected apps or distributed apps these problems are starting to raise their heads. >That leaves non-preemptive threads, but for the most part, these can >be implemented on top of select() or poll(). Yes, but beware of the unfairness issue! >As for http itself, I will simply note that Jef Poskanzer has been >working on something called `thttpd' that implements threaded HTTP >service in a single UNIX process using select(). My understanding >is that, while it is not (yet?) as `full featured' as other existing >HTTP servers, it beats the pants off them. I am not surprised. On the BSDi (or Unix in general) vs NT issue I can relate my experience. I wrote a server that is quite typical in that lots of clients connect to it via TCP and get some service. I use select() on all platforms and the server is built from the same source code (modulo a bit of low level OS specific code). What we found is that the server runs at fairly similar speed and uses similar percentage of the CPU under NT and Unix (where comparison was possible on identical platforms). Porting to NT was surprisingly easy, not to mention its picky C++ compiler caught a number of portability bugs. I was also surprised to find that NT's select() implementation can handle many more fds than BSDi, SunOS, Solaris, NetBSD, FreeBSD, Linux etc. Only IRIX did better (haven't yet tested a number of other UNIX machines). For OSes where I have the source I can recompile the kernel with bigger FDSET_SIZE but they really should be doing dynamic allocation. Bakul Shah <bakul@netcom.com>