Because the functions are, uh, functional there is almost no state to save and restore when there is a context switch. Also because things like iteration are done with recursion- the 20 functions and then context switch doesn't get hung up on some loop somewhere. The faster the OS tries to keep switching between processes, the more overhead it introduces.
BTW, this is why in my opinion you don't see a lot of other systems duplicating this sort of concurrency model- because there are some language "limitations" (recursion only, for example) that make it possible.
Most apache, erlang, or kernel hackers would know way more about this than I do, of course.
IIR my OS design course correctly, the big performance hit is the switch from user mode to kernel mode. I'm not sure *why* that's a big hit, but it seems to be a slow operation on most processors.
You can use user-mode threading libraries in C/C++, but Apache doesn't. Perhaps that's why it's slow. (The main reason it doesn't is probably that user-mode threading blocks the whole process when one thread performs IO, which obviously doesn't work well in an I/O bound application like a webserver.)
There are other C/C++ webservers - like Lighttpd - that use poll/epoll for I/O. These should run even faster than YAWS/Erlang; anyone have any benchmarks to compare them?
I wonder if old pre-forked process Apache would have done better, since it's not so easy to trick it into consuming infinite memory.