From d0582223a24891d5145455ec26a966760e60b7be Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 8 May 2013 20:19:27 +0900 Subject: [PATCH] add option to print latency distribution --- src/stats.c | 33 ++++++++++++++++----------------- src/stats.h | 5 ++--- src/wrk.c | 26 +++++++++++++++++++++++--- src/wrk.h | 1 + 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/stats.c b/src/stats.c index a114917..9f27c67 100644 --- a/src/stats.c +++ b/src/stats.c @@ -23,27 +23,21 @@ void stats_record(stats *stats, uint64_t x) { if (stats->index == stats->samples) stats->index = 0; } -uint64_t stats_min(stats *stats) { - uint64_t min = 0; - for (uint64_t i = 0; i < stats->limit; i++) { - uint64_t x = stats->data[i]; - if (x < min || min == 0) min = x; - } - return min; +static int stats_compare(const void *a, const void *b) { + uint64_t *x = (uint64_t *) a; + uint64_t *y = (uint64_t *) b; + return *x - *y; } -uint64_t stats_max(stats *stats) { - uint64_t max = 0; - for (uint64_t i = 0; i < stats->limit; i++) { - uint64_t x = stats->data[i]; - if (x > max || max == 0) max = x; - } - return max; -} +long double stats_summarize(stats *stats, int64_t *min, uint64_t *max) { + qsort(stats->data, stats->limit, sizeof(uint64_t), &stats_compare); + + if (min) *min = stats->data[0]; + if (max) *max = stats->data[stats->limit - 1]; -long double stats_mean(stats *stats) { - uint64_t sum = 0; if (stats->limit == 0) return 0.0; + + uint64_t sum = 0; for (uint64_t i = 0; i < stats->limit; i++) { sum += stats->data[i]; } @@ -71,3 +65,8 @@ long double stats_within_stdev(stats *stats, long double mean, long double stdev return (sum / (long double) stats->limit) * 100; } + +uint64_t stats_percentile(stats *stats, long double p) { + uint64_t rank = round((p / 100.0) * stats->limit + 0.5); + return stats->data[rank - 1]; +} diff --git a/src/stats.h b/src/stats.h index 04a9393..20be205 100644 --- a/src/stats.h +++ b/src/stats.h @@ -11,11 +11,10 @@ typedef struct { stats *stats_alloc(uint64_t); void stats_free(stats *); void stats_record(stats *, uint64_t); -uint64_t stats_min(stats *); -uint64_t stats_max(stats *); -long double stats_mean(stats *); +long double stats_summarize(stats *, int64_t *, uint64_t *); long double stats_stdev(stats *stats, long double); long double stats_within_stdev(stats *, long double, long double, uint64_t); +uint64_t stats_percentile(stats *, long double); #endif /* STATS_H */ diff --git a/src/wrk.c b/src/wrk.c index 09cb007..6532e28 100644 --- a/src/wrk.c +++ b/src/wrk.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ static struct config { uint64_t connections; uint64_t duration; uint64_t timeout; + bool latency; } cfg; static struct { @@ -64,6 +66,7 @@ static void usage() { " -t, --threads Number of threads to use \n" " \n" " -H, --header Add header to request \n" + " --latency Print latency statistics \n" " --timeout Socket/request timeout \n" " -v, --version Print version details \n" " \n" @@ -187,6 +190,7 @@ int main(int argc, char **argv) { print_stats_header(); print_stats("Latency", statistics.latency, format_time_us); print_stats("Req/Sec", statistics.requests, format_metric); + if (cfg.latency) print_stats_latency(statistics.latency); char *runtime_msg = format_time_us(runtime_us); @@ -428,6 +432,7 @@ static struct option longopts[] = { { "duration", required_argument, NULL, 'd' }, { "threads", required_argument, NULL, 't' }, { "header", required_argument, NULL, 'H' }, + { "latency", no_argument, NULL, 'L' }, { "timeout", required_argument, NULL, 'T' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, @@ -443,7 +448,7 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc, cfg->duration = 10; cfg->timeout = SOCKET_TIMEOUT_MS; - while ((c = getopt_long(argc, argv, "t:c:d:H:T:rv?", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "t:c:d:H:T:Lrv?", longopts, NULL)) != -1) { switch (c) { case 't': if (scan_metric(optarg, &cfg->threads)) return -1; @@ -457,6 +462,9 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc, case 'H': *header++ = optarg; break; + case 'L': + cfg->latency = true; + break; case 'T': if (scan_time(optarg, &cfg->timeout)) return -1; cfg->timeout *= 1000; @@ -506,8 +514,8 @@ static void print_units(long double n, char *(*fmt)(long double), int width) { } static void print_stats(char *name, stats *stats, char *(*fmt)(long double)) { - long double mean = stats_mean(stats); - long double max = stats_max(stats); + uint64_t max; + long double mean = stats_summarize(stats, NULL, &max); long double stdev = stats_stdev(stats, mean); printf(" %-10s", name); @@ -516,3 +524,15 @@ static void print_stats(char *name, stats *stats, char *(*fmt)(long double)) { print_units(max, fmt, 9); printf("%8.2Lf%%\n", stats_within_stdev(stats, mean, stdev, 1)); } + +static void print_stats_latency(stats *stats) { + long double percentiles[] = { 50.0, 75.0, 90.0, 99.0 }; + printf(" Latency Distribution\n"); + for (size_t i = 0; i < sizeof(percentiles) / sizeof(long double); i++) { + long double p = percentiles[i]; + uint64_t n = stats_percentile(stats, p); + printf("%7.0Lf%%", p); + print_units(n, format_time_us, 10); + printf("\n"); + } +} diff --git a/src/wrk.h b/src/wrk.h index 55f8634..114ee74 100644 --- a/src/wrk.h +++ b/src/wrk.h @@ -71,5 +71,6 @@ static char *format_request(char *, char *, char *, char **); static int parse_args(struct config *, char **, char **, int, char **); static void print_stats_header(); static void print_stats(char *, stats *, char *(*)(long double)); +static void print_stats_latency(stats *); #endif /* WRK_H */