分类:
2008-10-13 16:11:10
Whether (2) is best or not depends upon the kernel interface. If a
scan of 15,000 file descriptors is required for each event (such as
a straightforward design based on select()) then (2) could be nearly
as bad as a naive accept()/fork() model, while a thread-per-connection
model might scale better. If the kernel somehow passed an event list
to the server process more efficiently than via select() or poll(),
then I would agree that (2) is probably always the better way, perhaps
with extensions into a thread-per-processor regime on SMP's.
Achieving the best efficiency involves coupling the arrival of a packet
to the object (explicit or implicit) handling that particular connection
in the most direct way possible, modulo buffering. Eliminating linear
searches from that coupling is important--a scan of 15,000 descriptors
is going to be a significant hit, no matter what other efficiencies are
involved, since it is O(n^2) (where n is the number of connections). This
is why a threaded model might work better for large numbers of connections,
assuming that thread scheduling is better than O(n^2). (Is it?)
I've actually implemented high-efficiency servers based on a combination
of (1) and (2); in essence, a small pool of threads each handling a number
of connections using an event-based model. At least for my project (which
involved high connection rates more than large numbers of sustained
connections), one or two threads per processor seemed to be the most
efficient configuration--but this was under Solaris, not Linux, though
I certainly was able to achieve good performance (>1000 connects/second
on a UP P2/333) on the latter in tests.
Just another data point.
-Ed
-