The following changes since commit 55ed9636e82b8dee419b5a76c07098bff4d980b6: Fix parser bug dealing with range options and postfix (2011-03-27 20:55:09 +0200) are available in the git repository at: git://git.kernel.dk/fio.git master Jens Axboe (3): Switch to using our internal Tausworthe based random generator for offsets Switch the latter part of the offset generation to __rand() too Switch all random generators to be decided by use_os_rand HOWTO | 6 ++++++ filesetup.c | 13 ++++++++++--- fio.1 | 6 ++++++ fio.h | 33 +++++++++++++++++++++++++++------ init.c | 31 ++++++++++++++++++++++++++++++- io_u.c | 57 +++++++++++++++++++++++++++++++++++++++++++++------------ lib/rand.c | 32 ++++++++++++++++++++------------ lib/rand.h | 3 +++ options.c | 8 ++++++++ trim.c | 11 ++++++++--- 10 files changed, 163 insertions(+), 37 deletions(-) --- Diff of recent changes: diff --git a/HOWTO b/HOWTO index d4e7092..69b8cc6 100644 --- a/HOWTO +++ b/HOWTO @@ -348,6 +348,12 @@ kb_base=int The base unit for a kilobyte. The defacto base is 2^10, 1024. randrepeat=bool For random IO workloads, seed the generator in a predictable way so that results are repeatable across repetitions. +use_os_rand=bool Fio can either use the random generator supplied by the OS + to generator random offsets, or it can use it's own internal + generator (based on Tausworthe). Default is to use the + internal generator, which is often of better quality and + faster. + fallocate=bool By default, fio will use fallocate() to advise the system of the size of the file we are going to write. This can be turned off with fallocate=0. May not be available on all diff --git a/filesetup.c b/filesetup.c index 0454cc4..2b690c8 100644 --- a/filesetup.c +++ b/filesetup.c @@ -210,9 +210,16 @@ static unsigned long long get_rand_file_size(struct thread_data *td) unsigned long long ret, sized; long r; - r = os_random_long(&td->file_size_state); - sized = td->o.file_size_high - td->o.file_size_low; - ret = (unsigned long long) ((double) sized * (r / (OS_RAND_MAX + 1.0))); + if (td->o.use_os_rand) { + r = os_random_long(&td->file_size_state); + sized = td->o.file_size_high - td->o.file_size_low; + ret = (unsigned long long) ((double) sized * (r / (OS_RAND_MAX + 1.0))); + } else { + r = __rand(&td->__file_size_state); + sized = td->o.file_size_high - td->o.file_size_low; + ret = (unsigned long long) ((double) sized * (r / (FRAND_MAX + 1.0))); + } + ret += td->o.file_size_low; ret -= (ret % td->o.rw_min_bs); return ret; diff --git a/fio.1 b/fio.1 index a8c0027..0ced604 100644 --- a/fio.1 +++ b/fio.1 @@ -214,6 +214,12 @@ reasons. Allow values are 1024 or 1000, with 1024 being the default. Seed the random number generator in a predictable way so results are repeatable across runs. Default: true. .TP +.BI use_os_rand \fR=\fPbool +Fio can either use the random generator supplied by the OS to generator random +offsets, or it can use it's own internal generator (based on Tausworthe). +Default is to use the internal generator, which is often of better quality and +faster. Default: false. +.TP .BI fallocate \fR=\fPbool By default, fio will use fallocate() to advise the system of the size of the file we are going to write. This can be turned off with fallocate=0. May not diff --git a/fio.h b/fio.h index 08f1733..c0087e8 100644 --- a/fio.h +++ b/fio.h @@ -34,6 +34,7 @@ struct thread_data; #include "profile.h" #include "time.h" #include "lib/getopt.h" +#include "lib/rand.h" #ifdef FIO_HAVE_GUASI #include <guasi.h> @@ -206,6 +207,7 @@ struct thread_options { unsigned int do_disk_util; unsigned int override_sync; unsigned int rand_repeatable; + unsigned int use_os_rand; unsigned int write_lat_log; unsigned int write_bw_log; unsigned int norandommap; @@ -334,6 +336,7 @@ struct thread_data { union { unsigned int next_file; os_random_state_t next_file_state; + struct frand_state __next_file_state; }; int error; int done; @@ -357,9 +360,18 @@ struct thread_data { unsigned long rand_seeds[7]; - os_random_state_t bsrange_state; - os_random_state_t verify_state; - os_random_state_t trim_state; + union { + os_random_state_t bsrange_state; + struct frand_state __bsrange_state; + }; + union { + os_random_state_t verify_state; + struct frand_state __verify_state; + }; + union { + os_random_state_t trim_state; + struct frand_state __trim_state; + }; unsigned int verify_batch; unsigned int trim_batch; @@ -415,7 +427,10 @@ struct thread_data { /* * State for random io, a bitmap of blocks done vs not done */ - os_random_state_t random_state; + union { + os_random_state_t random_state; + struct frand_state __random_state; + }; struct timeval start; /* start of this loop */ struct timeval epoch; /* time job was started */ @@ -428,7 +443,10 @@ struct thread_data { /* * read/write mixed workload state */ - os_random_state_t rwmix_state; + union { + os_random_state_t rwmix_state; + struct frand_state __rwmix_state; + }; unsigned long rwmix_issues; enum fio_ddir rwmix_ddir; unsigned int ddir_seq_nr; @@ -464,7 +482,10 @@ struct thread_data { /* * For generating file sizes */ - os_random_state_t file_size_state; + union { + os_random_state_t file_size_state; + struct frand_state __file_size_state; + }; /* * Error counts diff --git a/init.c b/init.c index 327a3c5..b96350a 100644 --- a/init.c +++ b/init.c @@ -463,7 +463,7 @@ static int exists_and_not_file(const char *filename) return 1; } -void td_fill_rand_seeds(struct thread_data *td) +static void td_fill_rand_seeds_os(struct thread_data *td) { os_random_seed(td->rand_seeds[0], &td->bsrange_state); os_random_seed(td->rand_seeds[1], &td->verify_state); @@ -484,6 +484,35 @@ void td_fill_rand_seeds(struct thread_data *td) os_random_seed(td->rand_seeds[4], &td->random_state); } +static void td_fill_rand_seeds_internal(struct thread_data *td) +{ + init_rand_seed(&td->__bsrange_state, td->rand_seeds[0]); + init_rand_seed(&td->__verify_state, td->rand_seeds[1]); + init_rand_seed(&td->__rwmix_state, td->rand_seeds[2]); + + if (td->o.file_service_type == FIO_FSERVICE_RANDOM) + init_rand_seed(&td->__next_file_state, td->rand_seeds[3]); + + init_rand_seed(&td->__file_size_state, td->rand_seeds[5]); + init_rand_seed(&td->__trim_state, td->rand_seeds[6]); + + if (!td_random(td)) + return; + + if (td->o.rand_repeatable) + td->rand_seeds[4] = FIO_RANDSEED * td->thread_number; + + init_rand_seed(&td->__random_state, td->rand_seeds[4]); +} + +void td_fill_rand_seeds(struct thread_data *td) +{ + if (td->o.use_os_rand) + td_fill_rand_seeds_os(td); + else + td_fill_rand_seeds_internal(td); +} + /* * Initialize the various random states we need (random io, block size ranges, * read/write mix, etc). diff --git a/io_u.c b/io_u.c index 5a3ca74..787f382 100644 --- a/io_u.c +++ b/io_u.c @@ -168,9 +168,16 @@ static int get_next_rand_offset(struct thread_data *td, struct fio_file *f, goto ffz; do { - r = os_random_long(&td->random_state); + if (td->o.use_os_rand) { + r = os_random_long(&td->random_state); + *b = (lastb - 1) * (r / ((unsigned long long) OS_RAND_MAX + 1.0)); + } else { + r = __rand(&td->__random_state); + *b = (lastb - 1) * (r / ((unsigned long long) FRAND_MAX + 1.0)); + } + dprint(FD_RANDOM, "off rand %llu\n", r); - *b = (lastb - 1) * (r / ((unsigned long long) OS_RAND_MAX + 1.0)); + /* * if we are not maintaining a random map, we are done. @@ -203,7 +210,10 @@ static int get_next_rand_offset(struct thread_data *td, struct fio_file *f, if (!get_next_free_block(td, f, ddir, b)) goto ret; - r = os_random_long(&td->random_state); + if (td->o.use_os_rand) + r = os_random_long(&td->random_state); + else + r = __rand(&td->__random_state); } while (--loops); /* @@ -336,20 +346,29 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u) const int ddir = io_u->ddir; unsigned int uninitialized_var(buflen); unsigned int minbs, maxbs; - long r; + long r, rand_max; assert(ddir_rw(ddir)); minbs = td->o.min_bs[ddir]; maxbs = td->o.max_bs[ddir]; + if (td->o.use_os_rand) + rand_max = OS_RAND_MAX; + else + rand_max = FRAND_MAX; + if (minbs == maxbs) buflen = minbs; else { - r = os_random_long(&td->bsrange_state); + if (td->o.use_os_rand) + r = os_random_long(&td->bsrange_state); + else + r = __rand(&td->__bsrange_state); + if (!td->o.bssplit_nr[ddir]) { buflen = 1 + (unsigned int) ((double) maxbs * - (r / (OS_RAND_MAX + 1.0))); + (r / (rand_max + 1.0))); if (buflen < minbs) buflen = minbs; } else { @@ -361,7 +380,7 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u) buflen = bsp->bs; perc += bsp->perc; - if (r <= ((OS_RAND_MAX / 100L) * perc)) + if (r <= ((rand_max / 100L) * perc)) break; } } @@ -406,8 +425,14 @@ static inline enum fio_ddir get_rand_ddir(struct thread_data *td) unsigned int v; long r; - r = os_random_long(&td->rwmix_state); - v = 1 + (int) (100.0 * (r / (OS_RAND_MAX + 1.0))); + if (td->o.use_os_rand) { + r = os_random_long(&td->rwmix_state); + v = 1 + (int) (100.0 * (r / (OS_RAND_MAX + 1.0))); + } else { + r = __rand(&td->__rwmix_state); + v = 1 + (int) (100.0 * (r / (FRAND_MAX + 1.0))); + } + if (v <= td->o.rwmix[DDIR_READ]) return DDIR_READ; @@ -823,11 +848,19 @@ static struct fio_file *get_next_file_rand(struct thread_data *td, int fno; do { - long r = os_random_long(&td->next_file_state); int opened = 0; + long r; + + if (td->o.use_os_rand) { + r = os_random_long(&td->next_file_state); + fno = (unsigned int) ((double) td->o.nr_files + * (r / (OS_RAND_MAX + 1.0))); + } else { + r = __rand(&td->__next_file_state); + fno = (unsigned int) ((double) td->o.nr_files + * (r / (FRAND_MAX + 1.0))); + } - fno = (unsigned int) ((double) td->o.nr_files - * (r / (OS_RAND_MAX + 1.0))); f = td->files[fno]; if (fio_file_done(f)) continue; diff --git a/lib/rand.c b/lib/rand.c index 0681282..3b2d67a 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -43,20 +43,28 @@ static inline int __seed(unsigned int x, unsigned int m) return (x < m) ? x + m : x; } +static void __init_rand(struct frand_state *state, unsigned int seed) +{ + int cranks = 6; + +#define LCG(x, seed) ((x) * 69069 ^ (seed)) + + state->s1 = __seed(LCG((2^31) + (2^17) + (2^7), seed), 1); + state->s2 = __seed(LCG(state->s1, seed), 7); + state->s3 = __seed(LCG(state->s2, seed), 15); + + while (cranks--) + __rand(state); +} + void init_rand(struct frand_state *state) { -#define LCG(x) ((x) * 69069) /* super-duper LCG */ - - state->s1 = __seed(LCG((2^31) + (2^17) + (2^7)), 1); - state->s2 = __seed(LCG(state->s1), 7); - state->s3 = __seed(LCG(state->s2), 15); - - __rand(state); - __rand(state); - __rand(state); - __rand(state); - __rand(state); - __rand(state); + __init_rand(state, 1); +} + +void init_rand_seed(struct frand_state *state, unsigned int seed) +{ + __init_rand(state, seed); } void __fill_random_buf(void *buf, unsigned int len, unsigned long seed) diff --git a/lib/rand.h b/lib/rand.h index 02e6858..f80c111 100644 --- a/lib/rand.h +++ b/lib/rand.h @@ -1,6 +1,8 @@ #ifndef FIO_RAND_H #define FIO_RAND_H +#define FRAND_MAX (-1U) + struct frand_state { unsigned int s1, s2, s3; }; @@ -19,6 +21,7 @@ static inline unsigned int __rand(struct frand_state *state) } extern void init_rand(struct frand_state *); +extern void init_rand_seed(struct frand_state *, unsigned int seed); extern void __fill_random_buf(void *buf, unsigned int len, unsigned long seed); extern unsigned long fill_random_buf(void *buf, unsigned int len); diff --git a/options.c b/options.c index 55d11ae..28a17cf 100644 --- a/options.c +++ b/options.c @@ -1116,6 +1116,14 @@ static struct fio_option options[FIO_MAX_OPTS] = { .parent = "rw", }, { + .name = "use_os_rand", + .type = FIO_OPT_BOOL, + .off1 = td_var_offset(use_os_rand), + .help = "Set to use OS random generator", + .def = "0", + .parent = "rw", + }, + { .name = "norandommap", .type = FIO_OPT_STR_SET, .off1 = td_var_offset(norandommap), diff --git a/trim.c b/trim.c index 3da1e74..a9b15d6 100644 --- a/trim.c +++ b/trim.c @@ -75,10 +75,15 @@ int io_u_should_trim(struct thread_data *td, struct io_u *io_u) if (!td->o.trim_percentage) return 0; - r = os_random_long(&td->trim_state); - val = (OS_RAND_MAX / 100ULL); - val *= (unsigned long long) td->o.trim_percentage; + if (td->o.use_os_rand) { + r = os_random_long(&td->trim_state); + val = (OS_RAND_MAX / 100ULL); + } else { + r = __rand(&td->__trim_state); + val = (FRAND_MAX / 100ULL); + } + val *= (unsigned long long) td->o.trim_percentage; return r <= val; } #endif -- To unsubscribe from this list: send the line "unsubscribe fio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html