As one illustrative example of the overall benefits of a synchronous programming model in Google, a rewrite of the Colossus6 client library to use a synchronous I/O model with lightweight threads gained a significant improvement in performance while making the code more compact and easier to understand. More important, however, was that shifting the burden of managing asynchronous events away from the programmer to the operating system or the thread library makes the code significantly simpler.
I had a hard time understanding what this was saying. To me: synchronous programming is when I use a system call (as in calling the kernel) whose latency is dependent on some external event, such as another party sending data or a timer going off. Asynchronous is when I use something like select or kqueue which let’s me register my need and then go do something else.
The above paragraph says they use synchronous I/O model but with lightweight threads, which sounds closer to an asynchronous model to me.
Synchronous I/O is a programming model, whereas “lightweight threads” means that you don’t rely on OS threads to manage your call stack. These two are fundamentally orthogonal to each other. Even in C++, you can use coroutines to program in a synchronous fashion, while using lightweight threads. While in Haskell, this is the default without you having to do anything.
Given the definition of “synchronous” I gave, what you described still sounds like “asynchronous I/O”.
True, lightweight threads are implemented under the hood using some kind of asynchronous approach. But I read this as arguing for the synchronous I/O model as a better model in terms of what to expose to the programmer. From the perspective of someone writing code, a pseudo-blocking call using a userland/green threading library still looks like making a blocking call (your thread blocks until the call returns, etc.), and doesn’t look very much like registering a callback or interacting with select, even if the threading library does something asynchronous under the hood.
They are using the operating system to hide the complexity of asynchronous I/O from the programmer. The programmer creates a thread ; the thread does blocking I/O; the thread continues when the I/O arrives. Other application threads will run while the original thread is blocked. The argument is that the OS takes over the burden that would otherwise be implemented by the programmer in some callback/event procedure.
That is not really the impression the article gives. They refer to “lightweight threads” and they explicit talk about context switches being too expensive for the kind of operations they want to do. It sounds like they just don’t want to do Node.Js style callbacks.