Hacker News new | ask | show | jobs
by vbit 3811 days ago
I generally prefer coroutines myself but this article doesn't cover some points well.

'Fair scheduling' - nobody writes single threaded programs that process requests sequentially. It's either a thread-per-request system vs. a coroutine-per-request system. Threads are generally going to be more fair at scheduling since they are preemptive. Most coroutines are cooperative (Python, Lua) - so a long running coroutine (stuck doing some CPU cycle) will block all other inflight requests and cause latency variance. Some systems are preemptive (Erlang) so they don't suffer from the variance.

The benefit of coroutines is you can have a very large number of coroutines, probably orders of magnitude more than the number of threads - so coroutine-per-request models will scale much more than threads-per-request. The article is spot on about the context switching cost - it's so much cheaper to switch between coroutines.

You can also use multiple request per threads and use async IO, but that's the same as using coroutines with a much worse programming model.

1 comments

Yes, fibers/coroutines are good for socket I/O because sockets could be made non-blocking, therefore a fiber could yield on EWOULDBLOCK error from a socket. For file I/O there is no non-blocking I/O option, so the whole OS-thread might block on it along with all the fibers that it owns. Therefore any operation that is not socket I/O and goes beyond CPU and RAM should be delegated from fibers to a good old worker thread pool. The fiber should yield after submitting a request to the pool, and on the request completion the worker thread should notify the fiber scheduler to resume the original fiber.
Well, there's no POSIX async file I/O, but there's libaio for Linux's aio syscalls (io_submit and co.), which works. (MySQL/InnoDB uses it for example.)
Upon return from a non-blocking I/O call the request is either failed or succeeded. Upon return from an async I/O call the request might additionally be in progress thus the fiber is required to wait for its completion. This case has little practical difference from a thread pool. Except with a thread pool it is the worker thread that notifies the fiber scheduler on I/O completion. With async I/O it is the kernel that notifies the application and then the application (from a signal handler or from an auxiliary thread) should propagate the notification to the fiber scheduler.