]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
global: Rename timing API to stats-dist API
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 18 Dec 2017 13:21:44 +0000 (15:21 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 29 Dec 2017 14:28:47 +0000 (16:28 +0200)
The API wasn't really about timings specifically, but about statistics in
general. The new stats service was already using it for tracking statistics
for non-timing related numbers.

16 files changed:
src/dict/dict-commands.c
src/dict/dict-commands.h
src/dict/main.c
src/lib-fs/fs-api.c
src/lib-fs/fs-api.h
src/lib/Makefile.am
src/lib/stats-dist.c [new file with mode: 0644]
src/lib/stats-dist.h [new file with mode: 0644]
src/lib/test-lib.inc
src/lib/test-stats-dist.c [new file with mode: 0644]
src/lib/test-timing.c [deleted file]
src/lib/timing.c [deleted file]
src/lib/timing.h [deleted file]
src/stats/client-reader.c
src/stats/stats-metrics.c
src/stats/stats-metrics.h

index 2044c34bca12cf97227e40b9c01370cf454ec9b1..e3e2fb6b6e64027b50dca73c98c9721f67f46ed2 100644 (file)
@@ -5,7 +5,7 @@
 #include "ostream.h"
 #include "str.h"
 #include "strescape.h"
-#include "timing.h"
+#include "stats-dist.h"
 #include "time-util.h"
 #include "dict-client.h"
 #include "dict-settings.h"
@@ -132,7 +132,7 @@ static void dict_connection_cmd_async(struct dict_connection_cmd *cmd)
 }
 
 static void
-cmd_stats_update(struct dict_connection_cmd *cmd, struct timing *timing)
+cmd_stats_update(struct dict_connection_cmd *cmd, struct stats_dist *stats)
 {
        long long diff;
 
@@ -142,16 +142,16 @@ cmd_stats_update(struct dict_connection_cmd *cmd, struct timing *timing)
        diff = timeval_diff_usecs(&ioloop_timeval, &cmd->start_timeval);
        if (diff < 0)
                diff = 0;
-       timing_add_usecs(timing, diff);
+       stats_dist_add(stats, diff);
        dict_proctitle_update_later();
 }
 
 static void
-dict_cmd_reply_handle_timings(struct dict_connection_cmd *cmd,
-                             string_t *str, struct timing *timing)
+dict_cmd_reply_handle_stats(struct dict_connection_cmd *cmd,
+                           string_t *str, struct stats_dist *stats)
 {
        io_loop_time_refresh();
-       cmd_stats_update(cmd, timing);
+       cmd_stats_update(cmd, stats);
 
        if (cmd->conn->minor_version < DICT_CLIENT_PROTOCOL_TIMINGS_MIN_VERSION)
                return;
@@ -202,7 +202,7 @@ cmd_lookup_callback(const struct dict_lookup_result *result, void *context)
                str_append_c(str, DICT_PROTOCOL_REPLY_FAIL);
                str_append_tabescaped(str, result->error);
        }
-       dict_cmd_reply_handle_timings(cmd, str, cmd_stats.lookups);
+       dict_cmd_reply_handle_stats(cmd, str, cmd_stats.lookups);
        str_append_c(str, '\n');
 
        cmd->reply = i_strdup(str_c(str));
@@ -268,7 +268,7 @@ static int cmd_iterate_flush(struct dict_connection_cmd *cmd)
                i_error("dict_iterate() failed: %s", error);
                str_printfa(str, "%c%s", DICT_PROTOCOL_REPLY_FAIL, error);
        }
-       dict_cmd_reply_handle_timings(cmd, str, cmd_stats.iterations);
+       dict_cmd_reply_handle_stats(cmd, str, cmd_stats.iterations);
        str_append_c(str, '\n');
 
        cmd->reply = i_strdup(str_c(str));
@@ -423,7 +423,7 @@ cmd_commit_finish(struct dict_connection_cmd *cmd,
                str_append_c(str, '\t');
                str_append_tabescaped(str, result->error);
        }
-       dict_cmd_reply_handle_timings(cmd, str, cmd_stats.commits);
+       dict_cmd_reply_handle_stats(cmd, str, cmd_stats.commits);
        str_append_c(str, '\n');
        cmd->reply = i_strdup(str_c(str));
 
@@ -668,14 +668,14 @@ static void dict_connection_cmd_output_more(struct dict_connection_cmd *cmd)
 
 void dict_commands_init(void)
 {
-       cmd_stats.lookups = timing_init();
-       cmd_stats.iterations = timing_init();
-       cmd_stats.commits = timing_init();
+       cmd_stats.lookups = stats_dist_init();
+       cmd_stats.iterations = stats_dist_init();
+       cmd_stats.commits = stats_dist_init();
 }
 
 void dict_commands_deinit(void)
 {
-       timing_deinit(&cmd_stats.lookups);
-       timing_deinit(&cmd_stats.iterations);
-       timing_deinit(&cmd_stats.commits);
+       stats_dist_deinit(&cmd_stats.lookups);
+       stats_dist_deinit(&cmd_stats.iterations);
+       stats_dist_deinit(&cmd_stats.commits);
 }
index 62200d0bbc456779bb0482756037a185d31f379f..ce4a04f189584614743c25b08d7754c21db75d0b 100644 (file)
@@ -4,9 +4,9 @@
 struct dict_connection;
 
 struct dict_command_stats {
-       struct timing *lookups;
-       struct timing *iterations;
-       struct timing *commits;
+       struct stats_dist *lookups;
+       struct stats_dist *iterations;
+       struct stats_dist *commits;
 };
 
 extern struct dict_command_stats cmd_stats;
index 997ea07b498927a2d63739ccf3e9ef8a031cdafd..068ec85f7eb38c6634134ca92c530e9976bfd456 100644 (file)
@@ -6,7 +6,7 @@
 #include "randgen.h"
 #include "str.h"
 #include "hostpid.h"
-#include "timing.h"
+#include "stats-dist.h"
 #include "process-title.h"
 #include "env-util.h"
 #include "module-dir.h"
@@ -25,13 +25,13 @@ static struct timeout *to_proctitle;
 static bool proctitle_updated;
 
 static void
-add_timing_string(string_t *str, struct timing *timing, const char *name)
+add_stats_string(string_t *str, struct stats_dist *stats, const char *name)
 {
        str_printfa(str, ", %u %s:%"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64,
-                   timing_get_count(timing), name,
-                   timing_get_min(timing)/1000, timing_get_avg(timing)/1000,
-                   timing_get_95th(timing)/1000, timing_get_max(timing)/1000);
-       timing_reset(timing);
+                   stats_dist_get_count(stats), name,
+                   stats_dist_get_min(stats)/1000, stats_dist_get_avg(stats)/1000,
+                   stats_dist_get_95th(stats)/1000, stats_dist_get_max(stats)/1000);
+       stats_dist_reset(stats);
 }
 
 static void dict_proctitle_update(void *context ATTR_UNUSED)
@@ -43,9 +43,9 @@ static void dict_proctitle_update(void *context ATTR_UNUSED)
 
        str_printfa(str, "[%u clients", dict_connections_current_count());
 
-       add_timing_string(str, cmd_stats.lookups, "lookups");
-       add_timing_string(str, cmd_stats.iterations, "iters");
-       add_timing_string(str, cmd_stats.commits, "commits");
+       add_stats_string(str, cmd_stats.lookups, "lookups");
+       add_stats_string(str, cmd_stats.iterations, "iters");
+       add_stats_string(str, cmd_stats.commits, "commits");
        str_append_c(str, ']');
 
        process_title_set(str_c(str));
index 38fd28374e311fbb2516e712194eea0d49a4993f..b9ae5dcb68c0a1807f271fe2a85535d20b2418e7 100644 (file)
@@ -9,7 +9,7 @@
 #include "istream.h"
 #include "istream-seekable.h"
 #include "ostream.h"
-#include "timing.h"
+#include "stats-dist.h"
 #include "time-util.h"
 #include "istream-fs-stats.h"
 #include "fs-api-private.h"
@@ -220,7 +220,7 @@ void fs_unref(struct fs **_fs)
        i_free(fs->temp_path_prefix);
        for (i = 0; i < FS_OP_COUNT; i++) {
                if (fs->stats.timings[i] != NULL)
-                       timing_deinit(&fs->stats.timings[i]);
+                       stats_dist_deinit(&fs->stats.timings[i]);
        }
        T_BEGIN {
                fs->v.deinit(fs);
@@ -424,7 +424,7 @@ static void fs_file_timing_start(struct fs_file *file, enum fs_op op)
 }
 
 static void
-fs_timing_end(struct timing **timing, const struct timeval *start_tv)
+fs_timing_end(struct stats_dist **timing, const struct timeval *start_tv)
 {
        struct timeval now;
        long long diff;
@@ -435,8 +435,8 @@ fs_timing_end(struct timing **timing, const struct timeval *start_tv)
        diff = timeval_diff_usecs(&now, start_tv);
        if (diff > 0) {
                if (*timing == NULL)
-                       *timing = timing_init();
-               timing_add_usecs(*timing, diff);
+                       *timing = stats_dist_init();
+               stats_dist_add(*timing, diff);
        }
 }
 
@@ -1216,7 +1216,7 @@ fs_stats_count_ops(const struct fs_stats *stats, const enum fs_op ops[],
 
        for (unsigned int i = 0; i < ops_count; i++) {
                if (stats->timings[ops[i]] != NULL)
-                       ret += timing_get_sum(stats->timings[ops[i]]);
+                       ret += stats_dist_get_sum(stats->timings[ops[i]]);
        }
        return ret;
 }
index 6cd84ca694d6d9323ecba6c104ed09913c54bc57..988d475f9a8e1d89c3e6e095d0cb48f82e7e33fd 100644 (file)
@@ -192,7 +192,7 @@ struct fs_stats {
 
        /* Cumulative sum of usecs spent on calls - set only if
           fs_settings.enable_timing=TRUE */
-       struct timing *timings[FS_OP_COUNT];
+       struct stats_dist *timings[FS_OP_COUNT];
 };
 
 struct fs_metadata {
index c5d013d75a03e0cba96660d4988678a4d5c36f82..d738b6fc81f252e1e7da27204ad91e62b3041d4d 100644 (file)
@@ -147,6 +147,7 @@ liblib_la_SOURCES = \
        sha2.c \
        sha3.c \
        sort.c \
+       stats-dist.c \
        str.c \
        str-find.c \
        str-sanitize.c \
@@ -155,7 +156,6 @@ liblib_la_SOURCES = \
        strfuncs.c \
        strnum.c \
        time-util.c \
-       timing.c \
        unix-socket-create.c \
        unlink-directory.c \
        unlink-old-files.c \
@@ -301,6 +301,7 @@ headers = \
        sha2.h \
        sha3.h \
        sort.h \
+       stats-dist.h \
        str.h \
        str-find.h \
        str-sanitize.h \
@@ -309,7 +310,6 @@ headers = \
        strfuncs.h \
        strnum.h \
        time-util.h \
-       timing.h \
        unix-socket-create.h \
        unlink-directory.h \
        unlink-old-files.h \
@@ -398,6 +398,7 @@ test_lib_SOURCES = \
        test-printf-format-fix.c \
        test-priorityq.c \
        test-seq-range-array.c \
+       test-stats-dist.c \
        test-str.c \
        test-strescape.c \
        test-strfuncs.c \
@@ -406,7 +407,6 @@ test_lib_SOURCES = \
        test-str-sanitize.c \
        test-str-table.c \
        test-time-util.c \
-       test-timing.c \
        test-unichar.c \
        test-utc-mktime.c \
        test-uri.c \
diff --git a/src/lib/stats-dist.c b/src/lib/stats-dist.c
new file mode 100644 (file)
index 0000000..6baad4a
--- /dev/null
@@ -0,0 +1,157 @@
+/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "stats-dist.h"
+#include "sort.h"
+
+/* In order to have a vaguely accurate 95th percentile, you need way
+   more than 20 in your subsample. */
+#define TIMING_DEFAULT_SUBSAMPLING_BUFFER (20*24) /* 20*24 fits in a page */
+
+struct stats_dist {
+       unsigned int sample_count;
+       unsigned int count;
+       bool     sorted;
+       uint64_t min;
+       uint64_t max;
+       uint64_t sum;
+       uint64_t samples[];
+};
+
+struct stats_dist *stats_dist_init(void)
+{
+       return stats_dist_init_with_size(TIMING_DEFAULT_SUBSAMPLING_BUFFER);
+}
+
+struct stats_dist *stats_dist_init_with_size(unsigned int sample_count)
+{
+       i_assert(sample_count > 0);
+
+       struct stats_dist *stats =
+               i_malloc(sizeof(struct stats_dist) +
+                        sizeof(uint64_t) * sample_count);
+       stats->sample_count = sample_count;
+       return stats;
+}
+
+void stats_dist_deinit(struct stats_dist **_stats)
+{
+       i_free_and_null(*_stats);
+}
+
+void stats_dist_reset(struct stats_dist *stats)
+{
+       unsigned int sample_count = stats->sample_count;
+       i_zero(stats);
+       stats->sample_count = sample_count;
+}
+
+void stats_dist_add(struct stats_dist *stats, uint64_t value)
+{
+       if (stats->count < stats->sample_count) {
+               stats->samples[stats->count] = value;
+               if (stats->count == 0)
+                       stats->min = stats->max = value;
+       } else {
+               unsigned int idx = i_rand_limit(stats->count);
+               if (idx < stats->sample_count)
+                       stats->samples[idx] = value;
+       }
+
+       stats->count++;
+       stats->sum += value;
+       if (stats->max < value)
+               stats->max = value;
+       if (stats->min > value)
+               stats->min = value;
+       stats->sorted = FALSE;
+}
+
+unsigned int stats_dist_get_count(const struct stats_dist *stats)
+{
+       return stats->count;
+}
+
+uint64_t stats_dist_get_sum(const struct stats_dist *stats)
+{
+       return stats->sum;
+}
+
+uint64_t stats_dist_get_min(const struct stats_dist *stats)
+{
+       return stats->min;
+}
+
+uint64_t stats_dist_get_max(const struct stats_dist *stats)
+{
+       return stats->max;
+}
+
+uint64_t stats_dist_get_avg(const struct stats_dist *stats)
+{
+       if (stats->count == 0)
+               return 0;
+
+       return (stats->sum + stats->count/2) / stats->count;
+}
+
+static void stats_dist_ensure_sorted(struct stats_dist *stats)
+{
+       if (stats->sorted)
+               return;
+
+       unsigned int count = (stats->count < stats->sample_count)
+               ? stats->count
+               : stats->sample_count;
+       i_qsort(stats->samples, count, sizeof(*stats->samples),
+               uint64_cmp);
+       stats->sorted = TRUE;
+}
+
+uint64_t stats_dist_get_median(const struct stats_dist *stats)
+{
+       if (stats->count == 0)
+               return 0;
+       /* cast-away const - reading requires sorting */
+       stats_dist_ensure_sorted((struct stats_dist *)stats);
+       unsigned int count = (stats->count < stats->sample_count)
+               ? stats->count
+               : stats->sample_count;
+       unsigned int idx1 = (count-1)/2, idx2 = count/2;
+       return (stats->samples[idx1] + stats->samples[idx2]) / 2;
+}
+
+/* This is independent of the stats framework, useful for any selection task */
+static unsigned int stats_dist_get_index(unsigned int range, double fraction)
+{
+       /* With out of range fractions, we can give the caller what
+          they probably want rather than just crashing. */
+       if (fraction >= 1.)
+               return range - 1;
+       if (fraction <= 0.)
+               return 0;
+
+       double idx_float = range * fraction;
+       unsigned int idx = idx_float; /* C defaults to rounding down */
+       idx_float -= idx;
+       /* Exact boundaries belong to the open range below them.
+          As FP isn't exact, and ratios may be specified inexactly,
+          include a small amount of fuzz around the exact boundary. */
+       if (idx_float < 1e-8*range)
+               idx--;
+
+       return idx;
+}
+
+uint64_t stats_dist_get_percentile(const struct stats_dist *stats, double fraction)
+{
+       if (stats->count == 0)
+               return 0;
+       /* cast-away const - reading requires sorting */
+       stats_dist_ensure_sorted((struct stats_dist *)stats);
+       unsigned int count = (stats->count < stats->sample_count)
+               ? stats->count
+               : stats->sample_count;
+       unsigned int idx = stats_dist_get_index(count, fraction);
+       return stats->samples[idx];
+}
diff --git a/src/lib/stats-dist.h b/src/lib/stats-dist.h
new file mode 100644 (file)
index 0000000..0a2f16a
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef STATS_DIST_H
+#define STATS_DIST_H
+
+struct stats_dist *stats_dist_init(void);
+struct stats_dist *stats_dist_init_with_size(unsigned int sample_count);
+void stats_dist_deinit(struct stats_dist **stats);
+
+/* Reset all events. */
+void stats_dist_reset(struct stats_dist *stats);
+
+/* Add a new event. */
+void stats_dist_add(struct stats_dist *stats, uint64_t value);
+
+/* Returns number of events added. */
+unsigned int stats_dist_get_count(const struct stats_dist *stats);
+/* Returns the sum of all events. */
+uint64_t stats_dist_get_sum(const struct stats_dist *stats);
+
+/* Returns events' minimum. */
+uint64_t stats_dist_get_min(const struct stats_dist *stats);
+/* Returns events' maximum. */
+uint64_t stats_dist_get_max(const struct stats_dist *stats);
+/* Returns events' average. */
+uint64_t stats_dist_get_avg(const struct stats_dist *stats);
+/* Returns events' approximate (through random subsampling) median. */
+uint64_t stats_dist_get_median(const struct stats_dist *stats);
+/* Returns events' approximate (through random subsampling) percentile.
+   fraction parameter is in the range (0., 1.], so 95th %-ile is 0.95. */
+uint64_t stats_dist_get_percentile(const struct stats_dist *stats, double fraction);
+/* Returns events' approximate (through random subsampling) 95th percentile. */
+static inline uint64_t stats_dist_get_95th(const struct stats_dist *stats)
+{
+       return stats_dist_get_percentile(stats, 0.95);
+}
+
+#endif
index ca89c5133827d4b1e28bea40b5f2d0c85a820efb..2bdbe4c99bb99c3a4f667b5c766c22c5a9c598b7 100644 (file)
@@ -73,6 +73,7 @@ TEST(test_printf_format_fix)
 FATAL(fatal_printf_format_fix)
 TEST(test_priorityq)
 TEST(test_seq_range_array)
+TEST(test_stats_dist)
 TEST(test_str)
 TEST(test_strescape)
 TEST(test_strfuncs)
@@ -81,7 +82,6 @@ TEST(test_str_find)
 TEST(test_str_sanitize)
 TEST(test_str_table)
 TEST(test_time_util)
-TEST(test_timing)
 TEST(test_unichar)
 TEST(test_uri)
 TEST(test_utc_mktime)
diff --git a/src/lib/test-stats-dist.c b/src/lib/test-stats-dist.c
new file mode 100644 (file)
index 0000000..cd2fa4f
--- /dev/null
@@ -0,0 +1,95 @@
+/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "stats-dist.h"
+#include "sort.h"
+
+static void
+test_stats_dist_verify(const struct stats_dist *t, const int64_t *input,
+                      unsigned int input_size)
+{
+       uint64_t min = INT_MAX, max = 0, sum = 0;
+       uint64_t *copy;
+       unsigned int i;
+
+       i_assert(input_size > 0);
+
+       copy = i_new(uint64_t, input_size);
+       for (i = 0; i < input_size; i++) {
+               uint64_t value = input[i];
+
+               if (min > value)
+                       min = value;
+               if (max < value)
+                       max = value;
+               sum += value;
+               copy[i] = value;
+       }
+       i_qsort(copy, input_size, sizeof(*copy), uint64_cmp);
+
+       test_assert_idx(stats_dist_get_count(t) == input_size, input_size);
+       test_assert_idx(stats_dist_get_sum(t) == sum, input_size);
+       test_assert_idx(stats_dist_get_min(t)  == min, input_size);
+       test_assert_idx(stats_dist_get_max(t) == max, input_size);
+       test_assert_idx(stats_dist_get_avg(t) == (sum + input_size/2)/input_size, input_size);
+
+       /* these aren't always fully accurate: */
+       test_assert_idx(stats_dist_get_median(t) >= copy[(input_size-1)/2] &&
+                       stats_dist_get_median(t) <= copy[input_size/2],
+                       input_size);
+       /* when we have 20 elements, [19] is the max, not the 95th %ile, so subtract 1 */
+       test_assert_idx(stats_dist_get_95th(t) == copy[input_size*95/100 - ((input_size%20) == 0 ? 1 : 0)],
+                       input_size);
+
+       i_free(copy);
+}
+
+void test_stats_dist(void)
+{
+       static int64_t test_input1[] = {
+               20, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+               9, 10, 11, 12, 13, 14, 15, 16, 17, -1
+       };
+       static int64_t test_input2[] = {
+               20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+               9, 10, 11, 12, 13, 14, 15, 16, 17, -1
+       };
+       static int64_t test_input3[] = {
+               20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
+               9, 10, 11, 12, 13, 14, 15, 16, 17, 22, -1
+       };
+       static int64_t *test_inputs[] = {
+               test_input1, test_input2, test_input3
+       };
+       struct stats_dist *t;
+       unsigned int i, j;
+
+       for (i = 0; i < N_ELEMENTS(test_inputs); i++) {
+               test_begin(t_strdup_printf("stats_dists %u", i));
+               t = stats_dist_init();
+               for (j = 0; test_inputs[i][j] >= 0; j++) {
+                       stats_dist_add(t, test_inputs[i][j]);
+                       test_stats_dist_verify(t, test_inputs[i], j+1);
+               }
+               stats_dist_reset(t);
+               test_assert(stats_dist_get_count(t) == 0);
+               test_assert(stats_dist_get_max(t) == 0);
+               stats_dist_deinit(&t);
+               test_end();
+       }
+
+       test_begin("stats_dists large");
+       t = stats_dist_init();
+       for (i = 0; i < 10000; i++)
+               stats_dist_add(t, i);
+       test_assert(stats_dist_get_count(t) == i);
+       test_assert(stats_dist_get_sum(t) == (i-1)*i/2);
+       test_assert(stats_dist_get_min(t) == 0);
+       test_assert(stats_dist_get_max(t) == i-1);
+       test_assert(stats_dist_get_avg(t) == i/2);
+       /* just test that these work: */
+       test_assert(stats_dist_get_median(t) > 0 && stats_dist_get_median(t) < i-1);
+       test_assert(stats_dist_get_95th(t) > 0 && stats_dist_get_95th(t) < i-1);
+       stats_dist_deinit(&t);
+       test_end();
+}
diff --git a/src/lib/test-timing.c b/src/lib/test-timing.c
deleted file mode 100644 (file)
index 5dbb695..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
-
-#include "test-lib.h"
-#include "timing.h"
-#include "sort.h"
-
-static void
-test_timing_verify(const struct timing *t, const int64_t *input,
-                  unsigned int input_size)
-{
-       uint64_t min = INT_MAX, max = 0, sum = 0;
-       uint64_t *copy;
-       unsigned int i;
-
-       i_assert(input_size > 0);
-
-       copy = i_new(uint64_t, input_size);
-       for (i = 0; i < input_size; i++) {
-               uint64_t value = input[i];
-
-               if (min > value)
-                       min = value;
-               if (max < value)
-                       max = value;
-               sum += value;
-               copy[i] = value;
-       }
-       i_qsort(copy, input_size, sizeof(*copy), uint64_cmp);
-
-       test_assert_idx(timing_get_count(t) == input_size, input_size);
-       test_assert_idx(timing_get_sum(t) == sum, input_size);
-       test_assert_idx(timing_get_min(t)  == min, input_size);
-       test_assert_idx(timing_get_max(t) == max, input_size);
-       test_assert_idx(timing_get_avg(t) == (sum + input_size/2)/input_size, input_size);
-
-       /* these aren't always fully accurate: */
-       test_assert_idx(timing_get_median(t) >= copy[(input_size-1)/2] &&
-                       timing_get_median(t) <= copy[input_size/2],
-                       input_size);
-       /* when we have 20 elements, [19] is the max, not the 95th %ile, so subtract 1 */
-       test_assert_idx(timing_get_95th(t) == copy[input_size*95/100 - ((input_size%20) == 0 ? 1 : 0)],
-                       input_size);
-
-       i_free(copy);
-}
-
-void test_timing(void)
-{
-       static int64_t test_input1[] = {
-               20, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
-               9, 10, 11, 12, 13, 14, 15, 16, 17, -1
-       };
-       static int64_t test_input2[] = {
-               20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
-               9, 10, 11, 12, 13, 14, 15, 16, 17, -1
-       };
-       static int64_t test_input3[] = {
-               20, 21, 19, 18, 1, 2, 3, 4, 5, 6, 7, 8,
-               9, 10, 11, 12, 13, 14, 15, 16, 17, 22, -1
-       };
-       static int64_t *test_inputs[] = {
-               test_input1, test_input2, test_input3
-       };
-       struct timing *t;
-       unsigned int i, j;
-
-       for (i = 0; i < N_ELEMENTS(test_inputs); i++) {
-               test_begin(t_strdup_printf("timings %u", i));
-               t = timing_init();
-               for (j = 0; test_inputs[i][j] >= 0; j++) {
-                       timing_add_usecs(t, test_inputs[i][j]);
-                       test_timing_verify(t, test_inputs[i], j+1);
-               }
-               timing_reset(t);
-               test_assert(timing_get_count(t) == 0);
-               test_assert(timing_get_max(t) == 0);
-               timing_deinit(&t);
-               test_end();
-       }
-
-       test_begin("timings large");
-       t = timing_init();
-       for (i = 0; i < 10000; i++)
-               timing_add_usecs(t, i);
-       test_assert(timing_get_count(t) == i);
-       test_assert(timing_get_sum(t) == (i-1)*i/2);
-       test_assert(timing_get_min(t) == 0);
-       test_assert(timing_get_max(t) == i-1);
-       test_assert(timing_get_avg(t) == i/2);
-       /* just test that these work: */
-       test_assert(timing_get_median(t) > 0 && timing_get_median(t) < i-1);
-       test_assert(timing_get_95th(t) > 0 && timing_get_95th(t) < i-1);
-       timing_deinit(&t);
-       test_end();
-}
diff --git a/src/lib/timing.c b/src/lib/timing.c
deleted file mode 100644 (file)
index 59b4fb0..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "timing.h"
-#include "sort.h"
-
-/* In order to have a vaguely accurate 95th percentile, you need way
-   more than 20 in your subsample. */
-#define TIMING_DEFAULT_SUBSAMPLING_BUFFER (20*24) /* 20*24 fits in a page */
-
-struct timing {
-       unsigned int sample_count;
-       unsigned int count;
-       bool     sorted;
-       uint64_t min;
-       uint64_t max;
-       uint64_t sum;
-       uint64_t samples[];
-};
-
-struct timing *timing_init(void)
-{
-       return timing_init_with_size(TIMING_DEFAULT_SUBSAMPLING_BUFFER);
-}
-
-struct timing *timing_init_with_size(unsigned int sample_count)
-{
-       i_assert(sample_count > 0);
-
-       struct timing *timing =
-               i_malloc(sizeof(struct timing) +
-                        sizeof(uint64_t) * sample_count);
-       timing->sample_count = sample_count;
-       return timing;
-}
-
-void timing_deinit(struct timing **_timing)
-{
-       i_free_and_null(*_timing);
-}
-
-void timing_reset(struct timing *timing)
-{
-       unsigned int sample_count = timing->sample_count;
-       i_zero(timing);
-       timing->sample_count = sample_count;
-}
-
-void timing_add_usecs(struct timing *timing, uint64_t usecs)
-{
-       if (timing->count < timing->sample_count) {
-               timing->samples[timing->count] = usecs;
-               if (timing->count == 0)
-                       timing->min = timing->max = usecs;
-       } else {
-               unsigned int idx = i_rand_limit(timing->count);
-               if (idx < timing->sample_count)
-                       timing->samples[idx] = usecs;
-       }
-
-       timing->count++;
-       timing->sum += usecs;
-       if (timing->max < usecs)
-               timing->max = usecs;
-       if (timing->min > usecs)
-               timing->min = usecs;
-       timing->sorted = FALSE;
-}
-
-unsigned int timing_get_count(const struct timing *timing)
-{
-       return timing->count;
-}
-
-uint64_t timing_get_sum(const struct timing *timing)
-{
-       return timing->sum;
-}
-
-uint64_t timing_get_min(const struct timing *timing)
-{
-       return timing->min;
-}
-
-uint64_t timing_get_max(const struct timing *timing)
-{
-       return timing->max;
-}
-
-uint64_t timing_get_avg(const struct timing *timing)
-{
-       if (timing->count == 0)
-               return 0;
-
-       return (timing->sum + timing->count/2) / timing->count;
-}
-
-static void timing_ensure_sorted(struct timing *timing)
-{
-       if (timing->sorted)
-               return;
-
-       unsigned int count = (timing->count < timing->sample_count)
-               ? timing->count
-               : timing->sample_count;
-       i_qsort(timing->samples, count, sizeof(*timing->samples),
-               uint64_cmp);
-       timing->sorted = TRUE;
-}
-
-uint64_t timing_get_median(const struct timing *timing)
-{
-       if (timing->count == 0)
-               return 0;
-       /* cast-away const - reading requires sorting */
-       timing_ensure_sorted((struct timing *)timing);
-       unsigned int count = (timing->count < timing->sample_count)
-               ? timing->count
-               : timing->sample_count;
-       unsigned int idx1 = (count-1)/2, idx2 = count/2;
-       return (timing->samples[idx1] + timing->samples[idx2]) / 2;
-}
-
-/* This is independent of the timing framework, useful for any selection task */
-static unsigned int timing_get_index(unsigned int range, double fraction)
-{
-       /* With out of range fractions, we can give the caller what
-          they probably want rather than just crashing. */
-       if (fraction >= 1.)
-               return range - 1;
-       if (fraction <= 0.)
-               return 0;
-
-       double idx_float = range * fraction;
-       unsigned int idx = idx_float; /* C defaults to rounding down */
-       idx_float -= idx;
-       /* Exact boundaries belong to the open range below them.
-          As FP isn't exact, and ratios may be specified inexactly,
-          include a small amount of fuzz around the exact boundary. */
-       if (idx_float < 1e-8*range)
-               idx--;
-
-       return idx;
-}
-
-uint64_t timing_get_percentile(const struct timing *timing, double fraction)
-{
-       if (timing->count == 0)
-               return 0;
-       /* cast-away const - reading requires sorting */
-       timing_ensure_sorted((struct timing *)timing);
-       unsigned int count = (timing->count < timing->sample_count)
-               ? timing->count
-               : timing->sample_count;
-       unsigned int idx = timing_get_index(count, fraction);
-       return timing->samples[idx];
-}
diff --git a/src/lib/timing.h b/src/lib/timing.h
deleted file mode 100644 (file)
index 065c9b4..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef TIMING_H
-#define TIMING_H
-
-struct timing *timing_init(void);
-struct timing *timing_init_with_size(unsigned int sample_count);
-void timing_deinit(struct timing **timing);
-
-/* Reset all events. */
-void timing_reset(struct timing *timing);
-
-/* Add a new event that took the specified number of usecs. */
-void timing_add_usecs(struct timing *timing, uint64_t usecs);
-
-/* Returns number of events added. */
-unsigned int timing_get_count(const struct timing *timing);
-/* Returns the sum of all usecs added. */
-uint64_t timing_get_sum(const struct timing *timing);
-
-/* Returns events' minimum. */
-uint64_t timing_get_min(const struct timing *timing);
-/* Returns events' maximum. */
-uint64_t timing_get_max(const struct timing *timing);
-/* Returns events' average. */
-uint64_t timing_get_avg(const struct timing *timing);
-/* Returns events' approximate (through random subsampling) median. */
-uint64_t timing_get_median(const struct timing *timing);
-/* Returns events' approximate (through random subsampling) percentile.
-   fraction parameter is in the range (0., 1.], so 95th %-ile is 0.95. */
-uint64_t timing_get_percentile(const struct timing *timing, double fraction);
-/* Returns events' approximate (through random subsampling) 95th percentile. */
-static inline uint64_t timing_get_95th(const struct timing *timing)
-{
-       return timing_get_percentile(timing, 0.95);
-}
-
-#endif
index afd85057429a030870e902979b910f8ed97e037b..d900b12e4cfa1982e8934d10bcf8fda7e55492bf 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "lib.h"
 #include "str.h"
-#include "timing.h"
+#include "stats-dist.h"
 #include "strescape.h"
 #include "connection.h"
 #include "ostream.h"
@@ -35,27 +35,27 @@ static void reader_client_destroy(struct connection *conn)
        master_service_client_connection_destroyed(master_service);
 }
 
-static void reader_client_dump_timing(string_t *str, struct timing *timing,
-                                     const char *const *fields)
+static void reader_client_dump_stats(string_t *str, struct stats_dist *stats,
+                                    const char *const *fields)
 {
        for (unsigned int i = 0; fields[i] != NULL; i++) {
                const char *field = fields[i];
 
                str_append_c(str, '\t');
                if (strcmp(field, "count") == 0)
-                       str_printfa(str, "%u", timing_get_count(timing));
+                       str_printfa(str, "%u", stats_dist_get_count(stats));
                else if (strcmp(field, "sum") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_sum(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_sum(stats));
                else if (strcmp(field, "min") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_min(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_min(stats));
                else if (strcmp(field, "max") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_max(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_max(stats));
                else if (strcmp(field, "avg") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_avg(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_avg(stats));
                else if (strcmp(field, "median") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_median(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_median(stats));
                else if (strcmp(field, "%95") == 0)
-                       str_printfa(str, "%"PRIu64, timing_get_95th(timing));
+                       str_printfa(str, "%"PRIu64, stats_dist_get_95th(stats));
                else {
                        /* return unknown fields as empty */
                }
@@ -74,11 +74,11 @@ reader_client_input_dump(struct reader_client *client, const char *const *args)
        while ((metric = stats_metrics_iterate(iter)) != NULL) {
                str_truncate(str, 0);
                str_append_tabescaped(str, metric->name);
-               reader_client_dump_timing(str, metric->duration_timing, args);
+               reader_client_dump_stats(str, metric->duration_stats, args);
                for (unsigned int i = 0; i < metric->fields_count; i++) {
                        str_append_c(str, '\t');
                        str_append_tabescaped(str, metric->fields[i].field_key);
-                       reader_client_dump_timing(str, metric->fields[i].timing, args);
+                       reader_client_dump_stats(str, metric->fields[i].stats, args);
                }
                str_append_c(str, '\n');
                o_stream_nsend(client->conn.output, str_data(str), str_len(str));
index 6f96073ab897a25473f97c65b21829bad0dffcff..c572ec7cd849e7e44af6387bef8693171743a521 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "lib.h"
 #include "array.h"
-#include "timing.h"
+#include "stats-dist.h"
 #include "time-util.h"
 #include "event-filter.h"
 #include "stats-settings.h"
@@ -54,7 +54,7 @@ static void stats_metrics_add_set(struct stats_metrics *metrics,
 
        metric = p_new(metrics->pool, struct metric, 1);
        metric->name = p_strdup(metrics->pool, set->name);
-       metric->duration_timing = timing_init();
+       metric->duration_stats = stats_dist_init();
 
        fields = t_strsplit_spaces(set->fields, " ");
        metric->fields_count = str_array_length(fields);
@@ -64,7 +64,7 @@ static void stats_metrics_add_set(struct stats_metrics *metrics,
                for (unsigned int i = 0; i < metric->fields_count; i++) {
                        metric->fields[i].field_key =
                                p_strdup(metrics->pool, fields[i]);
-                       metric->fields[i].timing = timing_init();
+                       metric->fields[i].stats = stats_dist_init();
                }
        }
        array_append(&metrics->metrics, &metric, 1);
@@ -106,9 +106,9 @@ struct stats_metrics *stats_metrics_init(const struct stats_settings *set)
 
 static void stats_metric_free(struct metric *metric)
 {
-       timing_deinit(&metric->duration_timing);
+       stats_dist_deinit(&metric->duration_stats);
        for (unsigned int i = 0; i < metric->fields_count; i++)
-               timing_deinit(&metric->fields[i].timing);
+               stats_dist_deinit(&metric->fields[i].stats);
 }
 
 void stats_metrics_deinit(struct stats_metrics **_metrics)
@@ -129,9 +129,9 @@ void stats_metrics_reset(struct stats_metrics *metrics)
        struct metric *const *metricp;
 
        array_foreach(&metrics->metrics, metricp) {
-               timing_reset((*metricp)->duration_timing);
+               stats_dist_reset((*metricp)->duration_stats);
                for (unsigned int i = 0; i < (*metricp)->fields_count; i++)
-                       timing_reset((*metricp)->fields[i].timing);
+                       stats_dist_reset((*metricp)->fields[i].stats);
        }
 }
 
@@ -151,7 +151,7 @@ stats_metric_event(struct metric *metric, struct event *event)
                event_get_create_time(event, &tv_start);
                duration = timeval_diff_usecs(&tv_end, &tv_start);
        }
-       timing_add_usecs(metric->duration_timing, duration);
+       stats_dist_add(metric->duration_stats, duration);
 
        for (unsigned int i = 0; i < metric->fields_count; i++) {
                const struct event_field *field =
@@ -171,7 +171,7 @@ stats_metric_event(struct metric *metric, struct event *event)
                                field->value.timeval.tv_usec;
                        break;
                }
-               timing_add_usecs(metric->fields[i].timing, num);
+               stats_dist_add(metric->fields[i].stats, num);
        }
 }
 
index 33cbc77b70732c3b3ad56e2c2a6ff4f7bd877c1e..2ac23053dc94ebc31acf59a63030a1b1cbac7a5d 100644 (file)
@@ -5,14 +5,14 @@ struct stats_settings;
 
 struct metric_field {
        const char *field_key;
-       struct timing *timing;
+       struct stats_dist *stats;
 };
 
 struct metric {
        const char *name;
 
        /* Timing for how long the event existed */
-       struct timing *duration_timing;
+       struct stats_dist *duration_stats;
 
        unsigned int fields_count;
        struct metric_field *fields;