selftests/powerpc: Add Anton's null_syscall benchmark to the selftests
authorAnton Blanchard <anton@au.ibm.com>
Tue, 27 Sep 2016 14:10:16 +0000 (22:10 +0800)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 14 Nov 2016 00:11:51 +0000 (11:11 +1100)
Pull in a version of Anton's null_syscall benchmark:
  http://ozlabs.org/~anton/junkcode/null_syscall.c

Suggested-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Anton Blanchard <anton@au.ibm.com>
Signed-off-by: Rui Teng <rui.teng@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
tools/testing/selftests/powerpc/benchmarks/.gitignore
tools/testing/selftests/powerpc/benchmarks/Makefile
tools/testing/selftests/powerpc/benchmarks/null_syscall.c [new file with mode: 0644]

index bce49ebd869ecbbaaf689c92d0dbdadaa6d4e1f0..04dc1e6ef2ce7325a2b40368850fc7ce1e8eacbf 100644 (file)
@@ -1,4 +1,5 @@
 gettimeofday
 context_switch
 mmap_bench
-futex_bench
\ No newline at end of file
+futex_bench
+null_syscall
index a9adfb7de78f3f163e5090c9c1ce7690e64b1afe..545077f98f72d72ea5fce37892812295c0ebe32b 100644 (file)
@@ -1,4 +1,4 @@
-TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench
+TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench null_syscall
 
 CFLAGS += -O2
 
diff --git a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
new file mode 100644 (file)
index 0000000..ecc14d6
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Test null syscall performance
+ *
+ * Copyright (C) 2009-2015 Anton Blanchard, IBM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define NR_LOOPS 10000000
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+static volatile int soak_done;
+unsigned long long clock_frequency;
+unsigned long long timebase_frequency;
+double timebase_multiplier;
+
+static inline unsigned long long mftb(void)
+{
+       unsigned long low;
+
+       asm volatile("mftb %0" : "=r" (low));
+
+       return low;
+}
+
+static void sigalrm_handler(int unused)
+{
+       soak_done = 1;
+}
+
+/*
+ * Use a timer instead of busy looping on clock_gettime() so we don't
+ * pollute profiles with glibc and VDSO hits.
+ */
+static void cpu_soak_usecs(unsigned long usecs)
+{
+       struct itimerval val;
+
+       memset(&val, 0, sizeof(val));
+       val.it_value.tv_usec = usecs;
+
+       signal(SIGALRM, sigalrm_handler);
+       setitimer(ITIMER_REAL, &val, NULL);
+
+       while (1) {
+               if (soak_done)
+                       break;
+       }
+
+       signal(SIGALRM, SIG_DFL);
+}
+
+/*
+ * This only works with recent kernels where cpufreq modifies
+ * /proc/cpuinfo dynamically.
+ */
+static void get_proc_frequency(void)
+{
+       FILE *f;
+       char line[128];
+       char *p, *end;
+       unsigned long v;
+       double d;
+       char *override;
+
+       /* Try to get out of low power/low frequency mode */
+       cpu_soak_usecs(0.25 * 1000000);
+
+       f = fopen("/proc/cpuinfo", "r");
+       if (f == NULL)
+               return;
+
+       timebase_frequency = 0;
+
+       while (fgets(line, sizeof(line), f) != NULL) {
+               if (strncmp(line, "timebase", 8) == 0) {
+                       p = strchr(line, ':');
+                       if (p != NULL) {
+                               v = strtoull(p + 1, &end, 0);
+                               if (end != p + 1)
+                                       timebase_frequency = v;
+                       }
+               }
+
+               if (((strncmp(line, "clock", 5) == 0) ||
+                    (strncmp(line, "cpu MHz", 7) == 0))) {
+                       p = strchr(line, ':');
+                       if (p != NULL) {
+                               d = strtod(p + 1, &end);
+                               if (end != p + 1) {
+                                       /* Find fastest clock frequency */
+                                       if ((d * 1000000ULL) > clock_frequency)
+                                               clock_frequency = d * 1000000ULL;
+                               }
+                       }
+               }
+       }
+
+       fclose(f);
+
+       override = getenv("FREQUENCY");
+       if (override)
+               clock_frequency = strtoull(override, NULL, 10);
+
+       if (timebase_frequency)
+               timebase_multiplier = (double)clock_frequency
+                                       / timebase_frequency;
+       else
+               timebase_multiplier = 1;
+}
+
+static void do_null_syscall(unsigned long nr)
+{
+       unsigned long i;
+
+       for (i = 0; i < nr; i++)
+               getppid();
+}
+
+#define TIME(A, STR) \
+
+int main(void)
+{
+       unsigned long tb_start, tb_now;
+       struct timespec tv_start, tv_now;
+       unsigned long long elapsed_ns, elapsed_tb;
+
+       get_proc_frequency();
+
+       clock_gettime(CLOCK_MONOTONIC, &tv_start);
+       tb_start = mftb();
+
+       do_null_syscall(NR_LOOPS);
+
+       clock_gettime(CLOCK_MONOTONIC, &tv_now);
+       tb_now = mftb();
+
+       elapsed_ns = (tv_now.tv_sec - tv_start.tv_sec) * 1000000000ULL +
+                       (tv_now.tv_nsec - tv_start.tv_nsec);
+       elapsed_tb = tb_now - tb_start;
+
+       printf("%10.2f ns %10.2f cycles\n", (float)elapsed_ns / NR_LOOPS,
+                       (float)elapsed_tb * timebase_multiplier / NR_LOOPS);
+
+       return 0;
+}