https://geocar.sdf1.org/fast-servers.html
fast-servers
There's a network-server programming pattern which is so popular that
it's the canonical approach towards writing network servers:
Flowchart of network server design described below Flowchart of network server design described below ...
This design is easy to recognise: The main loop waits for some event,
then dispatches based on the file descriptor and state that the file
descriptor is in. At one point it was in vogue to actually fork() so
that each file descriptor could be handled by a different thread, but
now "worker threads" are usually created that all perform the same
task and rely on the kernel to schedule file descriptors to them.
A much better design is possible because of the epoll and kqueue,
however most people use these "new" system calls using a wrapper like
libevent which just encourages the same slow design people have been
using for over twenty years now.
The design I currently use and recommend involves two major points:
1. One thread per core, pinned (affinity) to separate CPUs, each
with their own epoll/kqueue fd
2. Each major state transition (accept, reader) is handled by a
separate thread, and transitioning one client from one state to
another involves passing the file descriptor to the epoll/kqueue
fd of the other thread.
Flowchart of improved network server design...
This design has no decision points, simple blocking/IO calls, and
makes simple one-page performant servers that easily get into the
100k requests/second territory on modern systems.
Creating the thread pool
Ask the operating system how many cores there are. Sometimes
reserving some cores make sense, so let the user lower this number.
If raising this number helps, then your state transitions are too
complex and you will need to break them up.
pthread_attr_t a;
pthread_attr_init(&a);pthread_attr_setscope(&
a,PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&a,PTHREAD_CREATE_DETACHED);
t=sysconf(_SC_NPROCESSORS_ONLN);
for(i=0;i