* [PATCH] libtraceeval: Add average and standard deviation to stat
@ 2023-10-11 17:38 Steven Rostedt
0 siblings, 0 replies; only message in thread
From: Steven Rostedt @ 2023-10-11 17:38 UTC (permalink / raw)
To: Linux Trace Devel; +Cc: Ross Zwisler
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
Add the APIs:
traceeval_stat_average()
traceeval_stat_stddev()
That return the average mean and standard deviation respectively of all
the values in the stat.
It uses the Welford method of computing the running standard deviation,
which is:
count = 0;
calculate(x) {
if (!count) {
count = 1;
M = x;
M2 = 0.0;
} else {
double D;
count++;
D = x - M;
M += D / count;
M2 = D * (x - M);
}
}
stddev() {
return sqrt(M2 / (count - 1))
}
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
include/traceeval.h | 2 ++
samples/task-eval.c | 22 ++++++++++++++++++++-
scripts/utils.mk | 2 +-
src/Makefile | 2 ++
src/eval-local.h | 3 ++-
src/histograms.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/include/traceeval.h b/include/traceeval.h
index 55d6df67375a..69c7c45a7bea 100644
--- a/include/traceeval.h
+++ b/include/traceeval.h
@@ -345,6 +345,8 @@ unsigned long long traceeval_stat_max_timestamp(struct traceeval_stat *stat,
unsigned long long *ts);
unsigned long long traceeval_stat_min_timestamp(struct traceeval_stat *stat,
unsigned long long *ts);
+unsigned long long traceeval_stat_average(struct traceeval_stat *stat);
+double traceeval_stat_stddev(struct traceeval_stat *stat);
struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval);
struct traceeval_iterator *traceeval_iterator_delta_start_get(struct traceeval *teval);
diff --git a/samples/task-eval.c b/samples/task-eval.c
index 382e30514ec3..199c3b5dd926 100644
--- a/samples/task-eval.c
+++ b/samples/task-eval.c
@@ -814,6 +814,7 @@ static int compare_pdata(struct traceeval *teval,
static void display_cpus(struct traceeval *teval)
{
struct traceeval_iterator *iter = traceeval_iterator_get(teval);
+ unsigned long long max, max_ts, min, min_ts;
const struct traceeval_data *keys;
struct traceeval_stat *stat;
int last_cpu = -1;
@@ -834,6 +835,9 @@ static void display_cpus(struct traceeval *teval)
if (!stat)
continue; // die?
+ max = traceeval_stat_max_timestamp(stat, &max_ts);
+ min = traceeval_stat_min_timestamp(stat, &min_ts);
+
if (last_cpu != cpu)
printf(" CPU [%d]:\n", cpu);
@@ -853,6 +857,18 @@ static void display_cpus(struct traceeval *teval)
}
printf(" time (us):");
print_microseconds_nl(12, traceeval_stat_total(stat));
+ printf(" average (us):");
+ print_microseconds_nl(12, traceeval_stat_average(stat));
+ printf(" max:");
+ print_microseconds(12, max);
+ printf("\tat: %lld\n", max_ts);
+ printf(" min:");
+ print_microseconds(12, min);
+ printf("\tat: %lld\n", min_ts);
+ printf(" count: %*lld\n", 11,
+ traceeval_stat_count(stat));
+ printf(" stddev: %*.3f\n", 16,
+ traceeval_stat_stddev(stat) / 1000);
last_cpu = cpu;
}
@@ -883,13 +899,17 @@ static void print_stats(int idx, struct traceeval_stat *stat)
printf("\tat: %lld\n", max_ts);
} else {
print_microseconds_nl(idx, total);
- printf("%*s%*lld\n", 40 - idx, "count:", idx, cnt);
+ printf("%*s", 40 - idx, "average:");
+ print_microseconds_nl(idx, traceeval_stat_average(stat));
printf("%*s", 40 - idx, "max:");
print_microseconds(idx, max);
printf("\tat: %lld\n", max_ts);
printf("%*s", 40 - idx, "min:");
print_microseconds(idx, min);
printf("\tat: %lld\n", min_ts);
+ printf("%*s%*lld\n", 40 - idx, "count:", idx, cnt);
+ printf("%*s%*.3f\n", 40 - idx, "stddev:", idx + 4,
+ traceeval_stat_stddev(stat) / 1000);
}
}
diff --git a/scripts/utils.mk b/scripts/utils.mk
index 2ffc349ab412..2a9ef3450772 100644
--- a/scripts/utils.mk
+++ b/scripts/utils.mk
@@ -101,7 +101,7 @@ extract_example = \
do_sample_build = \
$(Q)($(print_sample_build) \
- $(CC) -o $1 $2 $(CFLAGS) $(LIBRARY_STATIC) $(LIBRARY_LIBS))
+ $(CC) -o $1 $2 $(CFLAGS) $(LIBRARY_STATIC) $(LIBRARY_LIBS) -lm)
do_sample_obj = \
$(Q)($(print_sample_obj) \
diff --git a/src/Makefile b/src/Makefile
index 6f790b21bae4..a5a36261d662 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,6 +9,8 @@ OBJS += hash.o
OBJS := $(OBJS:%.o=$(bdir)/%.o)
+LIBS = -lm
+
$(LIBRARY_STATIC): $(OBJS)
$(Q)$(call do_build_static_lib)
diff --git a/src/eval-local.h b/src/eval-local.h
index ca8195a2f4cb..5ae63841f39f 100644
--- a/src/eval-local.h
+++ b/src/eval-local.h
@@ -56,7 +56,8 @@ struct traceeval_stat {
unsigned long long min;
unsigned long long min_ts;
unsigned long long total;
- unsigned long long std;
+ double M;
+ double M2;
size_t count;
};
diff --git a/src/histograms.c b/src/histograms.c
index e16f0e9e903e..3323fcacae4a 100644
--- a/src/histograms.c
+++ b/src/histograms.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
+#include <math.h>
#include <traceeval.h>
#include "eval-local.h"
@@ -623,6 +624,8 @@ __hidden void _teval_update_stat(struct traceeval_type *type,
unsigned long long val,
unsigned long long ts)
{
+ double D;
+
/* If both the delta and the timestamp are zero, ignore this */
if (!val && !ts)
return;
@@ -633,6 +636,8 @@ __hidden void _teval_update_stat(struct traceeval_type *type,
stat->max_ts = ts;
stat->min_ts = ts;
stat->total = val;
+ stat->M = (double)val;
+ stat->M2 = 0.0;
return;
}
@@ -657,6 +662,14 @@ __hidden void _teval_update_stat(struct traceeval_type *type,
}
stat->total += val;
}
+ /*
+ * Welford's method for standard deviation:
+ * s^2 = 1 / (n - 1) * \Sum ((x - M_k-1) * (x - M_k))
+ * Where M_k is the mean of the current samples of k.
+ */
+ D = val - stat->M;
+ stat->M += D / stat->count;
+ stat->M2 += D * (val - stat->M);
}
static bool is_stat_type(struct traceeval_type *type)
@@ -1126,6 +1139,40 @@ unsigned long long traceeval_stat_total(struct traceeval_stat *stat)
return stat->total;
}
+/**
+ * traceeval_stat_average - return the average value of stat
+ * @stat: The stat structure that holds the stats
+ *
+ * Returns the calculated average within @stat.
+ */
+unsigned long long traceeval_stat_average(struct traceeval_stat *stat)
+{
+ return stat->total / stat->count;
+}
+
+/**
+ * traceeval_stat_stddev - return the standard deviation of stat
+ * @stat: The stat structure that holds the stats
+ *
+ * Returns the calculated standard deviation within @stat.
+ */
+double traceeval_stat_stddev(struct traceeval_stat *stat)
+{
+ double stddev;
+
+ if (stat->count < 2)
+ return 0.0;
+ /*
+ * Welford's method for standard deviation:
+ * s^2 = 1 / (n - 1) * \Sum ((x - M_k-1) * (x - M_k))
+ * Where M_k is the mean of the current samples of k.
+ */
+
+ stddev = stat->M2 / (stat->count - 1);
+
+ return sqrt(stddev);
+}
+
/**
* traceeval_stat_count - return count value of stat
* @stat: The stat structure that holds the stats
--
2.42.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2023-10-11 17:37 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-11 17:38 [PATCH] libtraceeval: Add average and standard deviation to stat Steven Rostedt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).