Skip to content

Commit

Permalink
latency-percentiles to affect latency settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Lev Walkin committed Sep 20, 2016
1 parent 0ef832e commit ce7556c
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 144 deletions.
3 changes: 2 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

0.9.*:
* Export --latency-connect and --latency-first-bytes to statsd.
* Export --latency-connect and --latency-first-bytes to statsd.
* --latency-percentiles now affect --statsd reporting as well.

0.9: 2016-Aug-24
* Added -r@<Latency> form to measure message rate at a given latency.
Expand Down
117 changes: 77 additions & 40 deletions src/tcpkali.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ struct multiplier {
};
static double parse_with_multipliers(const char *, char *str,
struct multiplier *, int n);
static int parse_array_of_doubles(const char *option, char *str,
struct array_of_doubles *array);
static int parse_percentile_values(const char *option, char *str,
struct percentile_values *array);

/* clang-format off */
static struct multiplier km_multiplier[] = { { "k", 1000 }, { "m", 1000000 } };
Expand Down Expand Up @@ -190,15 +190,16 @@ main(int argc, char **argv) {
srandom(time(NULL));
#endif

struct array_of_doubles latency_percentiles = {
.size = 0, .doubles = NULL,
struct percentile_values latency_percentiles = {
.size = 0, .values = NULL,
};

while(1) {
char *option = argv[optind];
int longindex = -1;
int c;
c = getopt_long(argc, argv, "dhc:e1:m:f:r:l:vw:T:", cli_long_options,
NULL);
&longindex);
if(c == -1) break;
switch(c) {
case 'V':
Expand Down Expand Up @@ -511,13 +512,13 @@ main(int argc, char **argv) {
engine_params.websocket_enable = 1;
break;
case CLI_LATENCY + 'c': /* --latency-connect */
engine_params.latency_setting |= LMEASURE_CONNECT;
engine_params.latency_setting |= SLT_CONNECT;
break;
case CLI_LATENCY + 'f': /* --latency-first-byte */
engine_params.latency_setting |= LMEASURE_FIRSTBYTE;
engine_params.latency_setting |= SLT_FIRSTBYTE;
break;
case CLI_LATENCY + 'm': { /* --latency-marker */
engine_params.latency_setting |= LMEASURE_MARKER;
engine_params.latency_setting |= SLT_MARKER;
char *data = strdup(optarg);
size_t size = strlen(optarg);
if(unescape_message_data) unescape_data(data, &size);
Expand Down Expand Up @@ -545,18 +546,9 @@ main(int argc, char **argv) {
}
} break;
case CLI_LATENCY + 'p': { /* --latency-percentiles */
if(parse_array_of_doubles(option, optarg, &latency_percentiles))
if(parse_percentile_values(cli_long_options[longindex].name,
optarg, &latency_percentiles))
exit(EX_USAGE);
for(size_t i = 0; i < latency_percentiles.size; i++) {
double number = latency_percentiles.doubles[i];
if(number < 0.0 || number > 100.0) {
fprintf(
stderr,
"--latency-percentiles: "
"Percentile number should be within [0..100] range\n");
exit(EX_USAGE);
}
}
} break;
case 'I': { /* --source-ip */
if(add_source_ip(&engine_params.source_addresses, optarg) < 0) {
Expand Down Expand Up @@ -790,8 +782,17 @@ main(int argc, char **argv) {
}

/* Which latency types to report to statsd */
statsd_report_latency_types requested_latency_types =
engine_reports_latency_types(&engine_params);
statsd_report_latency_types requested_latency_types = engine_params.latency_setting;

if(requested_latency_types && !latency_percentiles.size) {
static struct percentile_value percentile_values[] = {
{ 95, "95" }, { 99, "99" }, { 99.5, "99.5" } };
static struct percentile_values pvs = {
.size = sizeof(percentile_values) / sizeof(percentile_values[1]),
.values = percentile_values
};
latency_percentiles = pvs;
}

/*
* Initialize statsd library and push initial (empty) metrics.
Expand All @@ -801,7 +802,7 @@ main(int argc, char **argv) {
statsd_new(&statsd, conf.statsd_host, conf.statsd_port,
conf.statsd_namespace, NULL);
/* Clear up traffic numbers, for better graphing. */
report_to_statsd(statsd, 0, requested_latency_types);
report_to_statsd(statsd, 0, requested_latency_types, &latency_percentiles);
} else {
statsd = 0;
}
Expand Down Expand Up @@ -836,7 +837,8 @@ main(int argc, char **argv) {
if(open_connections_until_maxed_out(
eng, conf.connect_rate, conf.max_connections, epoch_end,
&checkpoint, traffic_mavgs, statsd, &term_flag,
PHASE_ESTABLISHING_CONNECTIONS, &rate_modulator, print_stats)
PHASE_ESTABLISHING_CONNECTIONS, &rate_modulator,
&latency_percentiles, print_stats)
== OC_CONNECTED) {
fprintf(stderr, "%s", tcpkali_clear_eol());
fprintf(stderr, "Ramped up to %d connections.\n",
Expand All @@ -849,7 +851,7 @@ main(int argc, char **argv) {
conf.max_connections, conf.max_connections == 1 ? "" : "s",
conf.test_duration);
/* Level down graphs/charts. */
report_to_statsd(statsd, 0, requested_latency_types);
report_to_statsd(statsd, 0, requested_latency_types, &latency_percentiles);
exit(1);
}
}
Expand All @@ -868,16 +870,14 @@ main(int argc, char **argv) {
enum oc_return_value orv = open_connections_until_maxed_out(
eng, conf.connect_rate, conf.max_connections, epoch_end, &checkpoint,
traffic_mavgs, statsd, &term_flag, PHASE_STEADY_STATE, &rate_modulator,
print_stats);
&latency_percentiles, print_stats);

fprintf(stderr, "%s", tcpkali_clear_eol());
engine_terminate(eng, checkpoint.epoch_start,
checkpoint.initial_traffic_stats, latency_percentiles);

free(latency_percentiles.doubles);
checkpoint.initial_traffic_stats, &latency_percentiles);

/* Send zeroes, otherwise graphs would continue showing non-zeroes... */
report_to_statsd(statsd, 0, requested_latency_types);
report_to_statsd(statsd, 0, requested_latency_types, &latency_percentiles);

switch(orv) {
case OC_CONNECTED:
Expand Down Expand Up @@ -939,33 +939,70 @@ parse_with_multipliers(const char *option, char *str, struct multiplier *ms,
}

static int
parse_array_of_doubles(const char *option, char *str,
struct array_of_doubles *array) {
double *doubles = array->doubles;
parse_percentile_values(const char *option, char *str,
struct percentile_values *array) {
struct percentile_value *values = array->values;
size_t size = array->size;

for(char *pos = str; *pos; pos++) {
const char *start = pos;
char *endpos;
double got = strtod(pos, &endpos);
if(pos == endpos) {
fprintf(stderr, "%s: Failed to parse: bad number\n", option);

double value_d = strtod(start, &endpos);

if(start == endpos) {
fprintf(stderr, "--%s: %s: Failed to parse: bad number\n", option, str);
return -1;
}
if(value_d < 0 || !isfinite(value_d) || value_d > 100) {
fprintf(stderr, "--%s: %s: Failed to parse: bad latency percentile specification"
", expected range is [0..100]\n", option, str);
return -1;
}
for(; pos < endpos; pos++) {
switch(*pos) {
case '+': case ' ':
assert(pos == start);
start++;
continue;
case '.':
if(endpos - pos == 1) {
fprintf(stderr, "--%s: %s: Fractional part is missing\n", option, str);
return -1;
}
continue;
case '0' ... '9':
continue;
default:
fprintf(stderr, "--%s: %s: Number should contain only digits\n", option, str);
return -1;
}
break;
}
if(endpos - start >= (ssize_t)sizeof(values[0].value_s)) {
fprintf(stderr, "--%s: %s: Unreasonably precise percentile specification\n", option, str);
return -1;
}

if(*endpos != 0 && *endpos != ',' && *endpos != '/') {
fprintf(stderr,
"%s: Failed to parse: "
"--%s: %s: Failed to parse: "
"invalid separator, use ',' or '/'\n",
option);
option, str);
return -1;
}
doubles = realloc(doubles, ++size * sizeof(double));
doubles[size - 1] = got;

values = realloc(values, ++size * sizeof(values[0]));
assert(values);
values[size - 1].value_d = value_d;
memcpy(values[size - 1].value_s, start, endpos-start);
values[size - 1].value_s[endpos - start] = '\0';

pos = endpos;
if(*pos == 0) break;
}

array->doubles = doubles;
array->values = values;
array->size = size;
return 0;
}
Expand Down
20 changes: 20 additions & 0 deletions src/tcpkali_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,24 @@
#define UNUSED __attribute__((unused))
#define PRINTFLIKE(n, m) __attribute__((format(printf, n, m)))

/*
* Requested types of latency measurement.
*/
typedef enum {
SLT_CONNECT = (1 << 0),
SLT_FIRSTBYTE = (1 << 1),
SLT_MARKER = (1 << 2)
} statsd_report_latency_types;

/*
* Array of doubles used in e.g. overriding reported latency percentiles.
*/
struct percentile_values {
size_t size;
struct percentile_value {
double value_d;
char value_s[sizeof("100.123")];
} *values;
};

#endif /* TCPKALI_COMMON_H */
41 changes: 16 additions & 25 deletions src/tcpkali_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,21 +393,21 @@ engine_start(struct engine_params params) {
largs->thread_no = n;
largs->serialize_output_lock = &eng->serialize_output_lock;
const int decims_in_1s = 10 * 1000; /* decimilliseconds, 1/10 ms */
if(params.latency_setting & LMEASURE_CONNECT) {
if(params.latency_setting & SLT_CONNECT) {
int ret = hdr_init(
1, /* 1/10 milliseconds is the lowest storable value. */
100 * decims_in_1s, /* 100 seconds is a max storable value */
3, &largs->connect_histogram_local);
assert(ret == 0);
}
if(params.latency_setting & LMEASURE_FIRSTBYTE) {
if(params.latency_setting & SLT_FIRSTBYTE) {
int ret = hdr_init(
1, /* 1/10 milliseconds is the lowest storable value. */
100 * decims_in_1s, /* 100 seconds is a max storable value */
3, &largs->firstbyte_histogram_local);
assert(ret == 0);
}
if(params.latency_setting & LMEASURE_MARKER) {
if(params.latency_setting & SLT_MARKER) {
int ret = hdr_init(
1, /* 1/10 milliseconds is the lowest storable value. */
100 * decims_in_1s, /* 100 seconds is a max storable value */
Expand Down Expand Up @@ -442,48 +442,39 @@ engine_start(struct engine_params params) {
*/
static void
print_latency_hdr_histrogram_percentiles(
char *title, struct array_of_doubles want_percentiles,
const char *title, const struct percentile_values *report_percentiles,
struct hdr_histogram *histogram) {
assert(histogram);

double *percentiles = want_percentiles.doubles;
size_t size = want_percentiles.size;

double default_percentiles[] = {95.0, 99.0, 99.5};
size_t default_size = sizeof(default_percentiles) / sizeof(double);

if(size == 0) {
size = default_size;
percentiles = default_percentiles;
}
size_t size = report_percentiles->size;

printf("%s latency at percentiles: ", title);
for(size_t i = 0; i < size; i++) {
double perc = percentiles[i];
printf("%.1f%s", hdr_value_at_percentile(histogram, perc) / 10.0,
double per_d = report_percentiles->values[i].value_d;
printf("%.1f%s", hdr_value_at_percentile(histogram, per_d) / 10.0,
i == size - 1 ? "" : "/");
}
printf(" (");
for(size_t i = 0; i < size; i++) {
double perc = percentiles[i];
printf("%.1f%s", perc, i == size - 1 ? "" : "/");
printf("%s%s", report_percentiles->values[i].value_s,
i == size - 1 ? "" : "/");
}
printf("%%)\n");
}

static void
latency_snapshot_print(struct array_of_doubles want_percentiles,
struct latency_snapshot *latency) {
latency_snapshot_print(const struct percentile_values *latency_percentiles,
const struct latency_snapshot *latency) {
if(latency->connect_histogram) {
print_latency_hdr_histrogram_percentiles(
"TCP connect", want_percentiles, latency->connect_histogram);
"TCP connect", latency_percentiles, latency->connect_histogram);
}
if(latency->firstbyte_histogram) {
print_latency_hdr_histrogram_percentiles("First byte", want_percentiles,
print_latency_hdr_histrogram_percentiles("First byte", latency_percentiles,
latency->firstbyte_histogram);
}
if(latency->marker_histogram) {
print_latency_hdr_histrogram_percentiles("Message", want_percentiles,
print_latency_hdr_histrogram_percentiles("Message", latency_percentiles,
latency->marker_histogram);
}
}
Expand Down Expand Up @@ -520,7 +511,7 @@ engine_update_message_send_rate(struct engine *eng, double msg_rate) {
void
engine_terminate(struct engine *eng, double epoch,
non_atomic_traffic_stats initial_traffic_stats,
struct array_of_doubles want_latency_percentiles) {
struct percentile_values *latency_percentiles) {
size_t connecting, conn_in, conn_out, conn_counter;

engine_get_connection_stats(eng, &connecting, &conn_in, &conn_out,
Expand Down Expand Up @@ -583,7 +574,7 @@ engine_terminate(struct engine *eng, double epoch,
epoch_traffic.bytes_rcvd),
estimate_segments_per_op(epoch_traffic.num_writes,
epoch_traffic.bytes_sent));
latency_snapshot_print(want_latency_percentiles, latency);
latency_snapshot_print(latency_percentiles, latency);

engine_free_latency_snapshot(latency);
printf("Test duration: %g s.\n", test_duration);
Expand Down
13 changes: 2 additions & 11 deletions src/tcpkali_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,13 @@ struct engine_params {
DS_DUMP_ALL_OUT = 8,
DS_DUMP_ALL = 12 /* 8|4 */
} dump_setting;
enum {
LMEASURE_CONNECT = (1 << 0),
LMEASURE_FIRSTBYTE = (1 << 1),
LMEASURE_MARKER = (1 << 2)
} latency_setting;
statsd_report_latency_types latency_setting;
tk_expr_t *latency_marker; /* --latency-marker */
int latency_marker_skip; /* --latency-marker-skip <N> */

struct StreamBMH_Occ sbmh_shared_occ; /* Streaming Boyer-Moore-Horspool */
};

struct array_of_doubles {
size_t size;
double *doubles;
};

struct engine *engine_start(struct engine_params);
const struct engine_params *engine_params(struct engine *);
void engine_update_message_send_rate(struct engine *, double msg_rate);
Expand Down Expand Up @@ -130,6 +121,6 @@ void engine_terminate(struct engine *, double epoch_start,
/* Traffic observed during ramp-up phase */
non_atomic_traffic_stats initial_traffic,
/* Report latencies at specified %'iles */
struct array_of_doubles want_latency_percentiles);
struct percentile_values *report_latency_percentiles);

#endif /* TCPKALI_ENGINE_H */
Loading

0 comments on commit ce7556c

Please sign in to comment.