From 7889f07b70d88f58e9d4dd2901a0c96ec2ab3e46 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 27 Oct 2005 12:37:39 +0200 Subject: [PATCH] [PATCH] Add support for the bsrange=x-y option, mixing thread block sizes --- README.fio | 1 + fio.c | 194 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 139 insertions(+), 56 deletions(-) diff --git a/README.fio b/README.fio index 9b2c2ef..fecf78d 100644 --- a/README.fio +++ b/README.fio @@ -29,6 +29,7 @@ The 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 0bc22e8..07b08a2 100644 --- 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: -- 2.25.1