blktrace support: speedup reading of data
authorJens Axboe <jens.axboe@oracle.com>
Wed, 16 May 2007 07:25:09 +0000 (09:25 +0200)
committerJens Axboe <jens.axboe@oracle.com>
Wed, 16 May 2007 07:25:09 +0000 (09:25 +0200)
We used to read in data in really small chunks (48 bytes at the time,
the size of the trace). This is really slow for large traces, so
add a fifo frontend to refill the cache in much larger sizes.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Makefile
blktrace.c
fifo.c [new file with mode: 0644]
fifo.h [new file with mode: 0644]
fio.h

index 5858ab090c774c6036708d6432b5b89a046c6a67..9effdaa4d5b3e8560059e39f3e6310e1f5bedc8a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ PROGS   = fio
 SCRIPTS = fio_generate_plots
 OBJS = gettime.o fio.o ioengines.o init.o stat.o log.o time.o md5.o crc32.o \
        filesetup.o eta.o verify.o memory.o io_u.o parse.o mutex.o options.o \
-       rbtree.o diskutil.o blktrace.o
+       rbtree.o diskutil.o fifo.o blktrace.o
 
 OBJS += engines/cpu.o
 OBJS += engines/libaio.o
index afa699cd1a3f7f4225e4b220654d347c9641397a..fc98091868566531b6cc2a3069d5d5c38c79a20b 100644 (file)
@@ -8,6 +8,59 @@
 #include "fio.h"
 #include "blktrace_api.h"
 
+#define TRACE_FIFO_SIZE        (sizeof(struct blk_io_trace) * 1000)
+
+/*
+ * fifo refill frontend, to avoid reading data in trace sized bites
+ */
+static int refill_fifo(struct thread_data *td, struct fifo *fifo, int fd)
+{
+       char buf[TRACE_FIFO_SIZE];
+       unsigned int total, left;
+       void *ptr;
+       int ret;
+
+       total = 0;
+       ptr = buf;
+       while (total < TRACE_FIFO_SIZE) {
+               left = TRACE_FIFO_SIZE - total;
+
+               ret = read(fd, ptr, left);
+               if (ret < 0) {
+                       td_verror(td, errno, "read blktrace file");
+                       return -1;
+               } else if (!ret)
+                       break;
+
+               fifo_put(fifo, ptr, ret);
+               ptr += ret;
+               total += ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Retrieve 'len' bytes from the fifo, refilling if necessary.
+ */
+static int trace_fifo_get(struct thread_data *td, struct fifo *fifo, int fd,
+                         void *buf, unsigned int len)
+{
+       int ret;
+
+       if (fifo_len(fifo) >= len)
+               return fifo_get(fifo, buf, len);
+
+       ret = refill_fifo(td, fifo, fd);
+       if (ret < 0)
+               return ret;
+
+       if (fifo_len(fifo) < len)
+               return 0;
+
+       return fifo_get(fifo, buf, len);
+}
+
 /*
  * Just discard the pdu by seeking past it.
  */
@@ -120,6 +173,7 @@ int load_blktrace(struct thread_data *td, const char *filename)
        unsigned long ios[2];
        unsigned int cpu;
        unsigned int rw_bs[2];
+       struct fifo *fifo;
        int fd;
 
        fd = open(filename, O_RDONLY);
@@ -128,6 +182,8 @@ int load_blktrace(struct thread_data *td, const char *filename)
                return 1;
        }
 
+       fifo = fifo_alloc(TRACE_FIFO_SIZE);
+
        td->o.size = 0;
 
        cpu = 0;
@@ -135,22 +191,15 @@ int load_blktrace(struct thread_data *td, const char *filename)
        ios[0] = ios[1] = 0;
        rw_bs[0] = rw_bs[1] = 0;
        do {
-               /*
-                * Once this is working fully, I'll add a layer between
-                * here and read to cache trace data. Then we can avoid
-                * doing itsy bitsy reads, but instead pull in a larger
-                * chunk of data at the time.
-                */
-               int ret = read(fd, &t, sizeof(t));
+               int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
 
-               if (ret < 0) {
-                       td_verror(td, errno, "read blktrace file");
+               if (ret < 0)
                        goto err;
-               } else if (!ret) {
+               else if (!ret)
+                       break;
+               else if (ret < (int) sizeof(t)) {
+                       log_err("fio: short fifo get\n");
                        break;
-               } else if (ret != sizeof(t)) {
-                       log_err("fio: short read on blktrace file\n");
-                       goto err;
                }
 
                if ((t.magic & 0xffffff00) != BLK_IO_TRACE_MAGIC) {
diff --git a/fifo.c b/fifo.c
new file mode 100644 (file)
index 0000000..1e55b63
--- /dev/null
+++ b/fifo.c
@@ -0,0 +1,88 @@
+/*
+ * A simple kernel FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fifo.h"
+
+struct fifo *fifo_alloc(unsigned int size)
+{
+       struct fifo *fifo;
+
+       fifo = malloc(sizeof(struct fifo));
+       if (!fifo)
+               return 0;
+
+       fifo->buffer = malloc(size);
+       fifo->size = size;
+       fifo->in = fifo->out = 0xffff0000;
+
+       return fifo;
+}
+
+void fifo_free(struct fifo *fifo)
+{
+       free(fifo->buffer);
+       free(fifo);
+}
+
+unsigned int fifo_put(struct fifo *fifo, void *buffer, unsigned int len)
+{
+       unsigned int l;
+
+       len = min(len, fifo->size - fifo->in + fifo->out);
+
+       /* first put the data starting from fifo->in to buffer end */
+       l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
+       memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
+
+       /* then put the rest (if any) at the beginning of the buffer */
+       memcpy(fifo->buffer, buffer + l, len - l);
+
+       /*
+        * Ensure that we add the bytes to the fifo -before-
+        * we update the fifo->in index.
+        */
+
+       fifo->in += len;
+
+       return len;
+}
+
+unsigned int fifo_get(struct fifo *fifo, void *buffer, unsigned int len)
+{
+       unsigned int l;
+
+       len = min(len, fifo->in - fifo->out);
+
+       /* first get the data from fifo->out until the end of the buffer */
+       l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
+       memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
+
+       /* then get the rest (if any) from the beginning of the buffer */
+       memcpy(buffer + l, fifo->buffer, len - l);
+
+       fifo->out += len;
+
+       return len;
+}
diff --git a/fifo.h b/fifo.h
new file mode 100644 (file)
index 0000000..6a9115d
--- /dev/null
+++ b/fifo.h
@@ -0,0 +1,48 @@
+/*
+ * A simple FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+struct fifo {
+       unsigned char *buffer;  /* the buffer holding the data */
+       unsigned int size;      /* the size of the allocated buffer */
+       unsigned int in;        /* data is added at offset (in % size) */
+       unsigned int out;       /* data is extracted from off. (out % size) */
+};
+
+struct fifo *fifo_alloc(unsigned int);
+unsigned int fifo_put(struct fifo *, void *, unsigned int);
+unsigned int fifo_get(struct fifo *, void *, unsigned int);
+
+static inline unsigned int fifo_len(struct fifo *fifo)
+{
+       return fifo->in - fifo->out;
+}
+
+#define min(x,y) ({ \
+       typeof(x) _x = (x);     \
+       typeof(y) _y = (y);     \
+       (void) (&_x == &_y);            \
+       _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+       typeof(x) _x = (x);     \
+       typeof(y) _y = (y);     \
+       (void) (&_x == &_y);            \
+       _x > _y ? _x : _y; })
+
diff --git a/fio.h b/fio.h
index fa0edb1ed02938899776f397aa97b446ad7052e1..bcd188b37808b8bc2f9e76c32d99a0a733803ce0 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -15,6 +15,7 @@
 
 #include "compiler/compiler.h"
 #include "list.h"
+#include "fifo.h"
 #include "rbtree.h"
 #include "md5.h"
 #include "crc32.h"
@@ -665,13 +666,6 @@ struct disk_util {
 
 #define DISK_UTIL_MSEC (250)
 
-#ifndef min
-#define min(a, b)      ((a) < (b) ? (a) : (b))
-#endif
-#ifndef max
-#define max(a, b)      ((a) > (b) ? (a) : (b))
-#endif
-
 /*
  * Log exports
  */