March 14th, 2016
(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: firstname.lastname@example.org
The obvious question thus is: Why state machines? Why not processes or threads?
And the obvious answer is: Performance.
When UNIX was still young, scheduling was supposed to be done by the OS on per-process basis. When implementing a network server, for example, you were supposed to fork a new instance of the process for each TCP connection and rely on the OS scheduler to switch between the processes.
I guess it made sense from performance point of view back then. All the stuff that makes process switching slow today haven’t yet existed. Machines had single processor, there was no virtual addressing or memory caches. In such an environment process switching was closer to what we call green threads today.
Later on came threads. Threads are like processes, but operate in the same address space, so there’s no need to switch the TLBs. Thus, cost of context switch is lower.
Finally, we’ve got green threads. These are typically implemented on the language level and avoid even more cost. The latest and most optimised version is Golang’s goroutines. They are cooperatively scheduled, thus there is no cost associated with preemption. They have tiny stacks, maybe couple of kilobytes. They can even be scheduled on multiple CPU cores like real OS threads.
Goroutines are performance-wise comparable with hand-written state machines. It seems that there’s no more space for radical performance improvement.
So, we can forget about state machines and use green threads instead, right?
Unfortunately no. Green threads, even if they bring us a long way towards the goal, aren’t sufficient to eliminate all state machines. In next blog post I’ll dig into the remaining problems in more detail.