Add support for 'bssplit' option, fine grained block size contrl
authorJens Axboe <jens.axboe@oracle.com>
Fri, 14 Dec 2007 11:21:19 +0000 (12:21 +0100)
committerJens Axboe <jens.axboe@oracle.com>
Fri, 14 Dec 2007 11:21:19 +0000 (12:21 +0100)
From the HOWTO addition:

bssplit=str Sometimes you want even finer grained control of the
block sizes issued, not just an even split between them.
This option allows you to weight various block sizes,
so that you are able to define a specific amount of
block sizes issued. The format for this option is:

bssplit=blocksize/percentage:blocksize/percentage

for as many block sizes as needed. So if you want to define
a workload that has 50% 64k blocks, 10% 4k blocks, and
40% 32k blocks, you would write:

bssplit=4k/10:64k/50:32k/40

Ordering does not matter. If the percentage is left blank,
fio will fill in the remaining values evenly. So a bssplit
option like this one:

bssplit=4k/50:1k/:32k/

would have 50% 4k ios, and 25% 1k and 32k ios. The percentages
always add up to 100, if bssplit is given a range that adds
up to more, it will error out.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
HOWTO
fio.h
io_u.c
options.c
parse.c
parse.h

diff --git a/HOWTO b/HOWTO
index d5fb52cfb0f9f931db588c78d92e81a95480f2ca..569b998f215b78d41a36ac71d78f1506a013af4e 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -281,6 +281,30 @@ bsrange=irange     Instead of giving a single block size, specify a range
                writes, however a second range can be given after a comma.
                See bs=.
 
                writes, however a second range can be given after a comma.
                See bs=.
 
+bssplit=str    Sometimes you want even finer grained control of the
+               block sizes issued, not just an even split between them.
+               This option allows you to weight various block sizes,
+               so that you are able to define a specific amount of
+               block sizes issued. The format for this option is:
+
+                       bssplit=blocksize/percentage:blocksize/percentage
+
+               for as many block sizes as needed. So if you want to define
+               a workload that has 50% 64k blocks, 10% 4k blocks, and
+               40% 32k blocks, you would write:
+
+                       bssplit=4k/10:64k/50:32k/40
+
+               Ordering does not matter. If the percentage is left blank,
+               fio will fill in the remaining values evenly. So a bssplit
+               option like this one:
+
+                       bssplit=4k/50:1k/:32k/
+
+               would have 50% 4k ios, and 25% 1k and 32k ios. The percentages
+               always add up to 100, if bssplit is given a range that adds
+               up to more, it will error out.
+
 blocksize_unaligned
 bs_unaligned   If this option is given, any byte size value within bsrange
                may be used as a block range. This typically wont work with
 blocksize_unaligned
 bs_unaligned   If this option is given, any byte size value within bsrange
                may be used as a block range. This typically wont work with
diff --git a/fio.h b/fio.h
index ca9fede63f66962f8d3ea4e171e8991c97ee57ea..ca6e2904782000d678d0a7d5482c784e13ab1573 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -373,6 +373,11 @@ struct thread_stat {
        unsigned long total_run_time;
 };
 
        unsigned long total_run_time;
 };
 
+struct bssplit {
+       unsigned int bs;
+       unsigned char perc;
+};
+
 struct thread_options {
        int pad;
        char *description;
 struct thread_options {
        int pad;
        char *description;
@@ -395,6 +400,8 @@ struct thread_options {
        unsigned int bs[2];
        unsigned int min_bs[2];
        unsigned int max_bs[2];
        unsigned int bs[2];
        unsigned int min_bs[2];
        unsigned int max_bs[2];
+       struct bssplit *bssplit;
+       unsigned int bssplit_nr;
 
        unsigned int nr_files;
        unsigned int open_files;
 
        unsigned int nr_files;
        unsigned int open_files;
diff --git a/io_u.c b/io_u.c
index 93c451cb9bd9c69038ae20e45d5fc4f4d30af1fc..7890a87099c26f7d5802f9a0479e8ddc6d768bad 100644 (file)
--- a/io_u.c
+++ b/io_u.c
@@ -186,7 +186,21 @@ static unsigned int get_next_buflen(struct thread_data *td, struct io_u *io_u)
                buflen = td->o.min_bs[ddir];
        else {
                r = os_random_long(&td->bsrange_state);
                buflen = td->o.min_bs[ddir];
        else {
                r = os_random_long(&td->bsrange_state);
-               buflen = (unsigned int) (1 + (double) (td->o.max_bs[ddir] - 1) * r / (RAND_MAX + 1.0));
+               if (!td->o.bssplit_nr)
+                       buflen = (unsigned int) (1 + (double) (td->o.max_bs[ddir] - 1) * r / (RAND_MAX + 1.0));
+               else {
+                       long perc = 0;
+                       unsigned int i;
+
+                       for (i = 0; i < td->o.bssplit_nr; i++) {
+                               struct bssplit *bsp = &td->o.bssplit[i];
+
+                               buflen = bsp->bs;
+                               perc += bsp->perc;
+                               if (r <= ((LONG_MAX / 100L) * perc))
+                                       break;
+                       }
+               }
                if (!td->o.bs_unaligned)
                        buflen = (buflen + td->o.min_bs[ddir] - 1) & ~(td->o.min_bs[ddir] - 1);
        }
                if (!td->o.bs_unaligned)
                        buflen = (buflen + td->o.min_bs[ddir] - 1) & ~(td->o.min_bs[ddir] - 1);
        }
index c56b9dfb63e8795e6b2dd623add21cb6c0c99a4e..055fbab7a14180a91062635a4176a9603b0fd8d2 100644 (file)
--- a/options.c
+++ b/options.c
@@ -28,6 +28,120 @@ static char *get_opt_postfix(const char *str)
        return strdup(p);
 }
 
        return strdup(p);
 }
 
+static int bs_cmp(const void *p1, const void *p2)
+{
+       const struct bssplit *bsp1 = p1;
+       const struct bssplit *bsp2 = p2;
+
+       return bsp1->perc < bsp2->perc;
+}
+
+static int str_bssplit_cb(void *data, const char *input)
+{
+       struct thread_data *td = data;
+       char *fname, *str, *p;
+       unsigned int i, perc, perc_missing;
+       unsigned int max_bs, min_bs;
+       long long val;
+
+       p = str = strdup(input);
+
+       strip_blank_front(&str);
+       strip_blank_end(str);
+
+       td->o.bssplit_nr = 4;
+       td->o.bssplit = malloc(4 * sizeof(struct bssplit));
+
+       i = 0;
+       max_bs = 0;
+       min_bs = -1;
+       while ((fname = strsep(&str, ":")) != NULL) {
+               char *perc_str;
+
+               if (!strlen(fname))
+                       break;
+
+               /*
+                * grow struct buffer, if needed
+                */
+               if (i == td->o.bssplit_nr) {
+                       td->o.bssplit_nr <<= 1;
+                       td->o.bssplit = realloc(td->o.bssplit, td->o.bssplit_nr * sizeof(struct bssplit));
+               }
+
+               perc_str = strstr(fname, "/");
+               if (perc_str) {
+                       *perc_str = '\0';
+                       perc_str++;
+                       perc = atoi(perc_str);
+                       if (perc > 100)
+                               perc = 100;
+                       else if (!perc)
+                               perc = -1;
+               } else
+                       perc = -1;
+
+               if (str_to_decimal(fname, &val, 1)) {
+                       log_err("fio: bssplit conversion failed\n");
+                       free(td->o.bssplit);
+                       return 1;
+               }
+
+               if (val > max_bs)
+                       max_bs = val;
+               if (val < min_bs)
+                       min_bs = val;
+
+               td->o.bssplit[i].bs = val;
+               td->o.bssplit[i].perc = perc;
+               i++;
+       }
+
+       td->o.bssplit_nr = i;
+
+       /*
+        * Now check if the percentages add up, and how much is missing
+        */
+       perc = perc_missing = 0;
+       for (i = 0; i < td->o.bssplit_nr; i++) {
+               struct bssplit *bsp = &td->o.bssplit[i];
+
+               if (bsp->perc == (unsigned char) -1)
+                       perc_missing++;
+               else
+                       perc += bsp->perc;
+       }
+
+       if (perc > 100) {
+               log_err("fio: bssplit percentages add to more than 100%%\n");
+               free(td->o.bssplit);
+               return 1;
+       }
+       /*
+        * If values didn't have a percentage set, divide the remains between
+        * them.
+        */
+       if (perc_missing) {
+               for (i = 0; i < td->o.bssplit_nr; i++) {
+                       struct bssplit *bsp = &td->o.bssplit[i];
+
+                       if (bsp->perc == (unsigned char) -1)
+                               bsp->perc = (100 - perc) / perc_missing;
+               }
+       }
+
+       td->o.min_bs[DDIR_READ] = td->o.min_bs[DDIR_WRITE] = min_bs;
+       td->o.max_bs[DDIR_READ] = td->o.max_bs[DDIR_WRITE] = max_bs;
+
+       /*
+        * now sort based on percentages, for ease of lookup
+        */
+       qsort(td->o.bssplit, td->o.bssplit_nr, sizeof(struct bssplit), bs_cmp);
+
+       free(p);
+       return 0;
+}
+
 static int str_rw_cb(void *data, const char *str)
 {
        struct thread_data *td = data;
 static int str_rw_cb(void *data, const char *str)
 {
        struct thread_data *td = data;
@@ -459,6 +573,13 @@ static struct fio_option options[] = {
                .help   = "Set block size range (in more detail than bs)",
                .parent = "rw",
        },
                .help   = "Set block size range (in more detail than bs)",
                .parent = "rw",
        },
+       {
+               .name   = "bssplit",
+               .type   = FIO_OPT_STR,
+               .cb     = str_bssplit_cb,
+               .help   = "Set a specific mix of block sizes",
+               .parent = "rw",
+       },
        {
                .name   = "bs_unaligned",
                .alias  = "blocksize_unaligned",
        {
                .name   = "bs_unaligned",
                .alias  = "blocksize_unaligned",
diff --git a/parse.c b/parse.c
index 879a3aa3c5b190bf63b37dd4b7f90f2be2fd5f83..f907a349ff24931044592a3c69ada66672eab722 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -106,7 +106,7 @@ static unsigned long get_mult_bytes(char c)
 /*
  * convert string into decimal value, noting any size suffix
  */
 /*
  * convert string into decimal value, noting any size suffix
  */
-static int str_to_decimal(const char *str, long long *val, int kilo)
+int str_to_decimal(const char *str, long long *val, int kilo)
 {
        int len;
 
 {
        int len;
 
diff --git a/parse.h b/parse.h
index c3b66d75be34216ff1f3df33b85db9d7779ef6c6..42046b5e96d25047889295c3113ab6d82069b5d7 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -58,6 +58,7 @@ extern void options_init(struct fio_option *);
 
 extern void strip_blank_front(char **);
 extern void strip_blank_end(char *);
 
 extern void strip_blank_front(char **);
 extern void strip_blank_end(char *);
+extern int str_to_decimal(const char *, long long *, int);
 
 /*
  * Handlers for the options
 
 /*
  * Handlers for the options