[PATCH] Add support for the bsrange=x-y option, mixing thread block sizes
authorJens Axboe <axboe@suse.de>
Thu, 27 Oct 2005 10:37:39 +0000 (12:37 +0200)
committerJens Axboe <axboe@suse.de>
Thu, 27 Oct 2005 10:37:39 +0000 (12:37 +0200)
README.fio
fio.c

index 9b2c2efee3ab7f730be5af5abd5425ed9c07cd82..fecf78dd5a222309bfc138c373086faeed095004 100644 (file)
@@ -29,6 +29,7 @@ The <jobs> format is as follows:
        prioclass=x     Run io at prio class X
        file=foo        Do the io to file foo
        bs=x            Thread blocksize is x bytes
+       bsrange=x-y     Mix thread block sizes randomly between x and y
        direct=x        1 for direct IO, 0 for buffered IO
        delay=x         Delay x useconds before each io
        random          IO is randomized
diff --git a/fio.c b/fio.c
index 0bc22e897d2f6b9d348129b32afb5827d4e4664e..07b08a2f669fdedc73e74e8fa4398f5b6b751aa3 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -232,6 +232,8 @@ struct thread_data {
        unsigned int mem_type;
        cpu_set_t cpumask;
 
+       struct drand48_data bsrange_state;
+
        int shm_id;
 
        off_t cur_off;
@@ -253,10 +255,10 @@ struct thread_data {
        struct timeval lastrate;
 
        unsigned long runtime;          /* sec */
-       unsigned long blocks;
+       unsigned long kb;
        unsigned long io_blocks;
        unsigned long io_kb;
-       unsigned long last_block;
+       unsigned long last_kb;
        sem_t mutex;
        struct drand48_data random_state;
 
@@ -295,7 +297,9 @@ static void sig_handler(int sig)
 
 static int init_random_state(struct thread_data *td)
 {
-       unsigned long seed;
+       unsigned long seed = DEF_RANDSEED;
+
+       srand48_r(seed, &td->bsrange_state);
 
        if (td->sequential)
                return 0;
@@ -315,8 +319,7 @@ static int init_random_state(struct thread_data *td)
                }
 
                close(fd);
-       } else
-               seed = DEF_RANDSEED;
+       }
 
        srand48_r(seed, &td->random_state);
        return 0;
@@ -375,13 +378,31 @@ static unsigned long long get_next_offset(struct thread_data *td)
 
        if (!td->sequential) {
                lrand48_r(&td->random_state, &r);
-               b = (1+(double) (td->blocks-1) * r / (RAND_MAX+1.0));
-       } else {
-               b = td->last_block;
-               td->last_block++;
+               b = (1+(double) (td->kb-1) * r / (RAND_MAX+1.0));
+       } else
+               b = td->last_kb << 10;
+
+       return b + td->file_offset;
+}
+
+static unsigned int get_next_buflen(struct thread_data *td)
+{
+       unsigned int buflen;
+       long r;
+
+       if (td->min_bs == td->max_bs)
+               buflen = td->min_bs;
+       else {
+               lrand48_r(&td->bsrange_state, &r);
+               buflen = (1 + (double) (td->max_bs - 1) * r / (RAND_MAX + 1.0));
+               buflen = (buflen + td->min_bs - 1) & ~(td->min_bs - 1);
        }
 
-       return b * td->bs + td->file_offset;
+       if (buflen > ((td->kb - td->io_kb) << 10))
+               buflen = (td->kb - td->io_kb) << 10;
+
+       td->last_kb += buflen >> 10;
+       return buflen;
 }
 
 static inline void add_stat_sample(struct thread_data *td, struct io_stat *is,
@@ -433,7 +454,7 @@ static void add_bw_sample(struct thread_data *td, unsigned long msec)
        if (spent < 500)
                return;
 
-       rate = (td->io_kb - td->stat_io_kb) / spent;
+       rate = ((td->io_kb - td->stat_io_kb) * 1024) / spent;
        add_stat_sample(td, &td->bw_stat, rate);
 
        if (td->bw_log)
@@ -497,7 +518,7 @@ static int check_min_rate(struct thread_data *td, struct timeval *now)
                if (spent < td->ratecycle)
                        return 0;
 
-               rate = (td->io_kb - td->rate_kb) / spent;
+               rate = ((td->io_kb - td->rate_kb) * 1024) / spent;
                if (rate < td->ratemin) {
                        printf("Client%d: min rate %d not met, got %ldKiB/sec\n", td->thread_number, td->ratemin, rate);
                        if (rate_quit)
@@ -529,15 +550,23 @@ static void put_io_u(struct thread_data *td, struct io_u *io_u)
 static struct io_u *get_io_u(struct thread_data *td)
 {
        struct io_u *io_u;
+       unsigned int len;
+       unsigned long long off;
 
        if (list_empty(&td->io_u_freelist))
                return NULL;
 
+       off = get_next_offset(td);
+       len = get_next_buflen(td);
+       if (!len)
+               return NULL;
+
        io_u = list_entry(td->io_u_freelist.next, struct io_u, list);
        list_del(&io_u->list);
        list_add(&io_u->list, &td->io_u_busylist);
 
-       io_u->offset = get_next_offset(td);
+       io_u->offset = off;
+       io_u->buflen = len;
 
        if (td->use_aio) {
                if (td_read(td))
@@ -553,12 +582,12 @@ static struct io_u *get_io_u(struct thread_data *td)
 
 static void do_sync_io(struct thread_data *td)
 {
-       unsigned long blocks, msec, usec;
+       unsigned long msec, usec;
        struct timeval e;
 
        td->cur_off = 0;
 
-       for (blocks = 0; blocks < td->blocks; blocks++) {
+       for (td->io_kb = 0; td->io_kb < td->kb;) {
                struct io_u *io_u;
                int ret;
 
@@ -566,6 +595,8 @@ static void do_sync_io(struct thread_data *td)
                        break;
 
                io_u = get_io_u(td);
+               if (!io_u)
+                       break;
 
                if (td->cur_off != io_u->offset) {
                        if (lseek(td->fd, io_u->offset, SEEK_SET) == -1) {
@@ -703,9 +734,9 @@ static void cleanup_pending_aio(struct thread_data *td)
 static void do_async_io(struct thread_data *td)
 {
        struct timeval s, e;
-       unsigned long blocks, usec;
+       unsigned long usec;
 
-       for (blocks = 0; blocks < td->blocks; blocks++) {
+       for (td->io_kb = 0; td->io_kb < td->kb;) {
                struct timespec ts = { .tv_sec = 0, .tv_nsec = 0};
                struct timespec *timeout;
                int ret, min_evts = 0;
@@ -718,6 +749,8 @@ static void do_async_io(struct thread_data *td)
                        usec_sleep(td->delay_sleep);
 
                io_u = get_io_u(td);
+               if (!io_u)
+                       break;
 
                memcpy(&s, &io_u->start_time, sizeof(s));
 
@@ -830,7 +863,7 @@ static int init_io_u(struct thread_data *td)
        else
                max_units = td->aio_depth;
 
-       mem_size = td->bs * max_units + MASK;
+       mem_size = td->max_bs * max_units + MASK;
 
        if (td->mem_type == MEM_MALLOC)
                td->orig_buffer = malloc(mem_size);
@@ -859,9 +892,7 @@ static int init_io_u(struct thread_data *td)
                memset(io_u, 0, sizeof(*io_u));
                INIT_LIST_HEAD(&io_u->list);
 
-               io_u->buf = p + td->bs * i;
-               io_u->buflen = td->bs;
-
+               io_u->buf = p + td->max_bs * i;
                list_add(&io_u->list, &td->io_u_freelist);
        }
 
@@ -901,8 +932,9 @@ static void finish_log(struct thread_data *td, struct io_log *log, char *name)
 
 static int create_file(struct thread_data *td)
 {
-       unsigned int i;
+       unsigned long long left;
        char *b;
+       int r, bs;
 
        /*
         * unless specifically asked for overwrite, let normal io extend it
@@ -922,16 +954,22 @@ static int create_file(struct thread_data *td)
                return 1;
        }
 
-       td->blocks = td->file_size / td->bs;
-       b = malloc(td->bs);
-       memset(b, 0, td->bs);
+       td->kb = td->file_size >> 10;
+       b = malloc(td->max_bs);
+       memset(b, 0, td->max_bs);
+
+       left = td->file_size;
+       while (left) {
+               bs = td->max_bs;
+               if (bs > left)
+                       bs = left;
 
-       for (i = 0; i < td->blocks; i++) {
-               int r = write(td->fd, b, td->bs);
+               r = write(td->fd, b, bs);
 
-               if (r == td->bs)
+               if (r == bs) {
+                       left -= bs;
                        continue;
-               else {
+               else {
                        if (r < 0)
                                td->error = errno;
                        else
@@ -1006,8 +1044,8 @@ static int setup_file(struct thread_data *td)
                st.st_size = td->file_size;
        }
 
-       td->blocks = (st.st_size - td->file_offset) / td->bs;
-       if (!td->blocks) {
+       td->kb = (st.st_size - td->file_offset) / 1024;
+       if (!td->kb) {
                fprintf(stderr, "Client%d: no io blocks\n", td->thread_number);
                td->error = EINVAL;
                return 1;
@@ -1144,7 +1182,7 @@ static void show_thread_status(struct thread_data *td)
                return;
 
        if (td->runtime)
-               bw = td->io_kb / td->runtime;
+               bw = td->io_kb * 1024 / td->runtime;
 
        prio = td->ioprio & 0xff;
        prio_class = td->ioprio >> IOPRIO_CLASS_SHIFT;
@@ -1171,7 +1209,7 @@ static int setup_rate(struct thread_data *td)
                return -1;
        }
 
-       nr_reads_per_sec = td->rate * 1024 / td->bs;
+       nr_reads_per_sec = td->rate * 1024 / td->min_bs;
        td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
        td->rate_pending_usleep = 0;
        return 0;
@@ -1253,7 +1291,12 @@ static int add_job(struct thread_data *td, const char *filename, int prioclass,
        if (write_bw_log)
                setup_log(&td->bw_log);
 
-       printf("Client%d: file=%s, rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, filename, td->ddir, prioclass, prio, td->sequential, td->odirect, td->bs, td->rate, td->use_aio, td->aio_depth);
+       if (td->min_bs == -1)
+               td->min_bs = td->bs;
+       if (td->max_bs == -1)
+               td->max_bs = td->bs;
+
+       printf("Client%d: file=%s, rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d-%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, filename, td->ddir, prioclass, prio, td->sequential, td->odirect, td->min_bs, td->max_bs, td->rate, td->use_aio, td->aio_depth);
        return 0;
 }
 
@@ -1282,12 +1325,28 @@ static void fill_option(const char *input, char *output)
        output[i] = '\0';
 }
 
+unsigned long get_mult(char c)
+{
+       switch (c) {
+               case 'k':
+               case 'K':
+                       return 1024;
+               case 'm':
+               case 'M':
+                       return 1024 * 1024;
+               case 'g':
+               case 'G':
+                       return 1024 * 1024 * 1024;
+               default:
+                       return 1;
+       }
+}
+
 /*
  * convert string after '=' into decimal value, noting any size suffix
  */
 static int str_cnv(char *p, unsigned long long *val)
 {
-       unsigned long mult;
        char *str;
        int len;
 
@@ -1297,30 +1356,13 @@ static int str_cnv(char *p, unsigned long long *val)
 
        str++;
        len = strlen(str);
-       mult = 1;
-
-       switch (str[len - 2]) {
-               case 'k':
-               case 'K':
-                       mult = 1024;
-                       break;
-               case 'm':
-               case 'M':
-                       mult = 1024 * 1024;
-                       break;
-               case 'g':
-               case 'G':
-                       mult = 1024 * 1024 * 1024;
-                       break;
-       }
 
        *val = strtoul(str, NULL, 10);
        if (*val == ULONG_MAX && errno == ERANGE)
                return 1;
 
-       *val *= mult;
+       *val *= get_mult(str[len - 2]);
        return 0;
-
 }
 
 /*
@@ -1556,6 +1598,37 @@ static int check_str(char *p, char *name, char *option)
        return 1;
 }
 
+static int check_range(char *p, char *name, unsigned long *s, unsigned long *e)
+{
+       char str[128];
+       char s1, s2;
+
+       sprintf(str, "%s=%%lu%%c-%%lu%%c", name);
+       if (sscanf(p, str, s, &s1, e, &s2) == 4) {
+               *s *= get_mult(s1);
+               *e *= get_mult(s2);
+               return 0;
+       }
+
+       sprintf(str, "%s = %%lu%%c-%%lu%%c", name);
+       if (sscanf(p, str, s, &s1, e, &s2) == 4) {
+               *s *= get_mult(s1);
+               *e *= get_mult(s2);
+               return 0;
+       }
+
+       sprintf(str, "%s=%%lu-%%lu", name);
+       if (sscanf(p, str, s, e) == 2)
+               return 0;
+
+       sprintf(str, "%s = %%lu-%%lu", name);
+       if (sscanf(p, str, s, e) == 2)
+               return 0;
+
+       return 1;
+
+}
+
 static int check_int(char *p, char *name, unsigned int *val)
 {
        char str[128];
@@ -1589,6 +1662,7 @@ static int parse_jobs_ini(char *file)
 {
        unsigned int prioclass, prio, cpu, global;
        unsigned long long ull;
+       unsigned long ul1, ul2;
        struct thread_data *td;
        char *string, *name;
        fpos_t off;
@@ -1688,6 +1762,12 @@ static int parse_jobs_ini(char *file)
                                fgetpos(f, &off);
                                continue;
                        }
+                       if (!check_range(p, "bsrange", &ul1, &ul2)) {
+                               td->min_bs = ul1;
+                               td->max_bs = ul2;
+                               fgetpos(f, &off);
+                               continue;
+                       }
                        if (!check_strcnv(p, "bs", &ull)) {
                                td->bs = ull;
                                fgetpos(f, &off);
@@ -1992,6 +2072,8 @@ int main(int argc, char *argv[])
         */
        def_thread.ddir = DDIR_READ;
        def_thread.bs = DEF_BS;
+       def_thread.min_bs = -1;
+       def_thread.max_bs = -1;
        def_thread.odirect = DEF_ODIRECT;
        def_thread.ratecycle = DEF_RATE_CYCLE;
        def_thread.sequential = DEF_SEQUENTIAL;
@@ -2032,7 +2114,7 @@ int main(int argc, char *argv[])
                        max_run[td->ddir] = td->runtime;
 
                if (td->runtime)
-                       bw = td->io_kb / td->runtime;
+                       bw = td->io_kb * 1024 / td->runtime;
                if (bw < min_bw[td->ddir])
                        min_bw[td->ddir] = bw;
                if (bw > max_bw[td->ddir])
@@ -2041,11 +2123,11 @@ int main(int argc, char *argv[])
                if (td_read(td)) {
                        read_mb += td->io_kb >> 10;
                        if (td->runtime)
-                               read_agg += td->io_kb / td->runtime;
+                               read_agg += td->io_kb * 1024 / td->runtime;
                } else {
                        write_mb += td->io_kb >> 10;
                        if (td->runtime)
-                               write_agg += td->io_kb / td->runtime;
+                               write_agg += td->io_kb * 1024 / td->runtime;
                }
 
 show_stat: