I am new to asynchronous I/O. I need to get it working in some C and Fortran programs on a Linux system. I managed to write a little C test code (included below) that reads asynchronously from two files. The code compiled and ran. What I am wondering, though, is whether I am truly getting asynchronous I/O, or is the I/O really serial? The lustre file system I am dealing with is a bit outdated and it is not clear that it actually supports asynchronous I/O, and no one seems to have a definite answer. So I am wondering are there some timing statements or any kind of output I can add to the code to determine whether it is functioning in a truly asynchronous manner. I'm betting I'll need much larger files than what I am dealing with to do a meaningful test. No idea what else I need.
The code is:
#include <stdio.h>
#include <stdlib.h>
/* for "open()" ... */
#include <fcntl.h>
/* for "bzero()" ... */
#include<strings.h>
/* for asynch I/O ... */
#include <aio.h>
/* for EINPROGRESS ... */
#include <errno.h>
/* for "usleep()" ... */
#include <unistd.h>
#define BUFSIZE 1024
int main() {
int fd0, fd1, readstat0, readstat1;
struct aiocb *ioobjs[2];
ioobjs[0] = malloc(sizeof(struct aiocb));
ioobjs[1] = malloc(sizeof(struct aiocb));
fd0 = open("file.txt", O_RDONLY);
if (fd0 < 0) perror("open");
fd1 = open("otherfile.txt", O_RDONLY);
if (fd1 < 0) perror("open");
bzero((char *)ioobjs[0], sizeof(struct aiocb));
bzero((char *)ioobjs[1], sizeof(struct aiocb));
ioobjs[0]->aio_buf = malloc(BUFSIZE+1);
if (!ioobjs[0]->aio_buf) perror("malloc 0");
ioobjs[1]->aio_buf = malloc(BUFSIZE+1);
if (!ioobjs[1]->aio_buf) perror("malloc 0");
ioobjs[0]->aio_fildes = fd0;
ioobjs[0]->aio_nbytes = BUFSIZE;
ioobjs[0]->aio_offset = 0;
/* Don't forget this! With list I/O, there is no
* particular function call to make. You have to
* tell what you want to do via this member of
* your aiocb struct:
*/
ioobjs[0]->aio_lio_opcode = LIO_READ;
ioobjs[1]->aio_fildes = fd1;
ioobjs[1]->aio_nbytes = BUFSIZE;
ioobjs[1]->aio_offset = 0;
ioobjs[1]->aio_lio_opcode = LIO_READ;
readstat0 = aio_read(ioobjs[0]);
if (readstat0 < 0) perror("reading 0");
readstat1 = aio_read(ioobjs[1]);
if (readstat1 < 0) perror("reading 1");
lio_listio(LIO_NOWAIT, ioobjs, 2, NULL);
/* don't completely understand. gives system time to
* "wrap things up". without this, one of the outputs
* below (maybe both) will have no output to give.
*/
usleep(100);
if ((readstat0 = aio_return( ioobjs[0] )) > 0) {
printf(">>>\n");
printf("%s\n", (char *)(ioobjs[0]->aio_buf));
printf("<<<\n");
} else {
perror("return");
}
if ((readstat1 = aio_return( ioobjs[1] )) > 0) {
printf(">>>\n");
printf("%s\n", (char *)(ioobjs[1]->aio_buf));
printf("<<<\n");
} else {
perror("return");
}
}
From
man aio
, note thataio_*
is wholly aglibc
[userspace] implementation.So, as mentioned, it has some limitations.
The way to see what's going on, timewise, is to have an event log with timestamps.
The naive approach is to just use [debug]
printf
calls. But, for precision time measurements, the overhead ofprintf
can disrupt the real/actual timing. That is, we don't measure "the system under test", but, rather, "the system under test + the timing/benchmark overhead".One way is to run your program under
strace
with appropriate timestamp options. Thestrace
log will have information about the syscalls used. But, becauseaio
is implemented in userspace, it may not be able to drill down to a fine enough grain. And,strace
itself can impose an overhead.Another way is to create a trace/event log mechanism and instrument your code. Basically, it implements a fixed length ring queue of "trace elements". So, the trace data is stored in memory, so it's very fast.
A standard utility that can help with this is
dtrace
. I've not done this myself, as I've preferred to "roll my own". See below for some actual code I've used.Then, instrument your code with (e.g.):
Where
TRACE_*
is from anenum
you define as you wish.Anyway, here's some event trace code I've used in the past. The event struct can be extended to add whatever extra data you wish to store at an event point.