*BSD News Article 58190


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>