Add support for modifying the randomness of a workload
authorJens Axboe <axboe@kernel.dk>
Fri, 26 Apr 2013 14:56:17 +0000 (08:56 -0600)
committerJens Axboe <axboe@kernel.dk>
Fri, 26 Apr 2013 14:56:17 +0000 (08:56 -0600)
Fio supports random or sequential IO, or random IO with a number of
sequential IOs in between. The percentage_random options allows
more fine grained control over this. It defaults to 100, which would
make any random workload still purely random. If set below 100,
there will be some percentage of sequential IOs. This happens randomly,
but at the specified percentages.

The percentage_sequential option is just the complement of that, it
is guaranteed that:

percentage_sequential + percentage_random == 100%

The later setting has priority, each will adjust the other.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
HOWTO
cconv.c
fio.1
fio.h
init.c
io_u.c
options.c
thread_options.h

diff --git a/HOWTO b/HOWTO
index 64453489175aec0346e8e8f7df9422849582fc12..8211c3b48166cd6f03e4f32d9beddf57cf1e03e2 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -766,6 +766,17 @@ random_distribution=str:float      By default, fio will use a completely uniform
                random_distribution=zipf:1.2 as the option. If a non-uniform
                model is used, fio will disable use of the random map.
 
+percentage_random=int  For a random workload, set how big a percentage should
+               be random. This defaults to 100%, in which case the workload
+               is fully random. It can be set from anywhere from 0 to 100.
+               Setting it to 0 would make the workload fully sequential. Any
+               setting in between will result in a random mix of sequential
+               and random IO, at the given percentages.
+       
+percentage_sequential=int      See percentage_random. It is guaranteed that
+               they add up to 100. The later setting has priority, each
+               will adjust the other.
+
 norandommap    Normally fio will cover every block of the file when doing
                random IO. If this option is given, fio will just get a
                new random offset without looking at past io history. This
diff --git a/cconv.c b/cconv.c
index 57c76e32f4d3b96fedbb3330c9d2b7cbabdae4f7..c2b27938274b57f36e240af6bff7ff86c7c3c24f 100644 (file)
--- a/cconv.c
+++ b/cconv.c
@@ -125,6 +125,8 @@ void convert_thread_options_to_cpu(struct thread_options *o,
        o->zipf_theta.u.f = fio_uint64_to_double(le64_to_cpu(top->zipf_theta.u.i));
        o->pareto_h.u.f = fio_uint64_to_double(le64_to_cpu(top->pareto_h.u.i));
        o->random_generator = le32_to_cpu(top->random_generator);
+       o->perc_rand = le32_to_cpu(top->perc_rand);
+       o->perc_seq = le32_to_cpu(top->perc_seq);
        o->hugepage_size = le32_to_cpu(top->hugepage_size);
        o->rw_min_bs = le32_to_cpu(top->rw_min_bs);
        o->thinktime = le32_to_cpu(top->thinktime);
@@ -283,6 +285,8 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        top->zipf_theta.u.i = __cpu_to_le64(fio_double_to_uint64(o->zipf_theta.u.f));
        top->pareto_h.u.i = __cpu_to_le64(fio_double_to_uint64(o->pareto_h.u.f));
        top->random_generator = cpu_to_le32(o->random_generator);
+       top->perc_rand = cpu_to_le32(o->perc_rand);
+       top->perc_seq = cpu_to_le32(o->perc_seq);
        top->hugepage_size = cpu_to_le32(o->hugepage_size);
        top->rw_min_bs = cpu_to_le32(o->rw_min_bs);
        top->thinktime = cpu_to_le32(o->thinktime);
diff --git a/fio.1 b/fio.1
index 401d9566632b9ca47944c98e3e81ede8fac982fd..5082bf890fe519eb35f273f047132bed4f88a27d 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -641,6 +641,15 @@ If you wanted to use zipf with a theta of 1.2, you would use
 random_distribution=zipf:1.2 as the option. If a non-uniform model is used,
 fio will disable use of the random map.
 .TP
+.BI percentage_random \fR=\fPint
+For a random workload, set how big a percentage should be random. This defaults
+to 100%, in which case the workload is fully random. It can be set from
+anywhere from 0 to 100.  Setting it to 0 would make the workload fully
+sequential.
+.TP
+.BI percentage_sequential \fR=\fPint
+See \fBpercentage_random\fR.
+.TP
 .B norandommap
 Normally \fBfio\fR will cover every block of the file when doing random I/O. If
 this parameter is given, a new offset will be chosen without looking at past
diff --git a/fio.h b/fio.h
index 5438b768da8f781d7f1bd19b4c955de89039b39a..965d7d9c7a4c0804396e91c149a3165381b7bff7 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -81,6 +81,7 @@ enum {
        FIO_RAND_FILE_SIZE_OFF,
        FIO_RAND_TRIM_OFF,
        FIO_RAND_BUF_OFF,
+       FIO_RAND_SEQ_RAND_OFF,
        FIO_RAND_NR_OFFS,
 };
 
@@ -255,6 +256,14 @@ struct thread_data {
        enum fio_ddir rwmix_ddir;
        unsigned int ddir_seq_nr;
 
+       /*
+        * rand/seq mixed workload state
+        */
+       union {
+               os_random_state_t seq_rand_state;
+               struct frand_state __seq_rand_state;
+       };
+
        /*
         * IO history logs for verification. We use a tree for sorting,
         * if we are overwriting. Otherwise just use a fifo.
diff --git a/init.c b/init.c
index aba7671c1402ff953dc5549e5303b0b9954c2193..7246bd83c268dacac75c6de94027227bb1e7fb72 100644 (file)
--- a/init.c
+++ b/init.c
@@ -701,6 +701,7 @@ static void td_fill_rand_seeds_os(struct thread_data *td)
                td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
        os_random_seed(td->rand_seeds[FIO_RAND_BLOCK_OFF], &td->random_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_OFF], &td->seq_rand_state);
 }
 
 static void td_fill_rand_seeds_internal(struct thread_data *td)
@@ -722,6 +723,7 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
                td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
        init_rand_seed(&td->__random_state, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
+       init_rand_seed(&td->__seq_rand_state, td->rand_seeds[FIO_RAND_SEQ_RAND_OFF]);
 }
 
 void td_fill_rand_seeds(struct thread_data *td)
diff --git a/io_u.c b/io_u.c
index 19ef7b9f30fe0f2888d16e2ff1261fd99788b304..d03049e52332e2b01510364863af812ecfd8e496 100644 (file)
--- a/io_u.c
+++ b/io_u.c
@@ -191,6 +191,25 @@ static inline int should_sort_io(struct thread_data *td)
        return 1;
 }
 
+static int should_do_random(struct thread_data *td)
+{
+       unsigned int v;
+       unsigned long r;
+
+       if (td->o.perc_rand == 100)
+               return 1;
+
+       if (td->o.use_os_rand) {
+               r = os_random_long(&td->seq_rand_state);
+               v = 1 + (int) (100.0 * (r / (OS_RAND_MAX + 1.0)));
+       } else {
+               r = __rand(&td->__seq_rand_state);
+               v = 1 + (int) (100.0 * (r / (FRAND_MAX + 1.0)));
+       }
+
+       return v <= td->o.perc_rand;
+}
+
 static int get_next_rand_offset(struct thread_data *td, struct fio_file *f,
                                enum fio_ddir ddir, uint64_t *b)
 {
@@ -285,9 +304,16 @@ static int get_next_block(struct thread_data *td, struct io_u *io_u,
        b = offset = -1ULL;
 
        if (rw_seq) {
-               if (td_random(td))
-                       ret = get_next_rand_block(td, f, ddir, &b);
-               else
+               if (td_random(td)) {
+                       if (should_do_random(td))
+                               ret = get_next_rand_block(td, f, ddir, &b);
+                       else {
+                               io_u->flags |= IO_U_F_BUSY_OK;
+                               ret = get_next_seq_offset(td, f, ddir, &offset);
+                               if (ret)
+                                       ret = get_next_rand_block(td, f, ddir, &b);
+                       }
+               } else
                        ret = get_next_seq_offset(td, f, ddir, &offset);
        } else {
                io_u->flags |= IO_U_F_BUSY_OK;
index 1219803a73a1e9880698aa8ecf387f1426e7bdba..97c5b6f93669170981312e58e62f59d98c7730c9 100644 (file)
--- a/options.c
+++ b/options.c
@@ -376,6 +376,25 @@ static int str_rwmix_write_cb(void *data, unsigned long long *val)
        return 0;
 }
 
+static int str_perc_rand_cb(void *data, unsigned long long *val)
+{
+       struct thread_data *td = data;
+
+       td->o.perc_rand = *val;
+       td->o.perc_seq = 100 - *val;
+       return 0;
+}
+
+static int str_perc_seq_cb(void *data, unsigned long long *val)
+{
+       struct thread_data *td = data;
+
+       td->o.perc_seq = *val;
+       td->o.perc_rand = 100 - *val;
+       return 0;
+}
+
+
 static int str_exitall_cb(void)
 {
        exitall_on_terminate = 1;
@@ -1642,6 +1661,32 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RANDOM,
        },
+       {
+               .name   = "percentage_random",
+               .lname  = "Percentage Random",
+               .type   = FIO_OPT_INT,
+               .cb     = str_perc_rand_cb,
+               .maxval = 100,
+               .help   = "Percentage of seq/random mix that should be random",
+               .def    = "100",
+               .interval = 5,
+               .inverse = "percentage_sequential",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RANDOM,
+       },
+       {
+               .name   = "percentage_sequential",
+               .lname  = "Percentage Sequential",
+               .type   = FIO_OPT_INT,
+               .cb     = str_perc_seq_cb,
+               .maxval = 100,
+               .help   = "Percentage of seq/random mix that should be sequential",
+               .def    = "0",
+               .interval = 5,
+               .inverse = "percentage_random",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RANDOM,
+       },
        {
                .name   = "nrfiles",
                .lname  = "Number of files",
index f25988ac1922a97fa23468678bb742de1fe0dcfa..138d0265b0429c8686f91e5ef50d70725ef69aaa 100644 (file)
@@ -113,6 +113,9 @@ struct thread_options {
 
        unsigned int random_generator;
 
+       unsigned int perc_rand;
+       unsigned int perc_seq;
+
        unsigned int hugepage_size;
        unsigned int rw_min_bs;
        unsigned int thinktime;
@@ -322,6 +325,9 @@ struct thread_options_pack {
 
        uint32_t random_generator;
 
+       uint32_t perc_rand;
+       uint32_t perc_seq;
+
        uint32_t hugepage_size;
        uint32_t rw_min_bs;
        uint32_t thinktime;