.. option:: steadystate_duration=time, ss_dur=time
A rolling window of this duration will be used to judge whether steady state
- has been reached. Data will be collected once per second. The default is 0
- which disables steady state detection. When the unit is omitted, the
- value is interpreted in seconds.
+ has been reached. Data will be collected every ss_check_interval.
+ The default is 0 which disables steady state detection. When the unit is omitted,
+ the value is interpreted in seconds.
.. option:: steadystate_ramp_time=time, ss_ramp=time
collection for checking the steady state job termination criterion. The
default is 0. When the unit is omitted, the value is interpreted in seconds.
+.. option:: steadystate_check_interval=time, ss_interval=time
+
+ The values during the rolling window will be collected with a period
+ of this value. If ss_interval is 30s and ss_dur is 300s, 10 measurements will
+ be taken. Default is 1s but that might not converge, especially for
+ slower cards, so set this accordingly. When the unit is omitted,
+ the value is interpreted in seconds.
+
Measurements and reporting
~~~~~~~~~~~~~~~~~~~~~~~~~~
Known issues/TODO (for steady-state)
-- Allow user to specify the frequency of measurements
-
- Better documentation for output
- Report read, write, trim IOPS/BW separately
o->ss_ramp_time = le64_to_cpu(top->ss_ramp_time);
o->ss_state = le32_to_cpu(top->ss_state);
o->ss_limit.u.f = fio_uint64_to_double(le64_to_cpu(top->ss_limit.u.i));
+ o->ss_check_interval = le64_to_cpu(top->ss_check_interval);
o->zone_range = le64_to_cpu(top->zone_range);
o->zone_size = le64_to_cpu(top->zone_size);
o->zone_capacity = le64_to_cpu(top->zone_capacity);
top->ss_ramp_time = __cpu_to_le64(top->ss_ramp_time);
top->ss_state = cpu_to_le32(top->ss_state);
top->ss_limit.u.i = __cpu_to_le64(fio_double_to_uint64(o->ss_limit.u.f));
+ top->ss_check_interval = __cpu_to_le64(top->ss_check_interval);
top->zone_range = __cpu_to_le64(o->zone_range);
top->zone_size = __cpu_to_le64(o->zone_size);
top->zone_capacity = __cpu_to_le64(o->zone_capacity);
Allow the job to run for the specified duration before beginning data
collection for checking the steady state job termination criterion. The
default is 0. When the unit is omitted, the value is interpreted in seconds.
+.TP
+.BI steadystate_check_interval \fR=\fPtime "\fR,\fP ss_interval" \fR=\fPtime
+The values suring the rolling window will be collected with a period of this
+value. Default is 1s but that might not converge, especially for slower cards,
+so set this accordingly. When the unit is omitted, the value is interpreted
+in seconds.
.SS "Measurements and reporting"
.TP
.BI per_job_logs \fR=\fPbool
},
{
.name = "steadystate",
- .interval_ms = steadystate_enabled ? STEADYSTATE_MSEC :
+ .interval_ms = steadystate_enabled ? ss_check_interval :
0,
.func = steadystate_check,
}
}
}
+ for_each_td(td2) {
+ if (td->o.ss_check_interval != td2->o.ss_check_interval) {
+ log_err("fio: conflicting ss_check_interval: %llu and %llu, must be globally equal\n",
+ td->o.ss_check_interval, td2->o.ss_check_interval);
+ ret |= 1;
+ }
+ } end_for_each();
+ if (td->o.ss_dur && td->o.ss_check_interval / 1000L < 1000) {
+ log_err("fio: ss_check_interval must be at least 1s\n");
+ ret |= 1;
+
+ }
+ if (td->o.ss_dur && (td->o.ss_dur % td->o.ss_check_interval != 0 || td->o.ss_dur <= td->o.ss_check_interval)) {
+ log_err("fio: ss_duration %lluus must be multiple of ss_check_interval %lluus\n",
+ td->o.ss_dur, td->o.ss_check_interval);
+ ret |= 1;
+ }
+
+
return ret;
}
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
+ {
+ .name = "steadystate_check_interval",
+ .lname = "Steady state check interval",
+ .alias = "ss_interval",
+ .parent = "steadystate",
+ .type = FIO_OPT_STR_VAL_TIME,
+ .off1 = offsetof(struct thread_options, ss_check_interval),
+ .help = "Polling interval for the steady state check (too low means steadystate will not converge)",
+ .def = "1",
+ .is_seconds = 1,
+ .is_time = 1,
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_RUNTIME,
+ },
{
.name = NULL,
},
struct json_array *iops, *bw;
int j, k, l;
char ss_buf[64];
+ int intervals = ts->ss_dur / (ss_check_interval / 1000L);
snprintf(ss_buf, sizeof(ss_buf), "%s%s:%f%s",
ts->ss_state & FIO_SS_IOPS ? "iops" : "bw",
if ((ts->ss_state & FIO_SS_ATTAINED) || !(ts->ss_state & FIO_SS_BUFFER_FULL))
j = ts->ss_head;
else
- j = ts->ss_head == 0 ? ts->ss_dur - 1 : ts->ss_head - 1;
- for (l = 0; l < ts->ss_dur; l++) {
- k = (j + l) % ts->ss_dur;
+ j = ts->ss_head == 0 ? intervals - 1 : ts->ss_head - 1;
+ for (l = 0; l < intervals; l++) {
+ k = (j + l) % intervals;
json_array_add_value_int(bw, ts->ss_bw_data[k]);
json_array_add_value_int(iops, ts->ss_iops_data[k]);
}
#include "steadystate.h"
bool steadystate_enabled = false;
+unsigned int ss_check_interval = 1000;
void steadystate_free(struct thread_data *td)
{
static void steadystate_alloc(struct thread_data *td)
{
- td->ss.bw_data = calloc(td->ss.dur, sizeof(uint64_t));
- td->ss.iops_data = calloc(td->ss.dur, sizeof(uint64_t));
+ int intervals = td->ss.dur / (ss_check_interval / 1000L);
+
+ td->ss.bw_data = calloc(intervals, sizeof(uint64_t));
+ td->ss.iops_data = calloc(intervals, sizeof(uint64_t));
td->ss.state |= FIO_SS_DATA;
}
double result;
struct steadystate_data *ss = &td->ss;
uint64_t new_val;
+ int intervals = ss->dur / (ss_check_interval / 1000L);
ss->bw_data[ss->tail] = bw;
ss->iops_data[ss->tail] = iops;
else
new_val = bw;
- if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == ss->dur - 1) {
+ if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
if (!(ss->state & FIO_SS_BUFFER_FULL)) {
/* first time through */
- for(i = 0, ss->sum_y = 0; i < ss->dur; i++) {
+ for (i = 0, ss->sum_y = 0; i < intervals; i++) {
if (ss->state & FIO_SS_IOPS)
ss->sum_y += ss->iops_data[i];
else
return true;
}
- ss->tail = (ss->tail + 1) % ss->dur;
+ ss->tail = (ss->tail + 1) % intervals;
if (ss->tail <= ss->head)
- ss->head = (ss->head + 1) % ss->dur;
+ ss->head = (ss->head + 1) % intervals;
return false;
}
double mean;
struct steadystate_data *ss = &td->ss;
+ int intervals = ss->dur / (ss_check_interval / 1000L);
ss->bw_data[ss->tail] = bw;
ss->iops_data[ss->tail] = iops;
- if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == ss->dur - 1) {
+ if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
if (!(ss->state & FIO_SS_BUFFER_FULL)) {
/* first time through */
- for(i = 0, ss->sum_y = 0; i < ss->dur; i++)
+ for (i = 0, ss->sum_y = 0; i < intervals; i++) {
if (ss->state & FIO_SS_IOPS)
ss->sum_y += ss->iops_data[i];
else
ss->sum_y += ss->bw_data[i];
+ }
ss->state |= FIO_SS_BUFFER_FULL;
} else { /* easy to update the sum */
ss->sum_y -= ss->oldest_y;
else
ss->oldest_y = ss->bw_data[ss->head];
- mean = (double) ss->sum_y / ss->dur;
+ mean = (double) ss->sum_y / intervals;
ss->deviation = 0.0;
- for (i = 0; i < ss->dur; i++) {
+ for (i = 0; i < intervals; i++) {
if (ss->state & FIO_SS_IOPS)
diff = ss->iops_data[i] - mean;
else
else
ss->criterion = ss->deviation;
- dprint(FD_STEADYSTATE, "sum_y: %llu, mean: %f, max diff: %f, "
+ dprint(FD_STEADYSTATE, "intervals: %d, sum_y: %llu, mean: %f, max diff: %f, "
"objective: %f, limit: %f\n",
+ intervals,
(unsigned long long) ss->sum_y, mean,
ss->deviation, ss->criterion, ss->limit);
return true;
}
- ss->tail = (ss->tail + 1) % ss->dur;
- if (ss->tail <= ss->head)
- ss->head = (ss->head + 1) % ss->dur;
+ ss->tail = (ss->tail + 1) % intervals;
+ if (ss->tail == ss->head)
+ ss->head = (ss->head + 1) % intervals;
return false;
}
fio_gettime(&now, NULL);
if (ss->ramp_time && !(ss->state & FIO_SS_RAMP_OVER)) {
/*
- * Begin recording data one second after ss->ramp_time
+ * Begin recording data one check interval after ss->ramp_time
* has elapsed
*/
- if (utime_since(&td->epoch, &now) >= (ss->ramp_time + 1000000L))
+ if (utime_since(&td->epoch, &now) >= (ss->ramp_time + ss_check_interval * 1000L))
ss->state |= FIO_SS_RAMP_OVER;
}
memcpy(&ss->prev_time, &now, sizeof(now));
if (ss->state & FIO_SS_RAMP_OVER) {
- group_bw += 1000 * (td_bytes - ss->prev_bytes) / rate_time;
- group_iops += 1000 * (td_iops - ss->prev_iops) / rate_time;
+ group_bw += rate_time * (td_bytes - ss->prev_bytes) /
+ (ss_check_interval * ss_check_interval / 1000L);
+ group_iops += rate_time * (td_iops - ss->prev_iops) /
+ (ss_check_interval * ss_check_interval / 1000L);
++group_ramp_time_over;
}
ss->prev_iops = td_iops;
ss->dur = o->ss_dur;
ss->limit = o->ss_limit.u.f;
ss->ramp_time = o->ss_ramp_time;
+ ss_check_interval = o->ss_check_interval / 1000L;
ss->state = o->ss_state;
if (!td->ss.ramp_time)
{
int i;
uint64_t sum;
-
+ int intervals = ts->ss_dur / (ss_check_interval / 1000L);
+
if (!ts->ss_dur)
return 0;
- for (i = 0, sum = 0; i < ts->ss_dur; i++)
+ for (i = 0, sum = 0; i < intervals; i++)
sum += ts->ss_bw_data[i];
- return sum / ts->ss_dur;
+ return sum / intervals;
}
uint64_t steadystate_iops_mean(struct thread_stat *ts)
{
int i;
uint64_t sum;
+ int intervals = ts->ss_dur / (ss_check_interval / 1000L);
if (!ts->ss_dur)
return 0;
- for (i = 0, sum = 0; i < ts->ss_dur; i++)
+ for (i = 0, sum = 0; i < intervals; i++)
sum += ts->ss_iops_data[i];
- return sum / ts->ss_dur;
+ return sum / intervals;
}
extern uint64_t steadystate_iops_mean(struct thread_stat *);
extern bool steadystate_enabled;
+extern unsigned int ss_check_interval;
struct steadystate_data {
double limit;
FIO_SS_BW_SLOPE = FIO_SS_BW | FIO_SS_SLOPE,
};
-#define STEADYSTATE_MSEC 1000
-
#endif
{'s': False, 'timeout': 20, 'numjobs': 2},
{'s': True, 'timeout': 100, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 5, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
{'s': True, 'timeout': 10, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 500, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
+ {'s': True, 'timeout': 10, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 500, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True, 'ss_interval': 5},
]
jobnum = 0
fio_fp64_t ss_limit;
unsigned long long ss_dur;
unsigned long long ss_ramp_time;
+ unsigned long long ss_check_interval;
unsigned int overwrite;
unsigned int bw_avg_time;
unsigned int iops_avg_time;
uint64_t ss_ramp_time;
uint32_t ss_state;
fio_fp64_t ss_limit;
+ uint64_t ss_check_interval;
uint32_t overwrite;
uint32_t bw_avg_time;
uint32_t iops_avg_time;