Skip to content

Commit

Permalink
Add support for zones of random IO, with varying frequency of access
Browse files Browse the repository at this point in the history
Let's say you want to ensure that 50% of the IO falls in the first 5%
of the flie, with the remaining 50% over the last 95%. Now that's
possible with random_distribution=zoned. For this particular case,
you would do:

random_distribution=zoned:50/5:50/95

Up to 64 ranges can be specified, and multiple data directions can
be given as well. The above would apply to both reads, writes, and
trims. If you wanted to have 50% of the writes fall in the first
10%, 25% in the next 10%, and the last 25% over the remaining 80%,
you could extend the above ala:

random_distribution=zoned:50/5:50/95,50/10:25/10:25/80

Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
axboe committed Mar 4, 2016
1 parent 8116fd2 commit e0a04ac
Show file tree
Hide file tree
Showing 11 changed files with 472 additions and 28 deletions.
20 changes: 20 additions & 0 deletions HOWTO
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ random_distribution=str:float By default, fio will use a completely uniform
zipf Zipf distribution
pareto Pareto distribution
gauss Normal (guassian) distribution
zoned Zoned random distribution

When using a zipf or pareto distribution, an input value
is also needed to define the access pattern. For zipf, this
Expand All @@ -976,6 +977,25 @@ random_distribution=str:float By default, fio will use a completely uniform
the gauss distribution, a normal deviation is supplied as
a value between 0 and 100.

For a zoned distribution, fio supports specifying percentages
of IO access that should fall within what range of the file or
device. For example, given a criteria of:

60% of accesses should be to the first 10%
30% of accesses should be to the next 20%
8% of accesses should be to to the next 30%
2% of accesses should be to the next 40%

we can define that through zoning of the random accesses. For
the above example, the user would do:

random_distribution=zoned:60/10:30/20:8/30:2/40

similarly to how bssplit works for setting ranges and
percentages of block sizes. Like bssplit, it's possible to
specify separate zones for reads, writes, and trims. If just
one set is given, it'll apply to all of them.

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.
Expand Down
32 changes: 32 additions & 0 deletions cconv.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ static void __string_to_net(uint8_t *dst, const char *src, size_t dst_size)

static void free_thread_options_to_cpu(struct thread_options *o)
{
int i;

free(o->description);
free(o->name);
free(o->wait_for);
Expand All @@ -43,6 +45,11 @@ static void free_thread_options_to_cpu(struct thread_options *o)
free(o->ioscheduler);
free(o->profile);
free(o->cgroup);

for (i = 0; i < DDIR_RWDIR_CNT; i++) {
free(o->bssplit[i]);
free(o->zone_split[i]);
}
}

void convert_thread_options_to_cpu(struct thread_options *o,
Expand Down Expand Up @@ -111,6 +118,16 @@ void convert_thread_options_to_cpu(struct thread_options *o,
}
}

o->zone_split_nr[i] = le32_to_cpu(top->zone_split_nr[i]);

if (o->zone_split_nr[i]) {
o->zone_split[i] = malloc(o->zone_split_nr[i] * sizeof(struct zone_split));
for (j = 0; j < o->zone_split_nr[i]; j++) {
o->zone_split[i][j].access_perc = top->zone_split[i][j].access_perc;
o->zone_split[i][j].size_perc = top->zone_split[i][j].size_perc;
}
}

o->rwmix[i] = le32_to_cpu(top->rwmix[i]);
o->rate[i] = le32_to_cpu(top->rate[i]);
o->ratemin[i] = le32_to_cpu(top->ratemin[i]);
Expand Down Expand Up @@ -453,6 +470,21 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
}
}

top->zone_split_nr[i] = cpu_to_le32(o->zone_split_nr[i]);

if (o->zone_split_nr[i]) {
unsigned int zone_split_nr = o->zone_split_nr[i];

if (zone_split_nr > ZONESPLIT_MAX) {
log_err("fio: ZONESPLIT_MAX is too small\n");
zone_split_nr = ZONESPLIT_MAX;
}
for (j = 0; j < zone_split_nr; j++) {
top->zone_split[i][j].access_perc = o->zone_split[i][j].access_perc;
top->zone_split[i][j].size_perc = o->zone_split[i][j].size_perc;
}
}

top->rwmix[i] = cpu_to_le32(o->rwmix[i]);
top->rate[i] = cpu_to_le32(o->rate[i]);
top->ratemin[i] = cpu_to_le32(o->ratemin[i]);
Expand Down
18 changes: 18 additions & 0 deletions examples/rand-zones.fio
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Sample job file demonstrating how to use zoned random distributionss
# to have skewed random accesses. This example has 50% of the accesses
# to the first 5% of the file (50/5), 30% to the next 15% (30/15), and
# finally 20% of the IO will end up in the remaining 80%.
[zones]
size=2g
direct=1
bs=4k
rw=randread
norandommap
random_distribution=zoned:50/5:30/15:20/

# The above applies to all of reads/writes/trims. If we wanted to do
# something differently for writes, let's say 50% for the first 10%
# and 50% for the remaining 90%, we could do it by adding a new section
# after a a comma.

# random_distribution=zoned:50/5:30/15:20/,50/10:50/90
34 changes: 33 additions & 1 deletion fio.1
Original file line number Diff line number Diff line change
Expand Up @@ -877,8 +877,10 @@ Pareto distribution
.B gauss
Normal (gaussian) distribution
.TP
.B zoned
Zoned random distribution
.TP
.RE
.P
When using a \fBzipf\fR or \fBpareto\fR distribution, an input value is also
needed to define the access pattern. For \fBzipf\fR, this is the zipf theta.
For \fBpareto\fR, it's the pareto power. Fio includes a test program, genzipf,
Expand All @@ -887,6 +889,36 @@ hit rates. If you wanted to use \fBzipf\fR 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. For the \fBgauss\fR distribution, a
normal deviation is supplied as a value between 0 and 100.
.P
.RS
For a \fBzoned\fR distribution, fio supports specifying percentages of IO
access that should fall within what range of the file or device. For example,
given a criteria of:
.P
.RS
60% of accesses should be to the first 10%
.RE
.RS
30% of accesses should be to the next 20%
.RE
.RS
8% of accesses should be to to the next 30%
.RE
.RS
2% of accesses should be to the next 40%
.RE
.P
we can define that through zoning of the random accesses. For the above
example, the user would do:
.P
.RS
.B random_distribution=zoned:60/10:30/20:8/30:2/40
.RE
.P
similarly to how \fBbssplit\fR works for setting ranges and percentages of block
sizes. Like \fBbssplit\fR, it's possible to specify separate zones for reads,
writes, and trims. If just one set is given, it'll apply to all of them.
.RE
.TP
.BI percentage_random \fR=\fPint
For a random workload, set how big a percentage should be random. This defaults
Expand Down
10 changes: 10 additions & 0 deletions fio.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ enum {
FIO_RAND_START_DELAY,
FIO_DEDUPE_OFF,
FIO_RAND_POISSON_OFF,
FIO_RAND_ZONE_OFF,
FIO_RAND_NR_OFFS,
};

Expand All @@ -115,6 +116,11 @@ struct sk_out;
void sk_out_assign(struct sk_out *);
void sk_out_drop(void);

struct zone_split_index {
uint8_t size_perc;
uint8_t size_perc_prev;
};

/*
* This describes a single thread/process executing a fio job.
*/
Expand Down Expand Up @@ -200,6 +206,9 @@ struct thread_data {
struct frand_state buf_state;
struct frand_state buf_state_prev;
struct frand_state dedupe_state;
struct frand_state zone_state;

struct zone_split_index **zone_state_index;

unsigned int verify_batch;
unsigned int trim_batch;
Expand Down Expand Up @@ -712,6 +721,7 @@ enum {
FIO_RAND_DIST_ZIPF,
FIO_RAND_DIST_PARETO,
FIO_RAND_DIST_GAUSS,
FIO_RAND_DIST_ZONED,
};

#define FIO_DEF_ZIPF 1.1
Expand Down
1 change: 1 addition & 0 deletions init.c
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ void td_fill_rand_seeds(struct thread_data *td)
frand_copy(&td->buf_state_prev, &td->buf_state);

init_rand_seed(&td->dedupe_state, td->rand_seeds[FIO_DEDUPE_OFF], use64);
init_rand_seed(&td->zone_state, td->rand_seeds[FIO_RAND_ZONE_OFF], use64);
}

/*
Expand Down
106 changes: 81 additions & 25 deletions io_u.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,19 @@ struct rand_off {
};

static int __get_next_rand_offset(struct thread_data *td, struct fio_file *f,
enum fio_ddir ddir, uint64_t *b)
enum fio_ddir ddir, uint64_t *b,
uint64_t lastb)
{
uint64_t r;

if (td->o.random_generator == FIO_RAND_GEN_TAUSWORTHE ||
td->o.random_generator == FIO_RAND_GEN_TAUSWORTHE64) {
uint64_t frand_max, lastb;

lastb = last_block(td, f, ddir);
if (!lastb)
return 1;

frand_max = rand_max(&td->random_state);
r = __rand(&td->random_state);

dprint(FD_RANDOM, "off rand %llu\n", (unsigned long long) r);

*b = lastb * (r / ((uint64_t) frand_max + 1.0));
*b = lastb * (r / (rand_max(&td->random_state) + 1.0));
} else {
uint64_t off = 0;

Expand Down Expand Up @@ -161,6 +156,70 @@ static int __get_next_rand_offset_gauss(struct thread_data *td,
return 0;
}

static int __get_next_rand_offset_zoned(struct thread_data *td,
struct fio_file *f, enum fio_ddir ddir,
uint64_t *b)
{
unsigned int v, send, stotal;
uint64_t offset, lastb;
static int warned;
struct zone_split_index *zsi;

lastb = last_block(td, f, ddir);
if (!lastb)
return 1;

if (!td->o.zone_split_nr[ddir]) {
bail:
return __get_next_rand_offset(td, f, ddir, b, lastb);
}

/*
* Generate a value, v, between 1 and 100, both inclusive
*/
v = rand_between(&td->zone_state, 1, 100);

zsi = &td->zone_state_index[ddir][v - 1];
stotal = zsi->size_perc_prev;
send = zsi->size_perc;

/*
* Should never happen
*/
if (send == -1U) {
if (!warned) {
log_err("fio: bug in zoned generation\n");
warned = 1;
}
goto bail;
}

/*
* 'send' is some percentage below or equal to 100 that
* marks the end of the current IO range. 'stotal' marks
* the start, in percent.
*/
if (stotal)
offset = stotal * lastb / 100ULL;
else
offset = 0;

lastb = lastb * (send - stotal) / 100ULL;

/*
* Generate index from 0..send-of-lastb
*/
if (__get_next_rand_offset(td, f, ddir, b, lastb) == 1)
return 1;

/*
* Add our start offset, if any
*/
if (offset)
*b += offset;

return 0;
}

static int flist_cmp(void *data, struct flist_head *a, struct flist_head *b)
{
Expand All @@ -173,14 +232,22 @@ static int flist_cmp(void *data, struct flist_head *a, struct flist_head *b)
static int get_off_from_method(struct thread_data *td, struct fio_file *f,
enum fio_ddir ddir, uint64_t *b)
{
if (td->o.random_distribution == FIO_RAND_DIST_RANDOM)
return __get_next_rand_offset(td, f, ddir, b);
else if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
if (td->o.random_distribution == FIO_RAND_DIST_RANDOM) {
uint64_t lastb;

lastb = last_block(td, f, ddir);
if (!lastb)
return 1;

return __get_next_rand_offset(td, f, ddir, b, lastb);
} else if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
return __get_next_rand_offset_zipf(td, f, ddir, b);
else if (td->o.random_distribution == FIO_RAND_DIST_PARETO)
return __get_next_rand_offset_pareto(td, f, ddir, b);
else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS)
return __get_next_rand_offset_gauss(td, f, ddir, b);
else if (td->o.random_distribution == FIO_RAND_DIST_ZONED)
return __get_next_rand_offset_zoned(td, f, ddir, b);

log_err("fio: unknown random distribution: %d\n", td->o.random_distribution);
return 1;
Expand All @@ -207,16 +274,12 @@ static inline bool should_sort_io(struct thread_data *td)

static bool should_do_random(struct thread_data *td, enum fio_ddir ddir)
{
uint64_t frand_max;
unsigned int v;
unsigned long r;

if (td->o.perc_rand[ddir] == 100)
return true;

frand_max = rand_max(&td->seq_rand_state[ddir]);
r = __rand(&td->seq_rand_state[ddir]);
v = 1 + (int) (100.0 * (r / (frand_max + 1.0)));
v = rand_between(&td->seq_rand_state[ddir], 1, 100);

return v <= td->o.perc_rand[ddir];
}
Expand Down Expand Up @@ -536,12 +599,9 @@ static void set_rwmix_bytes(struct thread_data *td)

static inline enum fio_ddir get_rand_ddir(struct thread_data *td)
{
uint64_t frand_max = rand_max(&td->rwmix_state);
unsigned int v;
unsigned long r;

r = __rand(&td->rwmix_state);
v = 1 + (int) (100.0 * (r / (frand_max + 1.0)));
v = rand_between(&td->rwmix_state, 1, 100);

if (v <= td->o.rwmix[DDIR_READ])
return DDIR_READ;
Expand Down Expand Up @@ -1895,9 +1955,7 @@ void io_u_queued(struct thread_data *td, struct io_u *io_u)
*/
static struct frand_state *get_buf_state(struct thread_data *td)
{
uint64_t frand_max;
unsigned int v;
unsigned long r;

if (!td->o.dedupe_percentage)
return &td->buf_state;
Expand All @@ -1906,9 +1964,7 @@ static struct frand_state *get_buf_state(struct thread_data *td)
return &td->buf_state;
}

frand_max = rand_max(&td->dedupe_state);
r = __rand(&td->dedupe_state);
v = 1 + (int) (100.0 * (r / (frand_max + 1.0)));
v = rand_between(&td->dedupe_state, 1, 100);

if (v <= td->o.dedupe_percentage)
return &td->buf_state_prev;
Expand Down
Loading

0 comments on commit e0a04ac

Please sign in to comment.