Scaling a particular trace may result in different runtimes among the
merging traces. By knowing the approximate length of each trace as a
user, the overall runtime of each can be tuned to line up by letting
certain traces loop multiple times.
First, the last timestamp of a trace is recorded at the end of the first
iteration to denote the length of a trace. This value is then used to
offset subsequent iterations of a trace.
Next, the "--merge_blktrace_iters" option is introduced to let the user
specify the number of times to loop over each specific trace. This is
done by passing a comma separated list that index-wise pairs with the
passed files in "--read_iolog". Iteration counts are introduced as well
as keeping track of the length of each trace.
In an example, given two traces, A and B, each 60s long. If we want to
see the impact of trace A issuing IOs twice as fast, the
--merge_blktrace_scalars="50:100" can be set and then
--merge_blktrace_iters="2:1". This runs trace A at 2x the speed twice for
approximately the same runtime as a single run of trace B.
Signed-off-by: Dennis Zhou <dennis@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
:option:`replay_time_scale` which scales the trace during runtime and
does not change the output of the merge unlike this option.
:option:`replay_time_scale` which scales the trace during runtime and
does not change the output of the merge unlike this option.
+.. option:: merge_blktrace_iters=float_list
+
+ This is a whole number option that is index paired with the list of files
+ passed to :option:`read_iolog`. When merging is performed, run each trace
+ for the specified number of iterations. For example,
+ ``--merge_blktrace_iters="2:1"`` runs the first trace for two iterations
+ and the second trace for one iteration.
+
.. option:: replay_no_stall=bool
When replaying I/O with :option:`read_iolog` the default behavior is to
.. option:: replay_no_stall=bool
When replaying I/O with :option:`read_iolog` the default behavior is to
separated list of percentage scalars. It is index paired with the files passed
to :option:`read_iolog`.
separated list of percentage scalars. It is index paired with the files passed
to :option:`read_iolog`.
+With scaling, it may be desirable to match the running time of all traces.
+This can be done with :option:`merge_blktrace_iters`. It is index paired with
+:option:`read_iolog` just like :option:`merge_blktrace_scalars`.
+
+In an example, given two traces, A and B, each 60s long. If we want to see
+the impact of trace A issuing IOs twice as fast and repeat trace A over the
+runtime of trace B, the following can be done::
+
+ $ fio --read_iolog="<trace_a>:"<trace_b>" --merge_blktrace_file"<output_file>" --merge_blktrace_scalars="50:100" --merge_blktrace_iters="2:1"
+
+This runs trace A at 2x the speed twice for approximately the same runtime as
+a single run of trace B.
+
CPU idleness profiling
----------------------
CPU idleness profiling
----------------------
static void merge_finish_file(struct blktrace_cursor *bcs, int i, int *nr_logs)
{
static void merge_finish_file(struct blktrace_cursor *bcs, int i, int *nr_logs)
{
+ bcs[i].iter++;
+ if (bcs[i].iter < bcs[i].nr_iter) {
+ lseek(bcs[i].fd, 0, SEEK_SET);
+ return;
+ }
+
*nr_logs -= 1;
/* close file */
*nr_logs -= 1;
/* close file */
read_skip:
/* read an io trace */
ret = trace_fifo_get(td, bc->fifo, bc->fd, t, sizeof(*t));
read_skip:
/* read an io trace */
ret = trace_fifo_get(td, bc->fifo, bc->fd, t, sizeof(*t));
+ if (ret < 0) {
+ return ret;
+ } else if (!ret) {
+ if (!bc->length)
+ bc->length = bc->t.time;
return ret;
} else if (ret < (int) sizeof(*t)) {
log_err("fio: short fifo get\n");
return ret;
} else if (ret < (int) sizeof(*t)) {
log_err("fio: short fifo get\n");
- t->time = t->time * bc->scalar / 100;
+ t->time = (t->time + bc->iter * bc->length) * bc->scalar / 100;
+ ret = init_merge_param_list(td->o.merge_blktrace_iters, bcs, nr_logs,
+ 1, offsetof(struct blktrace_cursor,
+ nr_iter));
+ if (ret) {
+ log_err("fio: merge_blktrace_iters(%d) != nr_logs(%d)\n",
+ ret, nr_logs);
+ goto err_param;
+ }
+
/* setup output file */
merge_fp = fopen(td->o.merge_blktrace_file, "w");
merge_buf = malloc(128 * 1024);
/* setup output file */
merge_fp = fopen(td->o.merge_blktrace_file, "w");
merge_buf = malloc(128 * 1024);
+#include <asm/types.h>
+
#include "blktrace_api.h"
struct blktrace_cursor {
struct fifo *fifo; // fifo queue for reading
int fd; // blktrace file
#include "blktrace_api.h"
struct blktrace_cursor {
struct fifo *fifo; // fifo queue for reading
int fd; // blktrace file
+ __u64 length; // length of trace
struct blk_io_trace t; // current io trace
int swap; // bitwise reverse required
int scalar; // scale percentage
struct blk_io_trace t; // current io trace
int swap; // bitwise reverse required
int scalar; // scale percentage
+ int iter; // current iteration
+ int nr_iter; // number of iterations to run
};
bool is_blktrace(const char *, int *);
};
bool is_blktrace(const char *, int *);
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
o->merge_blktrace_scalars[i].u.f = fio_uint64_to_double(le64_to_cpu(top->merge_blktrace_scalars[i].u.i));
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
o->merge_blktrace_scalars[i].u.f = fio_uint64_to_double(le64_to_cpu(top->merge_blktrace_scalars[i].u.i));
+
+ for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
+ o->merge_blktrace_iters[i].u.f = fio_uint64_to_double(le64_to_cpu(top->merge_blktrace_iters[i].u.i));
#if 0
uint8_t cpumask[FIO_TOP_STR_MAX];
uint8_t verify_cpumask[FIO_TOP_STR_MAX];
#if 0
uint8_t cpumask[FIO_TOP_STR_MAX];
uint8_t verify_cpumask[FIO_TOP_STR_MAX];
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
top->merge_blktrace_scalars[i].u.i = __cpu_to_le64(fio_double_to_uint64(o->merge_blktrace_scalars[i].u.f));
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
top->merge_blktrace_scalars[i].u.i = __cpu_to_le64(fio_double_to_uint64(o->merge_blktrace_scalars[i].u.f));
+
+ for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
+ top->merge_blktrace_iters[i].u.i = __cpu_to_le64(fio_double_to_uint64(o->merge_blktrace_iters[i].u.f));
#if 0
uint8_t cpumask[FIO_TOP_STR_MAX];
uint8_t verify_cpumask[FIO_TOP_STR_MAX];
#if 0
uint8_t cpumask[FIO_TOP_STR_MAX];
uint8_t verify_cpumask[FIO_TOP_STR_MAX];
\fBreplay_time_scale\fR which scales the trace during runtime and will not
change the output of the merge unlike this option.
.TP
\fBreplay_time_scale\fR which scales the trace during runtime and will not
change the output of the merge unlike this option.
.TP
+.BI merge_blktrace_iters \fR=\fPfloat_list
+This is a whole number option that is index paired with the list of files
+passed to \fBread_iolog\fR. When merging is performed, run each trace for
+the specified number of iterations. For example,
+`\-\-merge_blktrace_iters="2:1"' runs the first trace for two iterations
+and the second trace for one iteration.
+.TP
.BI replay_no_stall \fR=\fPbool
When replaying I/O with \fBread_iolog\fR the default behavior is to
attempt to respect the timestamps within the log and replay them with the
.BI replay_no_stall \fR=\fPbool
When replaying I/O with \fBread_iolog\fR the default behavior is to
attempt to respect the timestamps within the log and replay them with the
being slowed down or sped up. \fBmerge_blktrace_scalars\fR takes in a colon
separated list of percentage scalars. It is index paired with the files passed
to \fBread_iolog\fR.
being slowed down or sped up. \fBmerge_blktrace_scalars\fR takes in a colon
separated list of percentage scalars. It is index paired with the files passed
to \fBread_iolog\fR.
+.P
+With scaling, it may be desirable to match the running time of all traces.
+This can be done with \fBmerge_blktrace_iters\fR. It is index paired with
+\fBread_iolog\fR just like \fBmerge_blktrace_scalars\fR.
+.P
+In an example, given two traces, A and B, each 60s long. If we want to see
+the impact of trace A issuing IOs twice as fast and repeat trace A over the
+runtime of trace B, the following can be done:
+.RS
+.P
+$ fio \-\-read_iolog="<trace_a>:"<trace_b>" \-\-merge_blktrace_file"<output_file>" \-\-merge_blktrace_scalars="50:100" \-\-merge_blktrace_iters="2:1"
+.RE
+.P
+This runs trace A at 2x the speed twice for approximately the same runtime as
+a single run of trace B.
.SH CPU IDLENESS PROFILING
In some cases, we want to understand CPU overhead in a test. For example, we
test patches for the specific goodness of whether they reduce CPU usage.
.SH CPU IDLENESS PROFILING
In some cases, we want to understand CPU overhead in a test. For example, we
test patches for the specific goodness of whether they reduce CPU usage.
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_IOLOG,
},
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_IOLOG,
},
+ {
+ .name = "merge_blktrace_iters",
+ .lname = "Number of iterations to run per trace",
+ .type = FIO_OPT_FLOAT_LIST,
+ .off1 = offsetof(struct thread_options, merge_blktrace_iters),
+ .maxlen = FIO_IO_U_LIST_MAX_LEN,
+ .help = "Number of iterations to run per trace",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_IOLOG,
+ },
{
.name = "exec_prerun",
.lname = "Pre-execute runnable",
{
.name = "exec_prerun",
.lname = "Pre-execute runnable",
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
FIO_SERVER_MAX_CMD_MB = 2048,
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
FIO_SERVER_MAX_CMD_MB = 2048,
char *write_iolog_file;
char *merge_blktrace_file;
fio_fp64_t merge_blktrace_scalars[FIO_IO_U_LIST_MAX_LEN];
char *write_iolog_file;
char *merge_blktrace_file;
fio_fp64_t merge_blktrace_scalars[FIO_IO_U_LIST_MAX_LEN];
+ fio_fp64_t merge_blktrace_iters[FIO_IO_U_LIST_MAX_LEN];
unsigned int write_bw_log;
unsigned int write_lat_log;
unsigned int write_bw_log;
unsigned int write_lat_log;
uint8_t write_iolog_file[FIO_TOP_STR_MAX];
uint8_t merge_blktrace_file[FIO_TOP_STR_MAX];
fio_fp64_t merge_blktrace_scalars[FIO_IO_U_LIST_MAX_LEN];
uint8_t write_iolog_file[FIO_TOP_STR_MAX];
uint8_t merge_blktrace_file[FIO_TOP_STR_MAX];
fio_fp64_t merge_blktrace_scalars[FIO_IO_U_LIST_MAX_LEN];
+ fio_fp64_t merge_blktrace_iters[FIO_IO_U_LIST_MAX_LEN];
uint32_t write_bw_log;
uint32_t write_lat_log;
uint32_t write_bw_log;
uint32_t write_lat_log;