X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=stat.c;h=3662fd9eee2bdd4fbbedf249cb850dbee4ab334c;hp=a79c7ef29da473567debc712f3c6909b077f7a13;hb=eb7976ef27c1d9f69885b0c8db3020303999c9af;hpb=743ed83a203800ba3a7d89fc76f3424d693db06c diff --git a/stat.c b/stat.c index a79c7ef2..3662fd9e 100644 --- a/stat.c +++ b/stat.c @@ -28,6 +28,147 @@ void update_rusage_stat(struct thread_data *td) memcpy(&ts->ru_start, &ts->ru_end, sizeof(ts->ru_end)); } +/* + * Given a latency, return the index of the corresponding bucket in + * the structure tracking percentiles. + * + * (1) find the group (and error bits) that the value (latency) + * belongs to by looking at its MSB. (2) find the bucket number in the + * group by looking at the index bits. + * + */ +static unsigned int plat_val_to_idx(unsigned int val) +{ + unsigned int msb, error_bits, base, offset, idx; + + /* Find MSB starting from bit 0 */ + if (val == 0) + msb = 0; + else + msb = (sizeof(val)*8) - __builtin_clz(val) - 1; + + /* + * MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use + * all bits of the sample as index + */ + if (msb <= FIO_IO_U_PLAT_BITS) + return val; + + /* Compute the number of error bits to discard*/ + error_bits = msb - FIO_IO_U_PLAT_BITS; + + /* Compute the number of buckets before the group */ + base = (error_bits + 1) << FIO_IO_U_PLAT_BITS; + + /* + * Discard the error bits and apply the mask to find the + * index for the buckets in the group + */ + offset = (FIO_IO_U_PLAT_VAL - 1) & (val >> error_bits); + + /* Make sure the index does not exceed (array size - 1) */ + idx = (base + offset) < (FIO_IO_U_PLAT_NR - 1)? + (base + offset) : (FIO_IO_U_PLAT_NR - 1); + + return idx; +} + +/* + * Convert the given index of the bucket array to the value + * represented by the bucket + */ +static unsigned int plat_idx_to_val(unsigned int idx) +{ + unsigned int error_bits, k, base; + + assert(idx < FIO_IO_U_PLAT_NR); + + /* MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use + * all bits of the sample as index */ + if (idx < (FIO_IO_U_PLAT_VAL << 1) ) + return idx; + + /* Find the group and compute the minimum value of that group */ + error_bits = (idx >> FIO_IO_U_PLAT_BITS) -1; + base = 1 << (error_bits + FIO_IO_U_PLAT_BITS); + + /* Find its bucket number of the group */ + k = idx % FIO_IO_U_PLAT_VAL; + + /* Return the mean of the range of the bucket */ + return base + ((k + 0.5) * (1 << error_bits)); +} + +static int double_cmp(const void *a, const void *b) +{ + const double fa = *(const double *)a; + const double fb = *(const double *)b; + int cmp = 0; + + if (fa > fb) + cmp = 1; + else if (fa < fb) + cmp = -1; + + return cmp; +} + +/* + * Find and display the p-th percentile of clat + */ +static void show_clat_percentiles(unsigned int* io_u_plat, unsigned long nr, + double* user_list) +{ + unsigned long sum = 0; + unsigned int len, i, j = 0; + const double *plist; + int is_last = 0; + static const double def_list[FIO_IO_U_LIST_MAX_LEN] = { + 1.0, 5.0, 10.0, 20.0, 30.0, + 40.0, 50.0, 60.0, 70.0, 80.0, + 90.0, 95.0, 99.0, 99.5, 99.9}; + + plist = user_list; + if (!plist) + plist = def_list; + + for (len = 0; len 1) + qsort((void*)user_list, len, sizeof(user_list[0]), double_cmp); + + log_info(" clat percentiles (usec) :"); + + for (i = 0; i < FIO_IO_U_PLAT_NR && !is_last; i++) { + sum += io_u_plat[i]; + while (sum >= (plist[j] / 100 * nr)) { + assert(plist[j] <= 100.0); + + /* for formatting */ + if (j != 0 && (j % 4) == 0) + log_info(" "); + + /* end of the list */ + is_last = (j == len - 1); + + log_info(" %2.2fth=%u%c", plist[j], plat_idx_to_val(i), + (is_last? '\n' : ',')); + + if (is_last) + break; + + if (j % 4 == 3) /* for formatting */ + log_info("\n"); + j++; + } + } +} + static int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, double *mean, double *dev) { @@ -157,6 +298,8 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts, char *io_p, *bw_p, *iops_p; int i2p; + assert(ddir_rw(ddir)); + if (!ts->runtime[ddir]) return; @@ -167,10 +310,10 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts, io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); bw_p = num2str(bw, 6, 1, i2p); - iops = (1000 * ts->total_io_u[ddir]) / runt; + iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; iops_p = num2str(iops, 6, 1, 0); - log_info(" %s: io=%sB, bw=%sB/s, iops=%s, runt=%6lumsec\n", + log_info(" %s: io=%sB, bw=%sB/s, iops=%s, runt=%6llumsec\n", ddir_str[ddir], io_p, bw_p, iops_p, ts->runtime[ddir]); @@ -226,6 +369,11 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts, free(minp); free(maxp); } + if (ts->clat_percentiles) { + show_clat_percentiles(ts->io_u_plat[ddir], + ts->clat_stat[ddir].samples, + ts->percentile_list); + } if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) { double p_of_agg; @@ -349,9 +497,11 @@ static void show_thread_status(struct thread_stat *ts, io_u_dist[1], io_u_dist[2], io_u_dist[3], io_u_dist[4], io_u_dist[5], io_u_dist[6]); - log_info(" issued r/w: total=%lu/%lu, short=%lu/%lu\n", + log_info(" issued r/w/d: total=%lu/%lu/%lu, short=%lu/%lu/%lu\n", ts->total_io_u[0], ts->total_io_u[1], - ts->short_io_u[0], ts->short_io_u[1]); + ts->total_io_u[2], + ts->short_io_u[0], ts->short_io_u[1], + ts->short_io_u[2]); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); show_latencies(io_u_lat_u, io_u_lat_m); @@ -370,11 +520,13 @@ static void show_ddir_status_terse(struct thread_stat *ts, unsigned long long bw; double mean, dev; + assert(ddir_rw(ddir)); + bw = 0; if (ts->runtime[ddir]) bw = ts->io_bytes[ddir] / ts->runtime[ddir]; - log_info(";%llu;%llu;%lu", ts->io_bytes[ddir] >> 10, bw, + log_info(";%llu;%llu;%llu", ts->io_bytes[ddir] >> 10, bw, ts->runtime[ddir]); if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) @@ -401,6 +553,7 @@ static void show_ddir_status_terse(struct thread_stat *ts, log_info(";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0); } +#define FIO_TERSE_VERSION "2" static void show_thread_status_terse(struct thread_stat *ts, struct group_run_stats *rs) @@ -411,11 +564,15 @@ static void show_thread_status_terse(struct thread_stat *ts, double usr_cpu, sys_cpu; int i; - log_info("%s;%d;%d", ts->name, ts->groupid, ts->error); - + /* General Info */ + log_info("%s;%s;%d;%d", FIO_TERSE_VERSION, ts->name, ts->groupid, + ts->error); + /* Log Read Status */ show_ddir_status_terse(ts, rs, 0); + /* Log Write Status */ show_ddir_status_terse(ts, rs, 1); + /* CPU Usage */ if (ts->total_run_time) { double runt = (double) ts->total_run_time; @@ -429,22 +586,28 @@ static void show_thread_status_terse(struct thread_stat *ts, log_info(";%f%%;%f%%;%lu;%lu;%lu", usr_cpu, sys_cpu, ts->ctx, ts->majf, ts->minf); + /* Calc % distribution of IO depths, usecond, msecond latency */ stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); + /* Only show fixed 7 I/O depth levels*/ log_info(";%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%", io_u_dist[0], io_u_dist[1], io_u_dist[2], io_u_dist[3], io_u_dist[4], io_u_dist[5], io_u_dist[6]); + /* Microsecond latency */ for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) log_info(";%3.2f%%", io_u_lat_u[i]); + /* Millisecond latency */ for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) log_info(";%3.2f%%", io_u_lat_m[i]); + /* Additional output if continue_on_error set - default off*/ if (ts->continue_on_error) log_info(";%lu;%d", ts->total_err_count, ts->first_error); log_info("\n"); + /* Additional output if description is set */ if (ts->description) log_info(";%s", ts->description); @@ -455,23 +618,33 @@ static void sum_stat(struct io_stat *dst, struct io_stat *src, int nr) { double mean, S; + if (src->samples == 0) + return; + dst->min_val = min(dst->min_val, src->min_val); dst->max_val = max(dst->max_val, src->max_val); - dst->samples += src->samples; /* - * Needs a new method for calculating stddev, we cannot just - * average them we do below for nr > 1 + * Compute new mean and S after the merge + * */ if (nr == 1) { mean = src->mean; S = src->S; } else { - mean = ((src->mean * (double) (nr - 1)) - + dst->mean) / ((double) nr); - S = ((src->S * (double) (nr - 1)) + dst->S) / ((double) nr); + double delta = src->mean - dst->mean; + + mean = ((src->mean * src->samples) + + (dst->mean * dst->samples)) / + (dst->samples + src->samples); + + S = src->S + dst->S + pow(delta, 2.0) * + (dst->samples * src->samples) / + (dst->samples + src->samples); } + dst->samples += src->samples; dst->mean = mean; dst->S = S; } @@ -541,6 +714,12 @@ void show_run_stats(void) ts = &threadstats[j]; + ts->clat_percentiles = td->o.clat_percentiles; + if (td->o.overwrite_plist) + ts->percentile_list = td->o.percentile_list; + else + ts->percentile_list = NULL; + idx++; ts->members++; @@ -609,11 +788,17 @@ void show_run_stats(void) ts->io_u_lat_m[k] += td->ts.io_u_lat_m[k]; - for (k = 0; k <= DDIR_WRITE; k++) { + for (k = 0; k <= 2; k++) { ts->total_io_u[k] += td->ts.total_io_u[k]; ts->short_io_u[k] += td->ts.short_io_u[k]; } + for (k = 0; k <= DDIR_WRITE; k++) { + int m; + for (m = 0; m < FIO_IO_U_PLAT_NR; m++) + ts->io_u_plat[k][m] += td->ts.io_u_plat[k][m]; + } + ts->total_run_time += td->ts.total_run_time; ts->total_submit += td->ts.total_submit; ts->total_complete += td->ts.total_complete; @@ -711,7 +896,7 @@ static inline void add_stat_sample(struct io_stat *is, unsigned long data) static void __add_log_sample(struct io_log *iolog, unsigned long val, enum fio_ddir ddir, unsigned int bs, - unsigned long time) + unsigned long t) { const int nr_samples = iolog->nr_samples; @@ -723,7 +908,7 @@ static void __add_log_sample(struct io_log *iolog, unsigned long val, } iolog->log[nr_samples].val = val; - iolog->log[nr_samples].time = time; + iolog->log[nr_samples].time = t; iolog->log[nr_samples].ddir = ddir; iolog->log[nr_samples].bs = bs; iolog->nr_samples++; @@ -733,25 +918,47 @@ static void add_log_sample(struct thread_data *td, struct io_log *iolog, unsigned long val, enum fio_ddir ddir, unsigned int bs) { + if (!ddir_rw(ddir)) + return; + __add_log_sample(iolog, val, ddir, bs, mtime_since_now(&td->epoch)); } void add_agg_sample(unsigned long val, enum fio_ddir ddir, unsigned int bs) { - struct io_log *iolog = agg_io_log[ddir]; + struct io_log *iolog; + + if (!ddir_rw(ddir)) + return; + iolog = agg_io_log[ddir]; __add_log_sample(iolog, val, ddir, bs, mtime_since_genesis()); } +static void add_clat_percentile_sample(struct thread_stat *ts, + unsigned long usec, enum fio_ddir ddir) +{ + unsigned int idx = plat_val_to_idx(usec); + assert(idx < FIO_IO_U_PLAT_NR); + + ts->io_u_plat[ddir][idx]++; +} + void add_clat_sample(struct thread_data *td, enum fio_ddir ddir, unsigned long usec, unsigned int bs) { struct thread_stat *ts = &td->ts; + if (!ddir_rw(ddir)) + return; + add_stat_sample(&ts->clat_stat[ddir], usec); if (ts->clat_log) add_log_sample(td, ts->clat_log, usec, ddir, bs); + + if (ts->clat_percentiles) + add_clat_percentile_sample(ts, usec, ddir); } void add_slat_sample(struct thread_data *td, enum fio_ddir ddir, @@ -759,6 +966,9 @@ void add_slat_sample(struct thread_data *td, enum fio_ddir ddir, { struct thread_stat *ts = &td->ts; + if (!ddir_rw(ddir)) + return; + add_stat_sample(&ts->slat_stat[ddir], usec); if (ts->slat_log) @@ -770,6 +980,9 @@ void add_lat_sample(struct thread_data *td, enum fio_ddir ddir, { struct thread_stat *ts = &td->ts; + if (!ddir_rw(ddir)) + return; + add_stat_sample(&ts->lat_stat[ddir], usec); if (ts->lat_log) @@ -780,9 +993,12 @@ void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs, struct timeval *t) { struct thread_stat *ts = &td->ts; - unsigned long spent = mtime_since(&ts->stat_sample_time[ddir], t); - unsigned long rate; + unsigned long spent, rate; + + if (!ddir_rw(ddir)) + return; + spent = mtime_since(&ts->stat_sample_time[ddir], t); if (spent < td->o.bw_avg_time) return;