
In real life, the most noticeable performance-related difference is that since threads share the same memory, and processes each have their own memory space, making separate processes tends to take up a lot more memory. The third thing that’s critical to follow is what happens when you have a lot of threads or processes that start blocking.įor our purposes, there is not a huge difference between a thread and process. And which mechanism is used will block the calling process for dramatically different lengths of time. The kernel provides the means to do both blocking I/O (“read from this network connection and give me the data”) and non-blocking I/O (“tell me when any of these network connections have new data”). Your process just waited 10 million times longer for the blocking call. And let’s say, for example, the non-blocking call took 20 nanoseconds, and the blocking call took 200,000,000 nanoseconds. A call that blocks for information being received over the network might take a much longer time - let’s say for example 200 milliseconds (1/5 of a second). A non-blocking system call might take on the order of 10s of cycles to complete - or “a relatively few nanoseconds”. If a CPU core is running at 3GHz, without getting into optimizations the CPU can do, it’s performing 3 billion cycles per second (or 3 cycles per nanosecond). It’s important to understand the order of magnitude of difference in timing here. This is great if you need the functionality, but as you can see it’s certainly more complex to use. This allows you to efficiently control a large number of I/O operations with a single thread, but I’m getting ahead of myself. epoll_create(), epoll_ctl() and epoll_wait() are calls that, respectively, let you create a group of handles to listen on, add/remove handlers from that group and then block until there is any activity. Note that this has the advantage of being nice and simple. read() is a blocking call - you pass it a handle saying which file and a buffer of where to deliver the data it reads, and the call returns when the data is there. Some examples (of Linux syscalls) might help clarify: So it “blocks” for only a very brief time period, just long enough to enqueue your request. However, some calls are categorized as “non-blocking,” which means that the kernel takes your request, puts it in queue or buffer somewhere, and then immediately returns without waiting for the actual I/O to occur. Now, I just said above that syscalls are blocking, and that is true in a general sense. In the real world, the kernel might have to do a number of things to fulfill your request including waiting for the device to be ready, updating its internal state, etc., but as an application developer, you don’t care about that. The kernel performs the underlying I/O operation on the physical device in question (disk, network card, etc.) and replies to the syscall.Generally speaking, syscalls are blocking, meaning your program waits for the kernel to return back to your code. There is going to be some specific instruction that transfers control from your program over to the kernel (like a function call but with some special sauce specifically for dealing with this situation). The specifics of how this is implemented vary between OSes but the basic concept is the same. A “syscall” is the means by which your program asks the kernel do something.Your program (in “user land,” as they say) must ask the operating system kernel to perform an I/O operation on its behalf.System Callsįirstly, we have system calls, which can be described as follows: While it is unlikely that will have to deal with many of these concepts directly, you deal with them indirectly through your application’s runtime environment all the time. To understand the factors involved with I/O, we must first review the concepts down at the operating system level. If you’re concerned about the I/O performance of your next web application, this article is for you. In this article, we’ll be comparing Node, Java, Go, and PHP with Apache, discussing how the different languages model their I/O, the advantages and disadvantages of each model, and conclude with some rudimentary benchmarks. Let’s take a walk across the I/O landscape and see what we can spy. But as your application’s traffic load increases, working with the wrong I/O model can get you into a world of hurt.Īnd like most any situation where multiple approaches are possible, it’s not just a matter of which one is better, it’s a matter of understanding the tradeoffs. Perhaps while your application is small and does not serve high loads, it may matter far less. Understanding the Input/Output (I/O) model of your application can mean the difference between an application that deals with the load it is subjected to, and one that crumples in the face of real-world use cases.
