From patchwork Wed Mar 7 17:01:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 882691 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxKhQ2B0Zz9sf4 for ; Thu, 8 Mar 2018 04:02:46 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id B15941297; Wed, 7 Mar 2018 17:01:21 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 6AD1B113D for ; Wed, 7 Mar 2018 17:01:18 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id E04D25E2 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3957C76FB9 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from monae.redhat.com (ovpn-124-182.rdu2.redhat.com [10.10.124.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id 16007215CDA7 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 7 Mar 2018 11:01:11 -0600 Message-Id: <20180307170115.29137-2-mmichels@redhat.com> In-Reply-To: <20180307170115.29137-1-mmichels@redhat.com> References: <20180307170115.29137-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mmichels@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v6 1/5] Add stopwatch timing API X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This is similar to the existing coverage and perf-counter APIs in OVS. However, rather than keeping counters, this is aimed at timing how long operations take to perform. "Operations" in this case can be anything from a loop iteration, to a function, to something more complex. The library allows for named stopwatches to be created. From there, the stopwatch can be started and stopped via stopwatch_start() and stopwatch_stop(). After each run, statistics for the stopwatch will be calculated. Statistics for a particular stopwatch can be queried from the command line by using ovs-appctl -t stopwatch/show . Statistics can be reset from the command line using ovs-appctl -t stopwatch/reset Signed-off-by: Mark Michelson Acked-by: Han Zhou --- lib/automake.mk | 2 + lib/stopwatch.c | 497 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/stopwatch.h | 41 +++++ 3 files changed, 540 insertions(+) create mode 100644 lib/stopwatch.c create mode 100644 lib/stopwatch.h diff --git a/lib/automake.mk b/lib/automake.mk index 5c26e0f33..7784057c9 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -222,6 +222,8 @@ lib_libopenvswitch_la_SOURCES = \ lib/pcap-file.h \ lib/perf-counter.h \ lib/perf-counter.c \ + lib/stopwatch.h \ + lib/stopwatch.c \ lib/poll-loop.c \ lib/process.c \ lib/process.h \ diff --git a/lib/stopwatch.c b/lib/stopwatch.c new file mode 100644 index 000000000..9b7847bf5 --- /dev/null +++ b/lib/stopwatch.c @@ -0,0 +1,497 @@ +/* Copyright (c) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "stopwatch.h" +#include "openvswitch/shash.h" +#include "openvswitch/vlog.h" +#include "unixctl.h" +#include "openvswitch/dynamic-string.h" +#include "openvswitch/poll-loop.h" +#include "ovs-thread.h" +#include +#include "socket-util.h" + +VLOG_DEFINE_THIS_MODULE(stopwatch); + +struct average { + double average; /* Moving average */ + double alpha; /* Weight given to new samples */ +}; + +#define MARKERS 5 + +/* Number of samples to collect before reporting P-square calculated + * percentile + */ +#define P_SQUARE_MIN 50 + +/* The naming of these fields is based on the naming used in the + * P-square algorithm paper. + */ +struct percentile { + int n[MARKERS]; + double n_prime[MARKERS]; + double q[MARKERS]; + double dn[MARKERS]; + unsigned long long samples[P_SQUARE_MIN]; + double percentile; +}; + +struct stopwatch { + enum stopwatch_units units; + unsigned long long n_samples; + unsigned long long max; + unsigned long long min; + struct percentile pctl; + struct average short_term; + struct average long_term; + unsigned long long sample_start; + bool sample_in_progress; +}; + +enum stopwatch_op { + OP_START_SAMPLE, + OP_END_SAMPLE, + OP_RESET, + OP_SHUTDOWN, +}; + +struct stopwatch_packet { + enum stopwatch_op op; + char name[32]; + unsigned long long time; +}; + +static struct shash stopwatches = SHASH_INITIALIZER(&stopwatches); +static struct ovs_mutex stopwatches_lock = OVS_MUTEX_INITIALIZER; + +static int stopwatch_pipe[2]; +static pthread_t stopwatch_thread_id; + +static const char *unit_name[] = { + [SW_MS] = "msec", + [SW_US] = "usec", + [SW_NS] = "nsec", +}; + +/* Percentile value we are calculating */ +#define P 0.95 + +static int +comp_samples(const void *left, const void *right) +{ + const double *left_d = left; + const double *right_d = right; + + return (int) *right_d - *left_d; +} + +/* Calculate the percentile using the P-square algorithm. For more + * information, see https://www1.cse.wustl.edu/~jain/papers/ftp/psqr.pdf + */ +static void +calc_percentile(unsigned long long n_samples, struct percentile *pctl, + unsigned long long new_sample) +{ + + if (n_samples < P_SQUARE_MIN) { + pctl->samples[n_samples - 1] = new_sample; + } + + /* For the first MARKERS samples, we calculate the percentile + * in the traditional way in the pct->q array. + */ + if (n_samples <= MARKERS) { + pctl->q[n_samples - 1] = new_sample; + qsort(pctl->q, n_samples, sizeof *pctl->q, comp_samples); + if (n_samples == MARKERS) { + pctl->n[0] = 0; + pctl->n[1] = 1; + pctl->n[2] = 2; + pctl->n[3] = 3; + pctl->n[4] = 4; + + pctl->n_prime[0] = 0; + pctl->n_prime[1] = 2 * P; + pctl->n_prime[2] = 4 * P; + pctl->n_prime[3] = 2 + 2 * P; + pctl->n_prime[4] = 4; + + pctl->dn[0] = 0; + pctl->dn[1] = P / 2; + pctl->dn[2] = P; + pctl->dn[3] = (1 + P) / 2; + pctl->dn[4] = 1; + } + pctl->percentile = pctl->q[(int) P * n_samples]; + return; + } + + /* From here on, update the markers using quadratic spline calculations */ + int k; + if (new_sample < pctl->q[0]) { + k = 0; + pctl->q[0] = new_sample; + } else if (new_sample < pctl->q[1]) { + k = 0; + } else if (new_sample < pctl->q[2]) { + k = 1; + } else if (new_sample < pctl->q[3]) { + k = 2; + } else if (new_sample <= pctl->q[4]) { + k = 3; + } else { + k = 3; + pctl->q[4] = new_sample; + } + + for (int i = k + 1; i < MARKERS; i++) { + pctl->n[i]++; + } + + for (int i = 0; i < MARKERS; i++) { + pctl->n_prime[i] += pctl->dn[i]; + } + + for (int i = 1; i < MARKERS - 1; i++) { + double d = pctl->n_prime[i] - pctl->n[i]; + + if ((d >= 1 && pctl->n[i + 1] - pctl->n[i] > 1) || + (d <= -1 && pctl->n[i - 1] - pctl->n[i] < -1)) { + d = d >= 0 ? 1 : -1; + + double a = d / (pctl->n[i + 1] - pctl->n[i - 1]); + double b = (pctl->n[i] - pctl->n[i - 1] + d) * + (pctl->q[i + 1] - pctl->q[i]) / (pctl->n[i + 1] - pctl->n[i]); + double c = (pctl->n[i + 1] - pctl->n[i] - d) * + (pctl->q[i] - pctl->q[i - 1]) / (pctl->n[i] - pctl->n[i - 1]); + + double candidate = pctl->q[i] + a * (b + c); + if (pctl->q[i - 1] < candidate && candidate < pctl->q[i + 1]) { + pctl->q[i] = candidate; + } else { + pctl->q[i] = pctl->q[i] + + (d * (pctl->q[i + (int)d] - pctl->q[i]) / + (pctl->n[i +(int)d] - pctl->n[i])); + } + + pctl->n[i] += d; + } + } + + /* Without enough samples, P-square is not very accurate. Until we reach + * P_SQUARE_MIN, use a traditional calculation for the percentile. + */ + if (n_samples < P_SQUARE_MIN) { + qsort(pctl->samples, n_samples, sizeof *pctl->samples, comp_samples); + pctl->percentile = pctl->samples[(int) (P * n_samples)]; + } else { + pctl->percentile = pctl->q[2]; + } +} + +static void +calc_average(struct average *avg, double new_sample) +{ + avg->average = new_sample * avg->alpha + (1 - avg->alpha) * avg->average; +} + +static void +add_sample(struct stopwatch *sw, unsigned long long new_sample) +{ + if (new_sample > sw->max) { + sw->max = new_sample; + } + + if (new_sample < sw->min || sw->n_samples == 0) { + sw->min = new_sample; + } + + calc_percentile(sw->n_samples, &sw->pctl, new_sample); + + if (sw->n_samples++ == 0) { + sw->short_term.average = sw->long_term.average = new_sample; + return; + } + + calc_average(&sw->short_term, new_sample); + calc_average(&sw->long_term, new_sample); +} + +static void +stopwatch_print(struct stopwatch *sw, const char *name, + struct ds *s) +{ + ds_put_format(s, "Statistics for '%s'\n", name); + + const char *units = unit_name[sw->units]; + ds_put_format(s, "\t Total samples: %llu\n", sw->n_samples); + ds_put_format(s, "\t Maximum: %llu %s\n", sw->max, units); + ds_put_format(s, "\t Minimum: %llu %s\n", sw->min, units); + ds_put_format(s, "\t 95th percentile: %f %s\n", + sw->pctl.percentile, units); + ds_put_format(s, "\t Short term average: %f %s\n", + sw->short_term.average, units); + ds_put_format(s, "\t Long term average: %f %s\n", + sw->long_term.average, units); +} + +static bool +stopwatch_show_protected(int argc, const char *argv[], struct ds *s) +{ + struct stopwatch *sw; + + if (argc > 1) { + sw = shash_find_data(&stopwatches, argv[1]); + if (!sw) { + ds_put_cstr(s, "No such stopwatch"); + return false; + } + stopwatch_print(sw, argv[1], s); + } else { + struct shash_node *node; + SHASH_FOR_EACH (node, &stopwatches) { + sw = node->data; + stopwatch_print(sw, node->name, s); + } + } + + return true; +} + +static void +stopwatch_show(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *ignore OVS_UNUSED) +{ + struct ds s = DS_EMPTY_INITIALIZER; + bool success; + + ovs_mutex_lock(&stopwatches_lock); + success = stopwatch_show_protected(argc, argv, &s); + ovs_mutex_unlock(&stopwatches_lock); + + if (success) { + unixctl_command_reply(conn, ds_cstr(&s)); + } else { + unixctl_command_reply_error(conn, ds_cstr(&s)); + } + ds_destroy(&s); +} + +static void +stopwatch_reset(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *ignore OVS_UNUSED) +{ + struct stopwatch_packet pkt = { + .op = OP_RESET, + }; + if (argc > 1) { + ovs_strlcpy(pkt.name, argv[1], sizeof(pkt.name)); + } + write(stopwatch_pipe[1], &pkt, sizeof(pkt)); + unixctl_command_reply(conn, ""); +} + +static bool +stopwatch_start_sample_protected(const struct stopwatch_packet *pkt) +{ + struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name); + if (!sw) { + return false; + } + + /* We already started sampling. Need an end before + * we start another sample + */ + if (sw->sample_in_progress) { + return false; + } + + sw->sample_start = pkt->time; + sw->sample_in_progress = true; + return true; +} + +static bool +stopwatch_end_sample_protected(const struct stopwatch_packet *pkt) +{ + struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name); + if (!sw) { + return false; + } + + /* We can't end a sample if we haven't started one */ + if (!sw->sample_in_progress) { + return false; + } + + add_sample(sw, pkt->time - sw->sample_start); + sw->sample_in_progress = false; + return true; +} + +static void reset_stopwatch(struct stopwatch *sw) +{ + sw->short_term.average = 0; + sw->long_term.average = 0; + sw->pctl.percentile = 0; + sw->n_samples = 0; + sw->max = 0; + sw->min = 0; + /* Don't reset sw->sample_start or sw->sample_in_progress. + * This way, if a sample was currently in progress, it can be + * concluded properly after the reset. + */ +} + +static void +stopwatch_reset_protected(const struct stopwatch_packet *pkt) +{ + if (pkt->name[0]) { + struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name); + if (!sw) { + return; + } + reset_stopwatch(sw); + return; + } + + struct shash_node *node; + SHASH_FOR_EACH (node, &stopwatches) { + struct stopwatch *sw = node->data; + reset_stopwatch(sw); + } +} + +static void * +stopwatch_thread(void *ign OVS_UNUSED) +{ + bool should_exit = false; + + while (!should_exit) { + struct stopwatch_packet pkt; + while (read(stopwatch_pipe[0], &pkt, sizeof(pkt)) > 0) { + ovs_mutex_lock(&stopwatches_lock); + switch (pkt.op) { + case OP_START_SAMPLE: + stopwatch_start_sample_protected(&pkt); + break; + case OP_END_SAMPLE: + stopwatch_end_sample_protected(&pkt); + break; + case OP_RESET: + stopwatch_reset_protected(&pkt); + break; + case OP_SHUTDOWN: + should_exit = true; + break; + } + ovs_mutex_unlock(&stopwatches_lock); + } + + if (!should_exit) { + poll_fd_wait(stopwatch_pipe[0], POLLIN); + poll_block(); + } + } + + return NULL; +} + +static void +stopwatch_exit(void) +{ + struct shash_node *node, *node_next; + struct stopwatch_packet pkt = { + .op = OP_SHUTDOWN, + }; + + write(stopwatch_pipe[1], &pkt, sizeof pkt); + xpthread_join(stopwatch_thread_id, NULL); + + /* Process is exiting and we have joined the only + * other competing thread. We are now the sole owners + * of all data in the file. + */ + SHASH_FOR_EACH_SAFE (node, node_next, &stopwatches) { + struct stopwatch *sw = node->data; + shash_delete(&stopwatches, node); + free(sw); + } + shash_destroy(&stopwatches); + ovs_mutex_destroy(&stopwatches_lock); +} + +static void +do_init_stopwatch(void) +{ + unixctl_command_register("stopwatch/show", "[NAME]", 0, 1, + stopwatch_show, NULL); + unixctl_command_register("stopwatch/reset", "[NAME]", 0, 1, + stopwatch_reset, NULL); + xpipe_nonblocking(stopwatch_pipe); + stopwatch_thread_id = ovs_thread_create( + "stopwatch", stopwatch_thread, NULL); + atexit(stopwatch_exit); +} + +static void +stopwatch_init(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + if (ovsthread_once_start(&once)) { + do_init_stopwatch(); + ovsthread_once_done(&once); + } +} + +void +stopwatch_create(const char *name, enum stopwatch_units units) +{ + stopwatch_init(); + + struct stopwatch *sw = xzalloc(sizeof *sw); + sw->units = units; + sw->short_term.alpha = 0.50; + sw->long_term.alpha = 0.01; + + ovs_mutex_lock(&stopwatches_lock); + shash_add(&stopwatches, name, sw); + ovs_mutex_unlock(&stopwatches_lock); +} + +void +stopwatch_start(const char *name, unsigned long long ts) +{ + struct stopwatch_packet pkt = { + .op = OP_START_SAMPLE, + .time = ts, + }; + ovs_strlcpy(pkt.name, name, sizeof(pkt.name)); + write(stopwatch_pipe[1], &pkt, sizeof(pkt)); +} + +void +stopwatch_stop(const char *name, unsigned long long ts) +{ + struct stopwatch_packet pkt = { + .op = OP_END_SAMPLE, + .time = ts, + }; + ovs_strlcpy(pkt.name, name, sizeof(pkt.name)); + write(stopwatch_pipe[1], &pkt, sizeof(pkt)); +} diff --git a/lib/stopwatch.h b/lib/stopwatch.h new file mode 100644 index 000000000..61f814523 --- /dev/null +++ b/lib/stopwatch.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STOPWATCH_H +#define STOPWATCH_H 1 + +#include + +enum stopwatch_units { + SW_MS, + SW_US, + SW_NS, +}; + +/* Create a new stopwatch. + * The "units" are not used for any calculations but are printed when + * statistics are requested. + */ +void stopwatch_create(const char *name, enum stopwatch_units units); + +/* Start a stopwatch. */ +void stopwatch_start(const char *name, unsigned long long ts); + +/* Stop a stopwatch. The elapsed time will be used for updating statistics + * for this stopwatch. + */ +void stopwatch_stop(const char *name, unsigned long long ts); + +#endif /* stopwatch.h */ From patchwork Wed Mar 7 17:01:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 882694 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxKk65c8Tz9sf4 for ; Thu, 8 Mar 2018 04:04:14 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8F52612A7; Wed, 7 Mar 2018 17:01:23 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id DAF691289 for ; Wed, 7 Mar 2018 17:01:18 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 23BC65E3 for ; Wed, 7 Mar 2018 17:01:17 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 608BC4023EFF for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from monae.redhat.com (ovpn-124-182.rdu2.redhat.com [10.10.124.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id 44D7A215CDA7 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 7 Mar 2018 11:01:12 -0600 Message-Id: <20180307170115.29137-3-mmichels@redhat.com> In-Reply-To: <20180307170115.29137-1-mmichels@redhat.com> References: <20180307170115.29137-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mmichels@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v6 2/5] Measure timing of ovn-controller loops. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This modifies ovn-controller to measure the amount of time it takes to detect a change in the southbound database and generate the resulting flow table. This may require multiple iterations of the ovn-controller loop. The statistics can be queried using: ovs-appctl -t ovn-controller stopwatch/show ovn-controller-loop The statistics can be reset using: ovs-appctl -t ovn-controller stopwatch/reset ovn-controller-loop Signed-off-by: Mark Michelson Acked-by: Han Zhou --- ovn/controller/ovn-controller.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 7592bda25..abd253fca 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -57,6 +57,9 @@ #include "stream.h" #include "unixctl.h" #include "util.h" +#include "timeval.h" +#include "timer.h" +#include "stopwatch.h" VLOG_DEFINE_THIS_MODULE(main); @@ -67,6 +70,8 @@ static unixctl_cb_func inject_pkt; #define DEFAULT_BRIDGE_NAME "br-int" #define DEFAULT_PROBE_INTERVAL_MSEC 5000 +#define CONTROLLER_LOOP_STOPWATCH_NAME "ovn-controller-loop" + static void update_probe_interval(struct controller_ctx *, const char *ovnsb_remote); static void parse_options(int argc, char *argv[]); @@ -639,8 +644,10 @@ main(int argc, char *argv[]) unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, &pending_pkt); + stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); /* Main loop. */ exiting = false; + unsigned int our_seqno = 0; while (!exiting) { /* Check OVN SB database. */ char *new_ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); @@ -659,6 +666,12 @@ main(int argc, char *argv[]) .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; + if (our_seqno != ovsdb_idl_get_seqno(ctx.ovnsb_idl)) { + stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); + our_seqno = ovsdb_idl_get_seqno(ctx.ovnsb_idl); + } + update_probe_interval(&ctx, ovnsb_remote); update_ssl_config(ctx.ovs_idl); @@ -728,6 +741,9 @@ main(int argc, char *argv[]) ofctrl_put(&flow_table, &pending_ct_zones, get_nb_cfg(ctx.ovnsb_idl)); + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); + hmap_destroy(&flow_table); } if (ctx.ovnsb_idl_txn) { @@ -792,6 +808,7 @@ main(int argc, char *argv[]) ofctrl_wait(); pinctrl_wait(&ctx); } + ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) { From patchwork Wed Mar 7 17:01:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 882693 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxKjG6p27z9sf4 for ; Thu, 8 Mar 2018 04:03:30 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 97C881291; Wed, 7 Mar 2018 17:01:22 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 9EB901289 for ; Wed, 7 Mar 2018 17:01:18 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 4B5E75E6 for ; Wed, 7 Mar 2018 17:01:17 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 880EAD142A for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from monae.redhat.com (ovpn-124-182.rdu2.redhat.com [10.10.124.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6BCDF215CDA7 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 7 Mar 2018 11:01:13 -0600 Message-Id: <20180307170115.29137-4-mmichels@redhat.com> In-Reply-To: <20180307170115.29137-1-mmichels@redhat.com> References: <20180307170115.29137-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mmichels@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v6 3/5] stopwatch: Add API for retrieving calculated statistics X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Jakub Sitnicki Will be used for testing the module. Signed-off-by: Jakub Sitnicki Signed-off-by: Mark Michelson --- lib/stopwatch.c | 34 ++++++++++++++++++++++++++++++++++ lib/stopwatch.h | 13 +++++++++++++ 2 files changed, 47 insertions(+) diff --git a/lib/stopwatch.c b/lib/stopwatch.c index 9b7847bf5..4f36c4692 100644 --- a/lib/stopwatch.c +++ b/lib/stopwatch.c @@ -232,6 +232,40 @@ add_sample(struct stopwatch *sw, unsigned long long new_sample) calc_average(&sw->long_term, new_sample); } +static bool +performance_get_stats_protected(const char *name, + struct performance_stats *stats) +{ + struct performance *perf; + + perf = shash_find_data(&performances, name); + if (!perf) { + return false; + } + + stats->count = perf->samples; + stats->unit = perf->units; + stats->max = perf->max; + stats->min = perf->min; + stats->pctl_95 = perf->pctl.percentile; + stats->ewma_50 = perf->short_term.average; + stats->ewma_1 = perf->long_term.average; + + return true; +} + +bool +performance_get_stats(const char *name, struct performance_stats *stats) +{ + bool found = false; + + ovs_mutex_lock(&performances_lock); + found = performance_get_stats_protected(name, stats); + ovs_mutex_unlock(&performances_lock); + + return found; +} + static void stopwatch_print(struct stopwatch *sw, const char *name, struct ds *s) diff --git a/lib/stopwatch.h b/lib/stopwatch.h index 61f814523..fac5de2c6 100644 --- a/lib/stopwatch.h +++ b/lib/stopwatch.h @@ -24,6 +24,16 @@ enum stopwatch_units { SW_NS, }; +struct stopwatch_stats { + unsigned long long count; /* Total number of samples. */ + enum stopwatch_units unit; /* Unit of following values. */ + unsigned long long max; /* Maximum value. */ + unsigned long long min; /* Minimum value. */ + double pctl_95; /* 95th percentile. */ + double ewma_50; /* Exponentially weighted moving average (alpha 0.50). */ + double ewma_1; /* Exponentially weighted moving average (alpha 0.01). */ +}; + /* Create a new stopwatch. * The "units" are not used for any calculations but are printed when * statistics are requested. @@ -38,4 +48,7 @@ void stopwatch_start(const char *name, unsigned long long ts); */ void stopwatch_stop(const char *name, unsigned long long ts); +/* Retrieve statistics calculated from collected samples */ +bool stopwatch_get_stats(const char *name, struct stopwatch_stats *stats); + #endif /* stopwatch.h */ From patchwork Wed Mar 7 17:01:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 882689 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxKgZ5R0gz9sf8 for ; Thu, 8 Mar 2018 04:02:02 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id CEBCB129A; Wed, 7 Mar 2018 17:01:19 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D9245128A for ; Wed, 7 Mar 2018 17:01:17 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 619325E7 for ; Wed, 7 Mar 2018 17:01:17 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B35D38151D47 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from monae.redhat.com (ovpn-124-182.rdu2.redhat.com [10.10.124.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id 92A8B215CDAC for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 7 Mar 2018 11:01:14 -0600 Message-Id: <20180307170115.29137-5-mmichels@redhat.com> In-Reply-To: <20180307170115.29137-1-mmichels@redhat.com> References: <20180307170115.29137-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mmichels@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v6 4/5] stopwatch: Add API for waiting until samples have been processed X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Jakub Sitnicki Will be used for testing the module. Signed-off-by: Jakub Sitnicki Signed-off-by: Mark Michelson --- lib/stopwatch.c | 34 ++++++++++++++++++++++++++-------- lib/stopwatch.h | 3 +++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/stopwatch.c b/lib/stopwatch.c index 4f36c4692..185f4a32f 100644 --- a/lib/stopwatch.c +++ b/lib/stopwatch.c @@ -66,6 +66,7 @@ struct stopwatch { enum stopwatch_op { OP_START_SAMPLE, OP_END_SAMPLE, + OP_SYNC, OP_RESET, OP_SHUTDOWN, }; @@ -78,6 +79,7 @@ struct stopwatch_packet { static struct shash stopwatches = SHASH_INITIALIZER(&stopwatches); static struct ovs_mutex stopwatches_lock = OVS_MUTEX_INITIALIZER; +static pthread_cond_t stopwatches_sync = PTHREAD_COND_INITIALIZER; static int stopwatch_pipe[2]; static pthread_t stopwatch_thread_id; @@ -233,12 +235,12 @@ add_sample(struct stopwatch *sw, unsigned long long new_sample) } static bool -performance_get_stats_protected(const char *name, - struct performance_stats *stats) +stopwatch_get_stats_protected(const char *name, + struct stopwatch_stats *stats) { - struct performance *perf; + struct stopwatch *perf; - perf = shash_find_data(&performances, name); + perf = shash_find_data(&stopwatches, name); if (!perf) { return false; } @@ -255,13 +257,13 @@ performance_get_stats_protected(const char *name, } bool -performance_get_stats(const char *name, struct performance_stats *stats) +stopwatch_get_stats(const char *name, struct stopwatch_stats *stats) { bool found = false; - ovs_mutex_lock(&performances_lock); - found = performance_get_stats_protected(name, stats); - ovs_mutex_unlock(&performances_lock); + ovs_mutex_lock(&stopwatches_lock); + found = stopwatch_get_stats_protected(name, stats); + ovs_mutex_unlock(&stopwatches_lock); return found; } @@ -427,6 +429,9 @@ stopwatch_thread(void *ign OVS_UNUSED) case OP_END_SAMPLE: stopwatch_end_sample_protected(&pkt); break; + case OP_SYNC: + xpthread_cond_signal(&stopwatches_sync); + break; case OP_RESET: stopwatch_reset_protected(&pkt); break; @@ -529,3 +534,16 @@ stopwatch_stop(const char *name, unsigned long long ts) ovs_strlcpy(pkt.name, name, sizeof(pkt.name)); write(stopwatch_pipe[1], &pkt, sizeof(pkt)); } + +void +stopwatch_sync(void) +{ + struct stopwatch_packet pkt = { + .op = OP_SYNC, + }; + + ovs_mutex_lock(&stopwatches_lock); + write(stopwatch_pipe[1], &pkt, sizeof(pkt)); + ovs_mutex_cond_wait(&stopwatches_sync, &stopwatches_lock); + ovs_mutex_unlock(&stopwatches_lock); +} diff --git a/lib/stopwatch.h b/lib/stopwatch.h index fac5de2c6..6ee7291d9 100644 --- a/lib/stopwatch.h +++ b/lib/stopwatch.h @@ -51,4 +51,7 @@ void stopwatch_stop(const char *name, unsigned long long ts); /* Retrieve statistics calculated from collected samples */ bool stopwatch_get_stats(const char *name, struct stopwatch_stats *stats); +/* Block until all enqueued samples have been processed. */ +void stopwatch_sync(void); + #endif /* stopwatch.h */ From patchwork Wed Mar 7 17:01:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 882695 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxKkq3L2Lz9sf4 for ; Thu, 8 Mar 2018 04:04:51 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8CC5B12B7; Wed, 7 Mar 2018 17:01:24 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 31FCB1293 for ; Wed, 7 Mar 2018 17:01:19 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id A52445EA for ; Wed, 7 Mar 2018 17:01:17 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E0D598424D for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) Received: from monae.redhat.com (ovpn-124-182.rdu2.redhat.com [10.10.124.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id BF026215CDA7 for ; Wed, 7 Mar 2018 17:01:16 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 7 Mar 2018 11:01:15 -0600 Message-Id: <20180307170115.29137-6-mmichels@redhat.com> In-Reply-To: <20180307170115.29137-1-mmichels@redhat.com> References: <20180307170115.29137-1-mmichels@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Wed, 07 Mar 2018 17:01:16 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'mmichels@redhat.com' RCPT:'' X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v6 5/5] tests: Add tests for stopwatch module X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Jakub Sitnicki Check if stopwatch module is calculating statistics as expected. Signed-off-by: Jakub Sitnicki Signed-off-by: Mark Michelson --- lib/stopwatch.c | 2 +- tests/automake.mk | 3 +- tests/library.at | 5 ++ tests/test-stopwatch.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 tests/test-stopwatch.c diff --git a/lib/stopwatch.c b/lib/stopwatch.c index 185f4a32f..ce48e3981 100644 --- a/lib/stopwatch.c +++ b/lib/stopwatch.c @@ -245,7 +245,7 @@ stopwatch_get_stats_protected(const char *name, return false; } - stats->count = perf->samples; + stats->count = perf->n_samples; stats->unit = perf->units; stats->max = perf->max; stats->min = perf->min; diff --git a/tests/automake.mk b/tests/automake.mk index 18698ebc3..4d43af0a5 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -364,7 +364,8 @@ tests_ovstest_SOURCES = \ tests/test-uuid.c \ tests/test-bitmap.c \ tests/test-vconn.c \ - tests/test-aa.c + tests/test-aa.c \ + tests/test-stopwatch.c if !WIN32 tests_ovstest_SOURCES += \ diff --git a/tests/library.at b/tests/library.at index 5efbfbb7c..1e81a80be 100644 --- a/tests/library.at +++ b/tests/library.at @@ -251,3 +251,8 @@ AT_CLEANUP AT_SETUP([rcu]) AT_CHECK([ovstest test-rcu-quiesce], [0], []) AT_CLEANUP + +AT_SETUP([stopwatch module]) +AT_CHECK([ovstest test-stopwatch], [0], [...... +], [ignore]) +AT_CLEANUP diff --git a/tests/test-stopwatch.c b/tests/test-stopwatch.c new file mode 100644 index 000000000..1270cd936 --- /dev/null +++ b/tests/test-stopwatch.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#undef NDEBUG +#include "stopwatch.h" +#include +#include +#include +#include "ovstest.h" +#include "util.h" + +#define MAX_SAMPLES 100 +#define UNIT SW_MS + +struct test_data { + const char *name; + unsigned long long samples[MAX_SAMPLES]; + size_t num_samples; + struct stopwatch_stats expected_stats; +}; + +static struct test_data data_sets[] = { + { + .name = "1-interval-zero-length", + .samples = { 0, 0 }, + .num_samples = 2, + .expected_stats = { + .count = 1, + .unit = UNIT, + .max = 0, + .min = 0, + .pctl_95 = 0, + .ewma_50 = 0, + .ewma_1 = 0, + }, + }, + { + .name = "1-interval-unit-length", + .samples = { 0, 1 }, + .num_samples = 2, + .expected_stats = { + .count = 1, + .unit = UNIT, + .max = 1, + .min = 1, + .pctl_95 = 0, + .ewma_50 = 1, + .ewma_1 = 1, + }, + }, + { + .name = "10-intervals-unit-length", + .samples = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + .num_samples = 11, + .expected_stats = { + .count = 10, + .unit = UNIT, + .max = 1, + .min = 1, + .pctl_95 = 1, + .ewma_50 = 1, + .ewma_1 = 1, + }, + }, + { + .name = "10-intervals-linear-growth", + .samples = { 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56 }, + .num_samples = 11, + .expected_stats = { + .count = 10, + .unit = UNIT, + .max = 10, + .min = 1, + .pctl_95 = 10.0, + .ewma_50 = 9.0, + .ewma_1 = 1.4, + }, + }, + { + .name = "60-intervals-unit-length", + .samples = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, }, + .num_samples = 61, + .expected_stats = { + .count = 60, + .unit = UNIT, + .max = 1, + .min = 1, + .pctl_95 = 1, + .ewma_50 = 1, + .ewma_1 = 1, + }, + }, + { + .name = "60-intervals-linear-growth", + .samples = { 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, + 56, 67, 79, 92, 106, 121, 137, 154, 172, 191, + 211, 232, 254, 277, 301, 326, 352, 379, 407, 436, + 466, 497, 529, 562, 596, 631, 667, 704, 742, 781, + 821, 862, 904, 947, 991, 1036, 1082, 1129, 1177, 1226, + 1276, 1327, 1379, 1432, 1486, 1541, 1597, 1654, 1712, 1771, + 1831, }, + .num_samples = 61, + .expected_stats = { + .count = 60, + .unit = UNIT, + .max = 60, + .min = 1, + /* 95th percentile is actually closer to 57, but the estimate is + * pretty dang close */ + .pctl_95 = 56, + .ewma_50 = 59, + .ewma_1 = 15.7, + }, + }, +}; + +#define ASSERT_MSG(COND, MSG, ...) \ + if (!(COND)) { \ + fprintf(stderr, MSG "\n", ##__VA_ARGS__); \ + assert(COND); \ + } + +#define ASSERT_ULL_EQ(a, b) \ + ASSERT_MSG(a == b, \ + "Assertion '%s == %s' failed: %llu == %llu", \ + #a, #b, a, b) + +#define ASSERT_DOUBLE_EQ(a, b, eps) \ + ASSERT_MSG(fabs(a - b) < eps, \ + "Assertion '|%s - %s| < %s' failed: |%g - %g| < %g", \ + #a, #b, #eps, a, b, eps) + +#define ASSERT_STATS_EQ(a, b) \ + do { \ + ASSERT_ULL_EQ((a)->count, (b)->count); \ + ASSERT_ULL_EQ((a)->max, (b)->max); \ + ASSERT_ULL_EQ((a)->min, (b)->min); \ + ASSERT_DOUBLE_EQ((a)->pctl_95, (b)->pctl_95, 1e-1); \ + ASSERT_DOUBLE_EQ((a)->ewma_50, (b)->ewma_50, 1e-1); \ + ASSERT_DOUBLE_EQ((a)->ewma_1, (b)->ewma_1, 1e-1); \ + } while (0) + +static void +test_stopwatch_calculate_stats(void) +{ + struct test_data *d; + + for (size_t i = 0; i < ARRAY_SIZE(data_sets); i++) { + d = &data_sets[i]; + + fprintf(stderr, "TEST '%s'\n", d->name); + + stopwatch_create(d->name, UNIT); + for (size_t j = 0; j < d->num_samples - 1; j ++) { + stopwatch_start(d->name, d->samples[j]); + stopwatch_stop(d->name, d->samples[j + 1]); + } + stopwatch_sync(); + + struct stopwatch_stats stats = { .unit = UNIT }; + stopwatch_get_stats(d->name, &stats); + ASSERT_STATS_EQ(&stats, &d->expected_stats); + + printf("."); + } +} + +static void +test_stopwatch_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + test_stopwatch_calculate_stats(); + printf("\n"); +} + +OVSTEST_REGISTER("test-stopwatch", test_stopwatch_main);