From 211c9b8948b43ed47aedf1227e0444a58db015e4 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 26 Apr 2013 08:56:17 -0600 Subject: [PATCH] Add support for modifying the randomness of a workload 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 --- HOWTO | 11 +++++++++++ cconv.c | 4 ++++ fio.1 | 9 +++++++++ fio.h | 9 +++++++++ init.c | 2 ++ io_u.c | 32 +++++++++++++++++++++++++++++--- options.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ thread_options.h | 6 ++++++ 8 files changed, 115 insertions(+), 3 deletions(-) diff --git a/HOWTO b/HOWTO index 64453489..8211c3b4 100644 --- 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 57c76e32..c2b27938 100644 --- 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 401d9566..5082bf89 100644 --- 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 5438b768..965d7d9c 100644 --- 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 aba7671c..7246bd83 100644 --- 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 19ef7b9f..d03049e5 100644 --- 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; diff --git a/options.c b/options.c index 1219803a..97c5b6f9 100644 --- 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", diff --git a/thread_options.h b/thread_options.h index f25988ac..138d0265 100644 --- a/thread_options.h +++ b/thread_options.h @@ -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; -- 2.25.1