linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] perf, tools: Support spark lines in perf stat v2
@ 2014-04-14 16:09 Andi Kleen
  2014-04-15  6:35 ` Namhyung Kim
  0 siblings, 1 reply; 8+ messages in thread
From: Andi Kleen @ 2014-04-14 16:09 UTC (permalink / raw)
  To: jolsa; +Cc: acme, linux-kernel, Andi Kleen

From: Andi Kleen <ak@linux.intel.com>

perf stat -rX prints the stddev for multiple measurements.
Just looking at the stddev for judging the quality of the data
is a bit dangerous The simplest sanity check is to just look
at a simple plot. This patchs add a sparkline to the end
of the measurements to make it simple to judge the data.

The sparkline only uses UTF-8, so should be readable
in all modern tools and terminals.

The sparkline is between the minimum and maximum of the data,
so it's mainly a indicator of variance. To keep the code
simple and make the output not too wide only the first
8 values are printed. If more values are there it adds '..'

The code is inspired by Zach Holman's spark shell script.

Example output (view in non-proportial font):

 Performance counter stats for 'true' (10 runs):

          0.175672      task-clock (msec)         #    0.555 CPUs utilized            ( +-  1.77% ) █▄▁▁▁▁▁▁..
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
               114      page-faults               #    0.647 M/sec                    ( +-  0.14% ) ▁█▁▁████..
           520,798      cycles                    #    2.965 GHz                      ( +-  1.75% ) █▄▁▁▁▁▁▁..
           433,525      instructions              #    0.83  insns per cycle          ( +-  0.28% ) ▅▇▅▄▇█▁▆..
            83,012      branches                  #  472.537 M/sec                    ( +-  0.31% ) ▅▇▆▄▇█▁▆..
             3,157      branch-misses             #    3.80% of all branches          ( +-  2.55% ) ▇█▃▅▁▃▁▂..

       0.000316660 seconds time elapsed                                          ( +-  1.78% ) █▅▁▁▁▁▁▁..

As you can see even in the most simple run there are quite interesting
patterns. The time sparkline suggests it would be also useful to have an option
to throw the first measurement away.

Known issues:
- Makes the perf stat output wider. Could be adjust by shrinking
some white space. Not done so far.
- No output for -A/--per-socket/--per-core with -rX. This code
is missing the basic noise detection code. Once it's added there
sparklines could be shown too.

v2: Avoid printing spark lines for normal CSV case (Jiri)
Signed-off-by: Andi Kleen <ak@linux.intel.com>
---
 tools/perf/Makefile.perf  |  1 +
 tools/perf/builtin-stat.c | 12 ++++++++++++
 tools/perf/util/spark.c   | 45 +++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/spark.h   |  3 +++
 tools/perf/util/stat.c    | 33 +++++++++++++++++++++++++++++++++
 tools/perf/util/stat.h    | 10 ++++++++++
 6 files changed, 104 insertions(+)
 create mode 100644 tools/perf/util/spark.c
 create mode 100644 tools/perf/util/spark.h

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 7257e7e..432d099 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -359,6 +359,7 @@ LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
 LIB_OBJS += $(OUTPUT)util/trace-event.o
 LIB_OBJS += $(OUTPUT)util/svghelper.o
 LIB_OBJS += $(OUTPUT)util/sort.o
+LIB_OBJS += $(OUTPUT)util/spark.o
 LIB_OBJS += $(OUTPUT)util/hist.o
 LIB_OBJS += $(OUTPUT)util/probe-event.o
 LIB_OBJS += $(OUTPUT)util/util.o
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 65a151e..cb0f7c5 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1176,6 +1176,9 @@ static void print_aggr(char *prefix)
 				if (run != ena)
 					fprintf(output, "  (%.2f%%)",
 						100.0 * run / ena);
+
+				fputc(' ', output);
+				print_stat_spark(output, counter->priv);
 			}
 			fputc('\n', output);
 		}
@@ -1229,6 +1232,9 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 		return;
 	}
 
+	fputc(' ', output);
+	print_stat_spark(output, counter->priv);
+
 	if (scaled) {
 		double avg_enabled, avg_running;
 
@@ -1295,6 +1301,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
 			if (run != ena)
 				fprintf(output, "  (%.2f%%)",
 					100.0 * run / ena);
+
+			fputc(' ', output);
+			print_stat_spark(output, counter->priv);
 		}
 		fputc('\n', output);
 	}
@@ -1355,6 +1364,9 @@ static void print_stat(int argc, const char **argv)
 			fprintf(output, "                                        ");
 			print_noise_pct(stddev_stats(&walltime_nsecs_stats),
 					avg_stats(&walltime_nsecs_stats));
+
+			fputc(' ', output);
+			print_stat_spark(output, &walltime_nsecs_stats);
 		}
 		fprintf(output, "\n\n");
 	}
diff --git a/tools/perf/util/spark.c b/tools/perf/util/spark.c
new file mode 100644
index 0000000..a677d2c
--- /dev/null
+++ b/tools/perf/util/spark.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <limits.h>
+#include "spark.h"
+
+#define NUM_SPARKS 8
+#define SPARK_SHIFT 8
+
+/* Print spark lines on outf for numval values in val. */
+void print_spark(FILE *outf, unsigned long *val, int numval)
+{
+	static const char *ticks[NUM_SPARKS] = {
+		"▁",  "▂", "▃", "▄", "▅", "▆", "▇", "█"
+	};
+	int i;
+	unsigned long min = LONG_MAX, max = 0, f;
+
+	for (i = 0; i < numval; i++) {
+		if (val[i] < min)
+			min = val[i];
+		if (val[i] > max)
+			max = val[i];
+	}
+	f = ((max - min) << SPARK_SHIFT) / (NUM_SPARKS - 1);
+	if (f < 1)
+		f = 1;
+	for (i = 0; i < numval; i++) {
+		fputs(ticks[((val[i] - min) << SPARK_SHIFT) / f], outf);
+	}
+}
+
+#ifdef TEST
+#include <stdlib.h>
+
+int main(int ac, char **av)
+{
+	unsigned long *val = calloc(ac - 1, sizeof(unsigned long));
+	int i;
+
+	for (i = 1; i < ac; i++)
+		val[i - 1] = strtoul(av[i], NULL, 0);
+	print_spark(stdout, val, ac - 1);
+	putchar('\n');
+	return 0;
+}
+#endif
diff --git a/tools/perf/util/spark.h b/tools/perf/util/spark.h
new file mode 100644
index 0000000..f2d5ac5
--- /dev/null
+++ b/tools/perf/util/spark.h
@@ -0,0 +1,3 @@
+#pragma once
+void print_spark(FILE *outf, unsigned long *val, int numval);
+
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 6506b3d..2b26d74 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -1,10 +1,16 @@
 #include <math.h>
+#include <stdio.h>
 
 #include "stat.h"
+#include "spark.h"
 
 void update_stats(struct stats *stats, u64 val)
 {
 	double delta;
+	int n = stats->n;
+
+	if (n < NUM_SPARK_VALS)
+		stats->svals[n] = val;
 
 	stats->n++;
 	delta = val - stats->mean;
@@ -61,3 +67,30 @@ double rel_stddev_stats(double stddev, double avg)
 
 	return pct;
 }
+
+static int all_the_same(unsigned long *vals, int len)
+{
+	int i;
+	unsigned long v0 = vals[0];
+
+	for (i = 1; i < len; i++)
+		if (vals[i] != v0)
+			return 0;
+	return 1;
+}
+
+void print_stat_spark(FILE *f, struct stats *stat)
+{
+	int n = stat->n, len;
+
+	if (n <= 1)
+		return;
+	len = n;
+	if (len > NUM_SPARK_VALS)
+		len = NUM_SPARK_VALS;
+	if (all_the_same(stat->svals, len))
+		return;
+	print_spark(f, stat->svals, len);
+	if (stat->n > NUM_SPARK_VALS)
+		fputs("..", f);
+}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index ae8ccd7..1b4dc71 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -1,12 +1,16 @@
 #ifndef __PERF_STATS_H
 #define __PERF_STATS_H
 
+#include <stdio.h>
 #include "types.h"
 
+#define NUM_SPARK_VALS 8 /* support spark line on first N items */
+
 struct stats
 {
 	double n, mean, M2;
 	u64 max, min;
+	unsigned long svals[NUM_SPARK_VALS];
 };
 
 void update_stats(struct stats *stats, u64 val);
@@ -14,12 +18,18 @@ double avg_stats(struct stats *stats);
 double stddev_stats(struct stats *stats);
 double rel_stddev_stats(double stddev, double avg);
 
+void print_stat_spark(FILE *f, struct stats *stat);
+
 static inline void init_stats(struct stats *stats)
 {
+	int i;
+
 	stats->n    = 0.0;
 	stats->mean = 0.0;
 	stats->M2   = 0.0;
 	stats->min  = (u64) -1;
 	stats->max  = 0;
+	for (i = 0; i < NUM_SPARK_VALS; i++)
+		stats->svals[i] = 0;
 }
 #endif
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-04-14 16:09 [PATCH] perf, tools: Support spark lines in perf stat v2 Andi Kleen
@ 2014-04-15  6:35 ` Namhyung Kim
  2014-04-16 18:46   ` Andi Kleen
  0 siblings, 1 reply; 8+ messages in thread
From: Namhyung Kim @ 2014-04-15  6:35 UTC (permalink / raw)
  To: Andi Kleen; +Cc: jolsa, acme, linux-kernel, Andi Kleen

Hi Andi,

On Mon, 14 Apr 2014 09:09:31 -0700, Andi Kleen wrote:
> From: Andi Kleen <ak@linux.intel.com>
>
> perf stat -rX prints the stddev for multiple measurements.
> Just looking at the stddev for judging the quality of the data
> is a bit dangerous The simplest sanity check is to just look
> at a simple plot. This patchs add a sparkline to the end
> of the measurements to make it simple to judge the data.
>
> The sparkline only uses UTF-8, so should be readable
> in all modern tools and terminals.
>
> The sparkline is between the minimum and maximum of the data,
> so it's mainly a indicator of variance. To keep the code
> simple and make the output not too wide only the first
> 8 values are printed. If more values are there it adds '..'
>
> The code is inspired by Zach Holman's spark shell script.
>
> Example output (view in non-proportial font):
>
>  Performance counter stats for 'true' (10 runs):
>
>           0.175672      task-clock (msec)         #    0.555 CPUs utilized            ( +-  1.77% ) █▄▁▁▁▁▁▁..
>                  0      context-switches          #    0.000 K/sec
>                  0      cpu-migrations            #    0.000 K/sec
>                114      page-faults               #    0.647 M/sec                    ( +-  0.14% ) ▁█▁▁████..
>            520,798      cycles                    #    2.965 GHz                      ( +-  1.75% ) █▄▁▁▁▁▁▁..
>            433,525      instructions              #    0.83  insns per cycle          ( +-  0.28% ) ▅▇▅▄▇█▁▆..
>             83,012      branches                  #  472.537 M/sec                    ( +-  0.31% ) ▅▇▆▄▇█▁▆..
>              3,157      branch-misses             #    3.80% of all branches          ( +-  2.55% ) ▇█▃▅▁▃▁▂..
>
>        0.000316660 seconds time elapsed                                          ( +-  1.78% ) █▅▁▁▁▁▁▁..
>
> As you can see even in the most simple run there are quite interesting
> patterns. The time sparkline suggests it would be also useful to have an option
> to throw the first measurement away.
>
> Known issues:
> - Makes the perf stat output wider. Could be adjust by shrinking
> some white space. Not done so far.
> - No output for -A/--per-socket/--per-core with -rX. This code
> is missing the basic noise detection code. Once it's added there
> sparklines could be shown too.
>
> v2: Avoid printing spark lines for normal CSV case (Jiri)
> Signed-off-by: Andi Kleen <ak@linux.intel.com>
> ---


[SNIP]
> diff --git a/tools/perf/util/spark.c b/tools/perf/util/spark.c
> new file mode 100644
> index 0000000..a677d2c
> --- /dev/null
> +++ b/tools/perf/util/spark.c
> @@ -0,0 +1,45 @@
> +#include <stdio.h>
> +#include <limits.h>
> +#include "spark.h"
> +
> +#define NUM_SPARKS 8
> +#define SPARK_SHIFT 8
> +
> +/* Print spark lines on outf for numval values in val. */
> +void print_spark(FILE *outf, unsigned long *val, int numval)
> +{
> +	static const char *ticks[NUM_SPARKS] = {
> +		"▁",  "▂", "▃", "▄", "▅", "▆", "▇", "█"
> +	};
> +	int i;
> +	unsigned long min = LONG_MAX, max = 0, f;

s/LONG_MAX/ULONG_MAX/ ?

> +
> +	for (i = 0; i < numval; i++) {
> +		if (val[i] < min)
> +			min = val[i];
> +		if (val[i] > max)
> +			max = val[i];
> +	}
> +	f = ((max - min) << SPARK_SHIFT) / (NUM_SPARKS - 1);
> +	if (f < 1)
> +		f = 1;
> +	for (i = 0; i < numval; i++) {
> +		fputs(ticks[((val[i] - min) << SPARK_SHIFT) / f], outf);
> +	}
> +}
> +
> +#ifdef TEST
> +#include <stdlib.h>
> +
> +int main(int ac, char **av)
> +{
> +	unsigned long *val = calloc(ac - 1, sizeof(unsigned long));
> +	int i;
> +
> +	for (i = 1; i < ac; i++)
> +		val[i - 1] = strtoul(av[i], NULL, 0);
> +	print_spark(stdout, val, ac - 1);
> +	putchar('\n');
> +	return 0;
> +}
> +#endif

Hmm.. test codes usually live in the tools/perf/tests directory.


> diff --git a/tools/perf/util/spark.h b/tools/perf/util/spark.h
> new file mode 100644
> index 0000000..f2d5ac5
> --- /dev/null
> +++ b/tools/perf/util/spark.h
> @@ -0,0 +1,3 @@
> +#pragma once
> +void print_spark(FILE *outf, unsigned long *val, int numval);
> +
> diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
> index 6506b3d..2b26d74 100644
> --- a/tools/perf/util/stat.c
> +++ b/tools/perf/util/stat.c
> @@ -1,10 +1,16 @@
>  #include <math.h>
> +#include <stdio.h>
>  
>  #include "stat.h"
> +#include "spark.h"
>  
>  void update_stats(struct stats *stats, u64 val)
>  {
>  	double delta;
> +	int n = stats->n;
> +
> +	if (n < NUM_SPARK_VALS)
> +		stats->svals[n] = val;
>  
>  	stats->n++;
>  	delta = val - stats->mean;
> @@ -61,3 +67,30 @@ double rel_stddev_stats(double stddev, double avg)
>  
>  	return pct;
>  }
> +
> +static int all_the_same(unsigned long *vals, int len)
> +{
> +	int i;
> +	unsigned long v0 = vals[0];
> +
> +	for (i = 1; i < len; i++)
> +		if (vals[i] != v0)
> +			return 0;
> +	return 1;
> +}
> +
> +void print_stat_spark(FILE *f, struct stats *stat)
> +{
> +	int n = stat->n, len;
> +
> +	if (n <= 1)
> +		return;
> +	len = n;

It seems the 'n' is not needed at all - just use 'len'.


> +	if (len > NUM_SPARK_VALS)
> +		len = NUM_SPARK_VALS;
> +	if (all_the_same(stat->svals, len))
> +		return;

Why does it skip printing if all values are same?  I think you wanted to
skip the "all zero" (uncounted) case, right?

Also adding a few blank lines might improve readability a bit IMHO.

Thanks,
Namhyung


> +	print_spark(f, stat->svals, len);
> +	if (stat->n > NUM_SPARK_VALS)
> +		fputs("..", f);
> +}
> diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
> index ae8ccd7..1b4dc71 100644
> --- a/tools/perf/util/stat.h
> +++ b/tools/perf/util/stat.h
> @@ -1,12 +1,16 @@
>  #ifndef __PERF_STATS_H
>  #define __PERF_STATS_H
>  
> +#include <stdio.h>
>  #include "types.h"
>  
> +#define NUM_SPARK_VALS 8 /* support spark line on first N items */
> +
>  struct stats
>  {
>  	double n, mean, M2;
>  	u64 max, min;
> +	unsigned long svals[NUM_SPARK_VALS];
>  };
>  
>  void update_stats(struct stats *stats, u64 val);
> @@ -14,12 +18,18 @@ double avg_stats(struct stats *stats);
>  double stddev_stats(struct stats *stats);
>  double rel_stddev_stats(double stddev, double avg);
>  
> +void print_stat_spark(FILE *f, struct stats *stat);
> +
>  static inline void init_stats(struct stats *stats)
>  {
> +	int i;
> +
>  	stats->n    = 0.0;
>  	stats->mean = 0.0;
>  	stats->M2   = 0.0;
>  	stats->min  = (u64) -1;
>  	stats->max  = 0;
> +	for (i = 0; i < NUM_SPARK_VALS; i++)
> +		stats->svals[i] = 0;
>  }
>  #endif

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-04-15  6:35 ` Namhyung Kim
@ 2014-04-16 18:46   ` Andi Kleen
  0 siblings, 0 replies; 8+ messages in thread
From: Andi Kleen @ 2014-04-16 18:46 UTC (permalink / raw)
  To: Namhyung Kim; +Cc: Andi Kleen, jolsa, acme, linux-kernel, Andi Kleen

> > +void print_stat_spark(FILE *f, struct stats *stat)
> > +{
> > +	int n = stat->n, len;
> > +
> > +	if (n <= 1)
> > +		return;
> > +	len = n;
> 
> It seems the 'n' is not needed at all - just use 'len'.

This would break ".." for n == NUM_SPARK_VALS
> 
> 
> > +	if (len > NUM_SPARK_VALS)
> > +		len = NUM_SPARK_VALS;
> > +	if (all_the_same(stat->svals, len))
> > +		return;
> 
> Why does it skip printing if all values are same?  I think you wanted to
> skip the "all zero" (uncounted) case, right?

A spark line is meaningless if all values are the same.

-Andi

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-05-26 17:33 ` Jiri Olsa
@ 2014-05-28  0:18   ` Andi Kleen
  0 siblings, 0 replies; 8+ messages in thread
From: Andi Kleen @ 2014-05-28  0:18 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Andi Kleen, acme, linux-kernel, peterz, eranian, namhyung, Andi Kleen

> 
> ok.. confusing.. so this v2 has changes which are missing in v3 patch:
> http://marc.info/?l=linux-kernel&m=139767371027557&w=2

What do you think is missing? I removed some code Namhyung didn't like
in v3

-Andi

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-05-12 23:01 Andi Kleen
  2014-05-13  9:27 ` Peter Zijlstra
@ 2014-05-26 17:33 ` Jiri Olsa
  2014-05-28  0:18   ` Andi Kleen
  1 sibling, 1 reply; 8+ messages in thread
From: Jiri Olsa @ 2014-05-26 17:33 UTC (permalink / raw)
  To: Andi Kleen; +Cc: acme, linux-kernel, peterz, eranian, namhyung, Andi Kleen

On Mon, May 12, 2014 at 04:01:26PM -0700, Andi Kleen wrote:
> From: Andi Kleen <ak@linux.intel.com>
> 
> perf stat -rX prints the stddev for multiple measurements.
> Just looking at the stddev for judging the quality of the data
> is a bit dangerous The simplest sanity check is to just look
> at a simple plot. This patchs add a sparkline to the end
> of the measurements to make it simple to judge the data.
> 
> The sparkline only uses UTF-8, so should be readable
> in all modern tools and terminals.
> 
> The sparkline is between the minimum and maximum of the data,
> so it's mainly a indicator of variance. To keep the code
> simple and make the output not too wide only the first
> 8 values are printed. If more values are there it adds '..'
> 
> The code is inspired by Zach Holman's spark shell script.
> 
> Example output (view in non-proportial font):
> 
>  Performance counter stats for 'true' (10 runs):
> 
>           0.175672      task-clock (msec)         #    0.555 CPUs utilized            ( +-  1.77% ) █▄▁▁▁▁▁▁..
>                  0      context-switches          #    0.000 K/sec
>                  0      cpu-migrations            #    0.000 K/sec
>                114      page-faults               #    0.647 M/sec                    ( +-  0.14% ) ▁█▁▁████..
>            520,798      cycles                    #    2.965 GHz                      ( +-  1.75% ) █▄▁▁▁▁▁▁..
>            433,525      instructions              #    0.83  insns per cycle          ( +-  0.28% ) ▅▇▅▄▇█▁▆..
>             83,012      branches                  #  472.537 M/sec                    ( +-  0.31% ) ▅▇▆▄▇█▁▆..
>              3,157      branch-misses             #    3.80% of all branches          ( +-  2.55% ) ▇█▃▅▁▃▁▂..
> 
>        0.000316660 seconds time elapsed                                          ( +-  1.78% ) █▅▁▁▁▁▁▁..
> 
> As you can see even in the most simple run there are quite interesting
> patterns. The time sparkline suggests it would be also useful to have an option
> to throw the first measurement away.
> 
> Known issues:
> - Makes the perf stat output wider. Could be adjust by shrinking
> some white space. Not done so far.
> - No output for -A/--per-socket/--per-core with -rX. This code
> is missing the basic noise detection code. Once it's added there
> sparklines could be shown too.
> 
> v2: Address review feedback. Use all_zero() and remove len.

ok.. confusing.. so this v2 has changes which are missing in v3 patch:
http://marc.info/?l=linux-kernel&m=139767371027557&w=2

jirka

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-05-13  9:27 ` Peter Zijlstra
@ 2014-05-13 14:08   ` Andi Kleen
  0 siblings, 0 replies; 8+ messages in thread
From: Andi Kleen @ 2014-05-13 14:08 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: acme, linux-kernel, eranian, namhyung, jolsa

Peter Zijlstra <peterz@infradead.org> writes:
>
> Hmm, my first looking at the spark thingies interpreted them as a
> histogram. But they're not really.

It's a histogram of variance.

> Would it be possible to make it a histogram of -2sigma,2sigma around the
> avg value? That way you can plot all data and get a good idea of the
> distribution. I'd suggest using 9 buckets for it.

I doubt that would work well with only ~8 values.
This would need multiple lines or a real plotter,
otherwise you would have all kinds of odd corner cases.

Sparklines are much better for limited space.

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] perf, tools: Support spark lines in perf stat v2
  2014-05-12 23:01 Andi Kleen
@ 2014-05-13  9:27 ` Peter Zijlstra
  2014-05-13 14:08   ` Andi Kleen
  2014-05-26 17:33 ` Jiri Olsa
  1 sibling, 1 reply; 8+ messages in thread
From: Peter Zijlstra @ 2014-05-13  9:27 UTC (permalink / raw)
  To: Andi Kleen; +Cc: acme, linux-kernel, eranian, namhyung, jolsa, Andi Kleen

[-- Attachment #1: Type: text/plain, Size: 2496 bytes --]

On Mon, May 12, 2014 at 04:01:26PM -0700, Andi Kleen wrote:
> From: Andi Kleen <ak@linux.intel.com>
> 
> perf stat -rX prints the stddev for multiple measurements.
> Just looking at the stddev for judging the quality of the data
> is a bit dangerous The simplest sanity check is to just look
> at a simple plot. This patchs add a sparkline to the end
> of the measurements to make it simple to judge the data.
> 
> The sparkline only uses UTF-8, so should be readable
> in all modern tools and terminals.
> 
> The sparkline is between the minimum and maximum of the data,
> so it's mainly a indicator of variance. To keep the code
> simple and make the output not too wide only the first
> 8 values are printed. If more values are there it adds '..'
> 
> The code is inspired by Zach Holman's spark shell script.
> 
> Example output (view in non-proportial font):
> 
>  Performance counter stats for 'true' (10 runs):
> 
>           0.175672      task-clock (msec)         #    0.555 CPUs utilized            ( +-  1.77% ) █▄▁▁▁▁▁▁..
>                  0      context-switches          #    0.000 K/sec
>                  0      cpu-migrations            #    0.000 K/sec
>                114      page-faults               #    0.647 M/sec                    ( +-  0.14% ) ▁█▁▁████..
>            520,798      cycles                    #    2.965 GHz                      ( +-  1.75% ) █▄▁▁▁▁▁▁..
>            433,525      instructions              #    0.83  insns per cycle          ( +-  0.28% ) ▅▇▅▄▇█▁▆..
>             83,012      branches                  #  472.537 M/sec                    ( +-  0.31% ) ▅▇▆▄▇█▁▆..
>              3,157      branch-misses             #    3.80% of all branches          ( +-  2.55% ) ▇█▃▅▁▃▁▂..
> 
>        0.000316660 seconds time elapsed                                          ( +-  1.78% ) █▅▁▁▁▁▁▁..
> 
> As you can see even in the most simple run there are quite interesting
> patterns. The time sparkline suggests it would be also useful to have an option
> to throw the first measurement away.

Hmm, my first looking at the spark thingies interpreted them as a
histogram. But they're not really.

Would it be possible to make it a histogram of -2sigma,2sigma around the
avg value? That way you can plot all data and get a good idea of the
distribution. I'd suggest using 9 buckets for it.

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] perf, tools: Support spark lines in perf stat v2
@ 2014-05-12 23:01 Andi Kleen
  2014-05-13  9:27 ` Peter Zijlstra
  2014-05-26 17:33 ` Jiri Olsa
  0 siblings, 2 replies; 8+ messages in thread
From: Andi Kleen @ 2014-05-12 23:01 UTC (permalink / raw)
  To: acme; +Cc: linux-kernel, peterz, eranian, namhyung, jolsa, Andi Kleen

From: Andi Kleen <ak@linux.intel.com>

perf stat -rX prints the stddev for multiple measurements.
Just looking at the stddev for judging the quality of the data
is a bit dangerous The simplest sanity check is to just look
at a simple plot. This patchs add a sparkline to the end
of the measurements to make it simple to judge the data.

The sparkline only uses UTF-8, so should be readable
in all modern tools and terminals.

The sparkline is between the minimum and maximum of the data,
so it's mainly a indicator of variance. To keep the code
simple and make the output not too wide only the first
8 values are printed. If more values are there it adds '..'

The code is inspired by Zach Holman's spark shell script.

Example output (view in non-proportial font):

 Performance counter stats for 'true' (10 runs):

          0.175672      task-clock (msec)         #    0.555 CPUs utilized            ( +-  1.77% ) █▄▁▁▁▁▁▁..
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
               114      page-faults               #    0.647 M/sec                    ( +-  0.14% ) ▁█▁▁████..
           520,798      cycles                    #    2.965 GHz                      ( +-  1.75% ) █▄▁▁▁▁▁▁..
           433,525      instructions              #    0.83  insns per cycle          ( +-  0.28% ) ▅▇▅▄▇█▁▆..
            83,012      branches                  #  472.537 M/sec                    ( +-  0.31% ) ▅▇▆▄▇█▁▆..
             3,157      branch-misses             #    3.80% of all branches          ( +-  2.55% ) ▇█▃▅▁▃▁▂..

       0.000316660 seconds time elapsed                                          ( +-  1.78% ) █▅▁▁▁▁▁▁..

As you can see even in the most simple run there are quite interesting
patterns. The time sparkline suggests it would be also useful to have an option
to throw the first measurement away.

Known issues:
- Makes the perf stat output wider. Could be adjust by shrinking
some white space. Not done so far.
- No output for -A/--per-socket/--per-core with -rX. This code
is missing the basic noise detection code. Once it's added there
sparklines could be shown too.

v2: Address review feedback. Use all_zero() and remove len.
Remove test code. Rebased to perf/core
Signed-off-by: Andi Kleen <ak@linux.intel.com>
---
 tools/perf/Makefile.perf  |  1 +
 tools/perf/builtin-stat.c | 12 ++++++++++++
 tools/perf/util/spark.c   | 29 +++++++++++++++++++++++++++++
 tools/perf/util/spark.h   |  3 +++
 tools/perf/util/stat.c    | 33 +++++++++++++++++++++++++++++++++
 tools/perf/util/stat.h    | 10 ++++++++++
 6 files changed, 88 insertions(+)
 create mode 100644 tools/perf/util/spark.c
 create mode 100644 tools/perf/util/spark.h

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 2baf61c..2f83f43 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -359,6 +359,7 @@ LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
 LIB_OBJS += $(OUTPUT)util/trace-event.o
 LIB_OBJS += $(OUTPUT)util/svghelper.o
 LIB_OBJS += $(OUTPUT)util/sort.o
+LIB_OBJS += $(OUTPUT)util/spark.o
 LIB_OBJS += $(OUTPUT)util/hist.o
 LIB_OBJS += $(OUTPUT)util/probe-event.o
 LIB_OBJS += $(OUTPUT)util/util.o
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 65a151e..6bf7b6c 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1176,6 +1176,9 @@ static void print_aggr(char *prefix)
 				if (run != ena)
 					fprintf(output, "  (%.2f%%)",
 						100.0 * run / ena);
+
+				fputc(' ', output);
+				print_stat_spark(output, counter->priv);
 			}
 			fputc('\n', output);
 		}
@@ -1224,6 +1227,9 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 
 	print_noise(counter, avg);
 
+	fputc(' ', output);
+	print_stat_spark(output, counter->priv);
+
 	if (csv_output) {
 		fputc('\n', output);
 		return;
@@ -1295,6 +1301,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
 			if (run != ena)
 				fprintf(output, "  (%.2f%%)",
 					100.0 * run / ena);
+
+			fputc(' ', output);
+			print_stat_spark(output, counter->priv);
 		}
 		fputc('\n', output);
 	}
@@ -1355,6 +1364,9 @@ static void print_stat(int argc, const char **argv)
 			fprintf(output, "                                        ");
 			print_noise_pct(stddev_stats(&walltime_nsecs_stats),
 					avg_stats(&walltime_nsecs_stats));
+
+			fputc(' ', output);
+			print_stat_spark(output, &walltime_nsecs_stats);
 		}
 		fprintf(output, "\n\n");
 	}
diff --git a/tools/perf/util/spark.c b/tools/perf/util/spark.c
new file mode 100644
index 0000000..c43b614
--- /dev/null
+++ b/tools/perf/util/spark.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <limits.h>
+#include "spark.h"
+
+#define NUM_SPARKS 8
+#define SPARK_SHIFT 8
+
+/* Print spark lines on outf for numval values in val. */
+void print_spark(FILE *outf, unsigned long *val, int numval)
+{
+	static const char *ticks[NUM_SPARKS] = {
+		"▁",  "▂", "▃", "▄", "▅", "▆", "▇", "█"
+	};
+	int i;
+	unsigned long min = LONG_MAX, max = 0, f;
+
+	for (i = 0; i < numval; i++) {
+		if (val[i] < min)
+			min = val[i];
+		if (val[i] > max)
+			max = val[i];
+	}
+	f = ((max - min) << SPARK_SHIFT) / (NUM_SPARKS - 1);
+	if (f < 1)
+		f = 1;
+	for (i = 0; i < numval; i++) {
+		fputs(ticks[((val[i] - min) << SPARK_SHIFT) / f], outf);
+	}
+}
diff --git a/tools/perf/util/spark.h b/tools/perf/util/spark.h
new file mode 100644
index 0000000..f2d5ac5
--- /dev/null
+++ b/tools/perf/util/spark.h
@@ -0,0 +1,3 @@
+#pragma once
+void print_spark(FILE *outf, unsigned long *val, int numval);
+
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 6506b3d..ecb8d3e 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -1,10 +1,16 @@
 #include <math.h>
+#include <stdio.h>
 
 #include "stat.h"
+#include "spark.h"
 
 void update_stats(struct stats *stats, u64 val)
 {
 	double delta;
+	int n = stats->n;
+
+	if (n < NUM_SPARK_VALS)
+		stats->svals[n] = val;
 
 	stats->n++;
 	delta = val - stats->mean;
@@ -61,3 +67,30 @@ double rel_stddev_stats(double stddev, double avg)
 
 	return pct;
 }
+
+static int all_zero(unsigned long *vals, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		if (vals[i] != 0)
+			return 0;
+	return 1;
+}
+
+void print_stat_spark(FILE *f, struct stats *stat)
+{
+	int n = stat->n;
+
+	if (n <= 1)
+		return;
+
+	if (n > NUM_SPARK_VALS)
+		n = NUM_SPARK_VALS;
+	if (all_zero(stat->svals, n))
+		return;
+
+	print_spark(f, stat->svals, n);
+	if (stat->n > NUM_SPARK_VALS)
+		fputs("..", f);
+}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index 5667fc3..38c085c 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -2,11 +2,15 @@
 #define __PERF_STATS_H
 
 #include <linux/types.h>
+#include <stdio.h>
+
+#define NUM_SPARK_VALS 8 /* support spark line on first N items */
 
 struct stats
 {
 	double n, mean, M2;
 	u64 max, min;
+	unsigned long svals[NUM_SPARK_VALS];
 };
 
 void update_stats(struct stats *stats, u64 val);
@@ -14,12 +18,18 @@ double avg_stats(struct stats *stats);
 double stddev_stats(struct stats *stats);
 double rel_stddev_stats(double stddev, double avg);
 
+void print_stat_spark(FILE *f, struct stats *stat);
+
 static inline void init_stats(struct stats *stats)
 {
+	int i;
+
 	stats->n    = 0.0;
 	stats->mean = 0.0;
 	stats->M2   = 0.0;
 	stats->min  = (u64) -1;
 	stats->max  = 0;
+	for (i = 0; i < NUM_SPARK_VALS; i++)
+		stats->svals[i] = 0;
 }
 #endif
-- 
1.9.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2014-05-28  0:18 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-14 16:09 [PATCH] perf, tools: Support spark lines in perf stat v2 Andi Kleen
2014-04-15  6:35 ` Namhyung Kim
2014-04-16 18:46   ` Andi Kleen
2014-05-12 23:01 Andi Kleen
2014-05-13  9:27 ` Peter Zijlstra
2014-05-13 14:08   ` Andi Kleen
2014-05-26 17:33 ` Jiri Olsa
2014-05-28  0:18   ` Andi Kleen

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).