Applications implementing Asynchronous I/O (AIO) can perform much faster than those using synchronous I/O. In this post I’m going to make a brief explanation on how this paradigm works and why you should use it.
What is Asynchronous I/O?
Wikipedia has a very simple definition of it:
Asynchronous I/O, or non-blocking I/O is a form of input/output processing that permits other processing to continue before the transmission has finished.
The basic idea behind AIO is to allow a process to initiate a number of I/O operations without having to block or wait for any of them to complete. At some later time, or after being notified of I/O completion, the process can retrieve the results of the I/O. The way the application gets notified about the I/O completion is usually determined by the platform the software is running on. The possibilities are hardware interrupts, signals, callback functions, or even any polling mechanism like poll () or select (). The latter are different in the sense they still block somewhere (while waiting for the notification). Please note that with “non-blocking I/O” we refer to the operation itself, not to non-blocking sockets.
Before digging more into AIO, I’m going to do a slight review of synchronous I/O.
Figure 1 illustrates the traditional blocking I/O model. This model, besides being very simple, works well for most applications. In fact, this is the most common model used in applications nowadays. The application will block during the execution of the system call read () until there’s a response from the device being read.
This model is very good for simple applications connecting to a server or cloud service because the application consumes no CPU while waiting for the I/O operation to complete. In this case, the application might have only one background thread which will block while the I/O operation is performed and notify the GUI at the end. But this model does not scale the other way around:
- What if the application needs to create 100 connections?
- What if those connections must be handled at the same time?
- Have you ever heard about the C10K problem?
The C10K problem refers to the ability of server to handle ten thousand clients simultaneously. This would be impractical if using synchronous I/O, how would a programmer implement this?
- Create 10K threads?
- Create a single thread and handle clients one at a time?
- Create a small amount -N- of threads in order to handle N clients at the same while the other 10000 – N clients wait?
Asynchronous I/O to the rescue!
Asynchronous I/O is based in the following premise:
Input/Ouput operations, such as writing to a disk or reading from a socket, are orders of magnitude slower than accessing the CPU.
AIO allows the application to perform other tasks while the I/O operations are being performed. Back to the C10K problem, the server can handle the client N+1 while the data to client N is is travelling through the wire. This model is illustrated in Figure 2 below.
This illustration shows how the application sends an asynchronous read request to the kernel and then, without waiting for any response, performs other operations that might have no relation with the previous call. At some later point, the I/O operation is completed and the application gets notified.
In summary, asynchronous I/O enhances performance by allowing applications to overlap processing with I/O operations. AIO can help you build an application that more efficiently uses the CPU resources available. Even with a very small number of threads, response times will decrease dramatically using this technique.
The following sites contain useful information about the topic:
- Introduction to Asynchronous I/O in Wikipedia.
- Details about the implementation of the POSIX.1b standard in the GNU library.
- Realtime support in Linux, is not only about AIO, but also about other real-time facilities.
- Notes about AIO design in the Linux Kernel.
- The C10K problem.
- Tim Jones writes about AIO in IBM developerWorks.