aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Renninger <trenn@suse.de>2009-08-04 20:17:42 +0200
committerDominik Brodowski <linux@dominikbrodowski.net>2009-08-04 20:17:42 +0200
commit5d2e3355391848b2552c1f97c856b1ca6fb1d22e (patch)
tree65d26280741b22537d46c71767e7ab1c38ff89c3
parent659204b524bf4b4a5973f24f89bea94b8e514ffe (diff)
downloadcpufrequtils-5d2e3355391848b2552c1f97c856b1ca6fb1d22e.tar.gz
Introduce cpufreq micro benchmark
What is this benchmark for: - Identify worst case performance loss when doing dynamic frequency scaling using Linux kernel governors - Identify average reaction time of a governor to CPU load changes - (Stress) Testing whether a cpufreq low level driver or governor works as expected - Identify cpufreq related performance regressions between kernels - Possibly Real time priority testing? -> what happens if there are processes with a higher prio than the governor's kernel thread - ... What this benchmark does *not* cover: - Power saving related regressions (In fact as better the performance throughput is, the worse the power savings will be, but the first should mostly count more...) - Real world (workloads) Details how to use it can be found in the readme. The benchmark got written by Christian Kornacker <ckornacker@suse.de> and is published under the GNU General Public License. (compare with copyright remarks inside the code). cpufrequtils Makefile adjustments and some minor compile warning cleanups came from: Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
-rw-r--r--Makefile22
-rw-r--r--bench/Makefile27
-rw-r--r--bench/README49
-rw-r--r--bench/benchmark.c160
-rw-r--r--bench/benchmark.h28
-rw-r--r--bench/config.h36
-rw-r--r--bench/example.cfg11
-rw-r--r--bench/main.c203
-rw-r--r--bench/parse.c201
-rw-r--r--bench/parse.h48
-rw-r--r--bench/system.c195
-rw-r--r--bench/system.h29
12 files changed, 1007 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index c46be5b..bfea0fb 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,9 @@ V ?= false
# Requires gettext.
NLS ?= true
+# cpufreq-bench benchmarking tool
+CPUFRQ_BENCH ?= true
+
# Use the sysfs-based interface which is included in all 2.6 kernels
# built with cpufreq support
SYSFS ?= true
@@ -62,10 +65,13 @@ LANGUAGES = de fr it cs pt
# added in front of any of them
bindir ?= /usr/bin
+sbindir ?= /usr/sbin
mandir ?= /usr/man
includedir ?= /usr/include
libdir ?= /usr/lib
localedir ?= /usr/share/locale
+docdir ?= /usr/share/doc/packages/cpufrequtils
+confdir ?= /etc/
# Toolchain: what tools do we use, and what options do they need:
@@ -142,6 +148,10 @@ ifeq ($(strip $(NLS)),true)
COMPILE_NLS += update-gmo
endif
+ifeq ($(strip $(CPUFRQ_BENCH)),true)
+ INSTALL_BENCH += install-bench
+ COMPILE_BENCH += compile-bench
+endif
CFLAGS += $(WARNINGS) -I$(GCCINCDIR)
@@ -169,7 +179,7 @@ endif
# the actual make rules
-all: ccdv libcpufreq utils $(COMPILE_NLS)
+all: ccdv libcpufreq utils $(COMPILE_NLS) $(COMPILE_BENCH)
ccdv: build/ccdv
build/ccdv: build/ccdv.c
@@ -215,6 +225,9 @@ update-gmo: po/$(PACKAGE).pot
msgfmt --statistics -o po/$$HLANG.gmo po/$$HLANG.po; \
done;
+compile-bench: libcpufreq
+ @V=$(V) make -C bench
+
clean:
-find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' -o -name '*.l[oas]' \) -type f -print \
| xargs rm -f
@@ -223,6 +236,7 @@ clean:
-rm -f cpufreq-info cpufreq-set
-rm -f build/ccdv
-rm -rf po/*.gmo po/*.pot
+ make -C bench clean
install-lib:
@@ -247,7 +261,11 @@ install-gmo:
$(INSTALL_DATA) -D po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpufrequtils.mo; \
done;
-install: install-lib install-tools install-man $(INSTALL_NLS)
+install-bench:
+ @#DESTDIR must be set from outside to survive
+ @sbindir=$(sbindir) docdir=$(docdir) confdir=$(confdir) make -C bench install
+
+install: install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH)
uninstall:
- rm -f $(DESTDIR)${libdir}/libcpufreq.*
diff --git a/bench/Makefile b/bench/Makefile
new file mode 100644
index 0000000..327c4e6
--- /dev/null
+++ b/bench/Makefile
@@ -0,0 +1,27 @@
+LIBS = -L../.libs/ -lm -lcpufreq
+
+OBJS = main.o parse.o system.o benchmark.o
+CFLAGS += -D_GNU_SOURCE
+
+ifeq ($(strip $(V)),false)
+ CC=@../build/ccdv gcc
+else
+ CC=gcc
+endif
+
+cpufreq-bench: $(OBJS)
+ $(CC) -o $@ $(CFLAGS) $(OBJS) $(LIBS)
+
+all: cpufreq-bench
+
+install:
+ mkdir -p $(DESTDIR)/$(sbindir)
+ mkdir -p $(DESTDIR)/$(docdir)
+ mkdir -p $(DESTDIR)/$(confdir)
+ install -m 755 cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench
+ install -m 644 README $(DESTDIR)/$(docdir)/README
+ install -m 644 example.cfg $(DESTDIR)/$(confdir)/cpufreq-bench.conf
+
+clean:
+ rm -f *.o
+ rm -f cpufreq-bench
diff --git a/bench/README b/bench/README
new file mode 100644
index 0000000..4f6a225
--- /dev/null
+++ b/bench/README
@@ -0,0 +1,49 @@
+This is cpufreq-bench, a microbenchmark for the cpufreq framework.
+
+description:
+cpufreq-bench helps to test the condition of a given cpufreq governor.
+For that purpose, it compares the performance governor to a configured
+powersave module.
+
+The functional principle is quite easy: we generate a load for a specific
+time with the performance governor. The load is generated with some rounds
+of calculation. Now, we idle for some time to let the CPU change to a lower
+frequency. Then, We take that amount of rounds and do another test with
+the powersave governor. But now, we don’t generate load for a specific time
+but rather generate load with the amount of calculations we’ve got.
+The resulting time is compared to the time we spent for the initial
+performance calculation.
+The powersave cycle should take 1-40% longer than the performance cycle due
+to the time the CPU needs to change to a higher frequency.
+nr. of calculations
+
+^
+|__________________ _ _ _ _ _
+| performance | powersave|
+| | |
+| | |
+|-----------------------------------------------> time
+
+To get a more precise value, this sleep/load cycle is done several times.
+We use the average values for the comparison.
+After each round, a specific time is added to the load and sleep time to see
+how good the sleep/load switch behaves with different timeframes.
+
+
+usage:
+-l, --load=<long int> initial load time in us
+-s, --sleep=<long int> initial sleep time in us
+-x, --load-step=<long int> time to be added to load time, in us
+-y, --sleep-step=<long int> time to be added to sleep time, in us
+-c, --cpu=<unsigned int> CPU Number to use, starting at 0
+-p, --prio=<priority> scheduler priority, HIGH, LOW or DEFAULT
+-g, --governor=<governor> cpufreq governor to test
+-n, --cycles=<int> load/sleep cycles to get an avarage value to compare
+-r, --rounds<int> load/sleep rounds
+-f, --file=<configfile> config file to use
+-o, --output=<dir> output dir, must exist
+-v, --verbose verbose output on/off
+
+Due to the high priority, the application my not be responsible for some time.
+After the benchmark, the logfile is saved in OUTPUTDIR/benchmark_TIMESTAMP.log
+
diff --git a/bench/benchmark.c b/bench/benchmark.c
new file mode 100644
index 0000000..d9c1d47
--- /dev/null
+++ b/bench/benchmark.c
@@ -0,0 +1,160 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "config.h"
+#include "system.h"
+#include "benchmark.h"
+
+/**
+ * compute how many rounds of calculation we should do
+ * to get the given load time
+ *
+ * @param load aimed load time in µs
+ *
+ * @retval rounds of calculation
+ **/
+
+unsigned int calculate_timespace(long load)
+{
+ int i;
+ long long now, then;
+ unsigned int estimated = GAUGECOUNT;
+ unsigned int rounds = 0;
+ unsigned int timed = 0;
+
+ printf("calibrating load of %lius, please wait...\n", load);
+
+ /* get the initial calculation time for a specific number of rounds */
+ now = get_time();
+ ROUNDS(estimated);
+ then = get_time();
+
+ timed = (unsigned int)(then - now);
+
+ /* approximation of the wanted load time by comparing with the
+ * initial calculation time */
+ for (i= 0; i < 4; i++)
+ {
+ rounds = (unsigned int)(load * estimated / timed);
+ dprintf("calibrating with %u rounds\n", rounds);
+ now = get_time();
+ ROUNDS(rounds);
+ then = get_time();
+
+ timed = (unsigned int)(then - now);
+ estimated = rounds;
+ }
+
+ printf("calibration done\n");
+
+ return estimated;
+}
+
+/**
+ * benchmark
+ * generates a specific sleep an load time with the performance
+ * governor and compares the used time for same calculations done
+ * with the configured powersave governor
+ *
+ * @param config config values for the benchmark
+ *
+ **/
+
+void start_benchmark(struct config *config)
+{
+ unsigned int _round, cycle;
+ long long now, then;
+ long sleep_time = 0, load_time = 0;
+ long performance_time = 0, powersave_time = 0;
+ unsigned int calculations;
+
+ sleep_time = config->sleep;
+ load_time = config->load;
+
+ for (_round=0; _round < config->rounds; _round++) {
+ performance_time = 0LL;
+ powersave_time = 0LL;
+
+ /* set the cpufreq governor to "performance" which disables
+ * P-State switching. */
+ if (set_cpufreq_governor("performance", config->cpu) != 0)
+ return;
+
+ printf("_round %i: doing %u cycles with %u calculations for %lius\n",
+ _round + 1, config->cycles, calculations, load_time);
+
+ /* calibrate the calculation time. the resulting calculation
+ * _rounds should produce a load which matches the configured
+ * load time */
+ calculations = calculate_timespace(load_time);
+
+ fprintf(config->output, "%li %li %li %u ",
+ load_time, sleep_time, load_time / calculations, calculations);
+
+ if (config->verbose) {
+ printf("avarage: %lius, rps:%li\n", load_time / calculations, 1000000 * calculations / load_time);
+ }
+
+ /* do some sleep/load cycles and determine the avarege time we need
+ * for one cycle */
+ for (cycle = 0; cycle < config->cycles; cycle++) {
+ now = get_time();
+ usleep(sleep_time);
+ ROUNDS(calculations);
+ then = get_time();
+ performance_time += then - now - sleep_time;
+ if (config->verbose)
+ printf("performance cycle took %lius, sleep: %lius, load: %lius, rounds: %u\n",
+ (long)(then - now), sleep_time, load_time, calculations);
+ }
+ fprintf(config->output, "%li ", performance_time / config->cycles);
+
+ /* set the powersave governor which activates P-State switching
+ * again */
+ if (set_cpufreq_governor(config->governor, config->cpu) != 0)
+ return;
+
+ /* again, do some sleep/load cycles with the powersave governor */
+ for (cycle = 0; cycle < config->cycles; cycle++) {
+ now = get_time();
+ usleep(sleep_time);
+ ROUNDS(calculations);
+ then = get_time();
+ powersave_time += then - now - sleep_time;
+ if (config->verbose)
+ printf("powersave cycle took %lius, sleep: %lius, load: %lius, rounds: %u\n",
+ (long)(then - now), sleep_time, load_time, calculations);
+ }
+
+ /* compare the avarage sleep/load cycles */
+ fprintf(config->output, "%li ", powersave_time / config->cycles);
+ fprintf(config->output, "%.3f\n", performance_time * 100.0 / powersave_time);
+
+ if (config->verbose)
+ printf("performance is at %.2f%%\n", performance_time * 100.0 / powersave_time);
+
+ sleep_time += config->sleep_step;
+ load_time += config->load_step;
+ }
+}
+
diff --git a/bench/benchmark.h b/bench/benchmark.h
new file mode 100644
index 0000000..9def4fd
--- /dev/null
+++ b/bench/benchmark.h
@@ -0,0 +1,28 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* load loop, this schould take about 1 to 2ms to complete */
+#define ROUNDS(x) {unsigned int rcnt; \
+ for (rcnt = 0; rcnt< x*1000; rcnt++) { \
+ (void)(((int)(pow(rcnt, rcnt) * sqrt(rcnt*7230970)) ^ 7230716) ^ (int)atan2(rcnt, rcnt)); \
+ }} \
+
+
+unsigned int calculate_timespace(long load);
+void start_benchmark(struct config *config);
diff --git a/bench/config.h b/bench/config.h
new file mode 100644
index 0000000..9690f1b
--- /dev/null
+++ b/bench/config.h
@@ -0,0 +1,36 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* initial loop count for the load calibration */
+#define GAUGECOUNT 1500
+
+/* default scheduling policy SCHED_OTHER */
+#define SCHEDULER SCHED_OTHER
+
+#define PRIORITY_DEFAULT 0
+#define PRIORITY_HIGH sched_get_priority_max(SCHEDULER)
+#define PRIORITY_LOW sched_get_priority_min(SCHEDULER)
+
+/* enable further debug messages */
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf( ... ) while(0) { }
+#endif
+
diff --git a/bench/example.cfg b/bench/example.cfg
new file mode 100644
index 0000000..09d2765
--- /dev/null
+++ b/bench/example.cfg
@@ -0,0 +1,11 @@
+sleep = 25000
+load = 25000
+cpu = 0
+priority = LOW
+output = /tmp
+sleep_step = 25000
+load_step = 25000
+cycles = 10
+rounds = 60
+#verbose = 0
+governor = ondemand
diff --git a/bench/main.c b/bench/main.c
new file mode 100644
index 0000000..1ff1525
--- /dev/null
+++ b/bench/main.c
@@ -0,0 +1,203 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include "config.h"
+#include "system.h"
+#include "benchmark.h"
+
+static struct option long_options[] =
+{
+ {"output", 1, 0, 'o'},
+ {"sleep", 1, 0, 's'},
+ {"load", 1, 0, 'l'},
+ {"verbose", 0, 0, 'v'},
+ {"cpu", 1, 0, 'c'},
+ {"governor", 1, 0, 'g'},
+ {"prio", 1, 0, 'p'},
+ {"file", 1, 0, 'f'},
+ {"cycles", 1, 0, 'n'},
+ {"rounds", 1, 0, 'r'},
+ {"load-step", 1, 0, 'x'},
+ {"sleep-step", 1, 0, 'y'},
+ {"help", 0, 0, 'h'},
+ {0, 0, 0, 0}
+};
+
+/*******************************************************************
+ usage
+*******************************************************************/
+
+void usage()
+{
+ printf("usage: ./bench\n");
+ printf("Options:\n");
+ printf(" -l, --load=<long int>\t\tinitial load time in us\n");
+ printf(" -s, --sleep=<long int>\t\tinitial sleep time in us\n");
+ printf(" -x, --load-step=<long int>\ttime to be added to load time, in us\n");
+ printf(" -y, --sleep-step=<long int>\ttime to be added to sleep time, in us\n");
+ printf(" -c, --cpu=<cpu #>\t\t\tCPU Nr. to use, starting at 0\n");
+ printf(" -p, --prio=<priority>\t\t\tscheduler priority, HIGH, LOW or DEFAULT\n");
+ printf(" -g, --governor=<governor>\t\tcpufreq governor to test\n");
+ printf(" -n, --cycles=<int>\t\t\tload/sleep cycles\n");
+ printf(" -r, --rounds<int>\t\t\tload/sleep rounds\n");
+ printf(" -f, --file=<configfile>\t\tconfig file to use\n");
+ printf(" -o, --output=<dir>\t\t\toutput path. Filename will be OUTPUTPATH/benchmark_TIMESTAMP.log\n");
+ printf(" -v, --verbose\t\t\t\tverbose output on/off\n");
+ printf(" -h, --help\t\t\t\tPrint this help screen\n");
+ exit (1);
+}
+
+/*******************************************************************
+ main
+*******************************************************************/
+
+int main(int argc, char **argv)
+{
+ int c;
+ int option_index = 0;
+ struct config *config = NULL;
+
+ if (argc == 1)
+ usage();
+
+ config = prepare_default_config();
+
+ if (config == NULL)
+ return EXIT_FAILURE;
+
+ while (1) {
+ c = getopt_long (argc, argv, "hg:o:s:l:vc:p:f:n:r:x:y:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'o':
+ if (config->output != NULL)
+ fclose(config->output);
+
+ config->output = prepare_output(optarg);
+
+ if (config->output == NULL)
+ return EXIT_FAILURE;
+
+ dprintf("user output path -> %s\n", optarg);
+ break;
+ case 's':
+ sscanf(optarg, "%li", &config->sleep);
+ dprintf("user sleep time -> %s\n", optarg);
+ break;
+ case 'l':
+ sscanf(optarg, "%li", &config->load);
+ dprintf("user load time -> %s\n", optarg);
+ break;
+ case 'c':
+ sscanf(optarg, "%u", &config->cpu);
+ dprintf("user cpu -> %s\n", optarg);
+ break;
+ case 'g':
+ strncpy(config->governor, optarg, 14);
+ dprintf("user governor -> %s\n", optarg);
+ break;
+ case 'p':
+ if (string_to_prio(optarg) != SCHED_ERR) {
+ config->prio = string_to_prio(optarg);
+ dprintf("user prio -> %s\n", optarg);
+ } else {
+ if (config != NULL) {
+ if (config->output != NULL)
+ fclose(config->output);
+ free(config);
+ }
+ usage();
+ }
+ break;
+ case 'n':
+ sscanf(optarg, "%u", &config->cycles);
+ dprintf("user cycles -> %s\n", optarg);
+ break;
+ case 'r':
+ sscanf(optarg, "%u", &config->rounds);
+ dprintf("user rounds -> %s\n", optarg);
+ break;
+ case 'x':
+ sscanf(optarg, "%li", &config->load_step);
+ dprintf("user load_step -> %s\n", optarg);
+ break;
+ case 'y':
+ sscanf(optarg, "%li", &config->sleep_step);
+ dprintf("user sleep_step -> %s\n", optarg);
+ break;
+ case 'f':
+ config = prepare_config(optarg);
+ if (config == NULL)
+ return EXIT_FAILURE;
+ break;
+ case 'v':
+ config->verbose = 1;
+ dprintf("verbose output enabled\n");
+ break;
+ case 'h':
+ case '?':
+ default:
+ if (config != NULL) {
+ if (config->output != NULL)
+ fclose(config->output);
+ free(config);
+ }
+ usage();
+ }
+ }
+
+ printf("starting benchmark with parameters:\n");
+ printf("config:\n\t"
+ "sleep=%li\n\t"
+ "load=%li\n\t"
+ "sleep_step=%li\n\t"
+ "load_step=%li\n\t"
+ "cpu=%u\n\t"
+ "cycles=%u\n\t"
+ "rounds=%u\n\t"
+ "governor=%s\n\n",
+ config->sleep,
+ config->load,
+ config->sleep_step,
+ config->load_step,
+ config->cpu,
+ config->cycles,
+ config->rounds,
+ config->governor);
+
+ prepare_user(config);
+ prepare_system(config);
+ start_benchmark(config);
+
+ fclose (config->output);
+ free (config);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/bench/parse.c b/bench/parse.c
new file mode 100644
index 0000000..2b26279
--- /dev/null
+++ b/bench/parse.c
@@ -0,0 +1,201 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <sys/utsname.h>
+
+#include "parse.h"
+#include "config.h"
+
+/**
+ * converts priority string to priority
+ *
+ * @param str string that represents a scheduler priority
+ *
+ * @retval priority
+ * @retval SCHED_ERR when the priority doesn't exit
+ **/
+
+enum sched_prio string_to_prio(const char *str)
+{
+ if (strncasecmp("high", str, strlen(str)) == 0)
+ return SCHED_HIGH;
+ else if (strncasecmp("default", str, strlen(str)) == 0)
+ return SCHED_DEFAULT;
+ else if (strncasecmp("low", str, strlen(str)) == 0)
+ return SCHED_LOW;
+ else
+ return SCHED_ERR;
+}
+
+/**
+ * create and open logfile
+ *
+ * @param dir directory in which the logfile should be created
+ *
+ * @retval logfile on success
+ * @retval NULL when the file can't be created
+ **/
+
+FILE *prepare_output(const char *dir)
+{
+ FILE *output = NULL;
+ int len;
+ char *filename;
+ struct utsname sysdata;
+
+ len = strlen(dir) + 30;
+ filename = malloc(sizeof(char) * len);
+
+ if (uname(&sysdata) == 0) {
+ len += strlen(sysdata.nodename) + strlen(sysdata.release);
+ filename = realloc(filename, sizeof(char) * len);
+
+ if(filename == NULL) {
+ perror("realloc");
+ return NULL;
+ }
+
+ snprintf(filename, len - 1, "%s/benchmark_%s_%s_%li.log",
+ dir, sysdata.nodename, sysdata.release, time(NULL));
+ } else {
+ snprintf(filename, len -1, "%s/benchmark_%li.log", dir, time(NULL));
+ }
+
+ dprintf("logilename: %s\n", filename);
+
+ if ((output = fopen(filename, "w+")) == NULL) {
+ perror("fopen");
+ fprintf(stderr, "error: unable to open logfile\n");
+ }
+
+ free(filename);
+ fprintf(output, "#load sleep avarage rounds performance powersave percentage\n");
+ return output;
+}
+
+/**
+ * returns the default config
+ *
+ * @retval default config on success
+ * @retval NULL when the output file can't be created
+ **/
+
+struct config *prepare_default_config()
+{
+ struct config *config = malloc(sizeof(struct config));
+
+ dprintf("loading defaults\n");
+
+ config->sleep = 500000;
+ config->load = 500000;
+ config->sleep_step = 500000;
+ config->load_step = 500000;
+ config->cycles = 5;
+ config->rounds = 50;
+ config->cpu = 0;
+ config->prio = SCHED_HIGH;
+ config->verbose = 0;
+ strncpy(config->governor, "ondemand", 8);
+
+ if ((config->output = prepare_output("/tmp")) == NULL) {
+ free(config);
+ return NULL;
+ }
+
+ return config;
+}
+
+/**
+ * parses config file and returns the config to the caller
+ *
+ * @param path config file name
+ *
+ * @retval parsed benchmark config
+ **/
+
+struct config *prepare_config(const char *path)
+{
+ size_t len = 0;
+ char *opt, *val, *line = NULL;
+ FILE *configfile = fopen(path, "r");
+ struct config *config = malloc(sizeof(struct config));
+
+ if (configfile == NULL) {
+ perror("fopen");
+ fprintf(stderr, "error: unable to read configfile\n");
+ free(config);
+ return NULL;
+ }
+
+ while (getline(&line, &len, configfile) != -1)
+ {
+ if (line[0] == '#' || line[0] == ' ')
+ continue;
+
+ sscanf(line, "%as = %as", &opt, &val);
+
+ dprintf("parsing: %s -> %s\n", opt, val);
+
+ if (strncmp("sleep", opt, strlen(opt)) == 0)
+ sscanf(val, "%li", &config->sleep);
+
+ else if (strncmp("load", opt, strlen(opt)) == 0)
+ sscanf(val, "%li", &config->load);
+
+ else if (strncmp("load_step", opt, strlen(opt)) == 0)
+ sscanf(val, "%li", &config->load_step);
+
+ else if (strncmp("sleep_step", opt, strlen(opt)) == 0)
+ sscanf(val, "%li", &config->sleep_step);
+
+ else if (strncmp("cycles", opt, strlen(opt)) == 0)
+ sscanf(val, "%u", &config->cycles);
+
+ else if (strncmp("rounds", opt, strlen(opt)) == 0)
+ sscanf(val, "%u", &config->rounds);
+
+ else if (strncmp("verbose", opt, strlen(opt)) == 0)
+ sscanf(val, "%u", &config->verbose);
+
+ else if (strncmp("output", opt, strlen(opt)) == 0)
+ config->output = prepare_output(val);
+
+ else if (strncmp("cpu", opt, strlen(opt)) == 0)
+ sscanf(val, "%u", &config->cpu);
+
+ else if (strncmp("governor", opt, 14) == 0)
+ strncpy(config->governor, val, 14);
+
+ else if (strncmp("priority", opt, strlen(opt)) == 0) {
+ if (string_to_prio(val) != SCHED_ERR)
+ config->prio = string_to_prio(val);
+ }
+ }
+
+ free(line);
+ free(opt);
+ free(val);
+
+ return config;
+}
diff --git a/bench/parse.h b/bench/parse.h
new file mode 100644
index 0000000..45ec3be
--- /dev/null
+++ b/bench/parse.h
@@ -0,0 +1,48 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* struct that holds the required config parameters */
+struct config
+{
+ long sleep; /* sleep time in µs */
+ long load; /* load time in µs */
+ long sleep_step; /* time value which changes the
+ * sleep time after every round in µs */
+ long load_step; /* time value which changes the
+ * load time after every round in µs */
+ unsigned int cycles; /* calculation cycles with the same sleep/load time */
+ unsigned int rounds; /* calculation rounds with iterated sleep/load time */
+ unsigned int cpu; /* cpu for which the affinity is set */
+ char governor[15]; /* cpufreq governor */
+ enum sched_prio /* possible scheduler priorities */
+ {
+ SCHED_ERR=-1,SCHED_HIGH, SCHED_DEFAULT, SCHED_LOW
+ } prio;
+
+ unsigned int verbose; /* verbose output */
+ FILE *output; /* logfile */
+};
+
+enum sched_prio string_to_prio(const char *str);
+
+FILE *prepare_output(const char *dir);
+
+struct config *prepare_config(const char *path);
+struct config *prepare_default_config();
+
diff --git a/bench/system.c b/bench/system.c
new file mode 100644
index 0000000..d089057
--- /dev/null
+++ b/bench/system.c
@@ -0,0 +1,195 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sched.h>
+
+#include <cpufreq.h>
+
+#include "config.h"
+#include "system.h"
+
+/**
+ * returns time since epoch in µs
+ *
+ * @retval time
+ **/
+
+long long int get_time()
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ return (long long int)(now.tv_sec * 1000000LL + now.tv_usec);
+}
+
+/**
+ * sets the cpufreq governor
+ *
+ * @param governor cpufreq governor name
+ * @param cpu cpu for which the governor should be set
+ *
+ * @retval 0 on success
+ * @retval -1 when failed
+ **/
+
+int set_cpufreq_governor(char *governor, unsigned int cpu)
+{
+
+ dprintf("set %s as cpufreq governor\n", governor);
+
+ if (cpufreq_cpu_exists(cpu) != 0) {
+ perror("cpufreq_cpu_exists");
+ fprintf(stderr, "error: cpu %u does not exist\n", cpu);
+ return -1;
+ }
+
+ if (cpufreq_modify_policy_governor(cpu, governor) != 0) {
+ perror("cpufreq_modify_policy_governor");
+ fprintf(stderr, "error: unable to set %s governor\n", governor);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * sets cpu affinity for the process
+ *
+ * @param cpu cpu# to which the affinity should be set
+ *
+ * @retval 0 on success
+ * @retval -1 when setting the affinity failed
+ **/
+
+int set_cpu_affinity(unsigned int cpu)
+{
+ cpu_set_t cpuset;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu, &cpuset);
+
+ dprintf("set affinity to cpu #%u\n", cpu);
+
+ if (sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset) < 0) {
+ perror("sched_setaffinity");
+ fprintf(stderr, "warning: unable to set cpu affinity\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * sets the process priority parameter
+ *
+ * @param priority priority value
+ *
+ * @retval 0 on success
+ * @retval -1 when setting the priority failed
+ **/
+
+int set_process_priority(int priority)
+{
+ struct sched_param param;
+
+ dprintf("set scheduler priority to %i\n", priority);
+
+ param.sched_priority = priority;
+
+ if (sched_setscheduler(0, SCHEDULER, &param) < 0) {
+ perror("sched_setscheduler");
+ fprintf(stderr, "warning: unable to set scheduler priority\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * notifys the user that the benchmark may run some time
+ *
+ * @param config benchmark config values
+ *
+ **/
+
+void prepare_user(const struct config *config)
+{
+ unsigned long sleep_time = 0;
+ unsigned long load_time = 0;
+ unsigned int round;
+ int i;
+
+ for (round = 0; round < config->rounds; round++) {
+ sleep_time += 2 * config->cycles * (config->sleep + config->sleep_step * round);
+ load_time += 2 * config->cycles * (config->load + config->load_step * round) + (config->load + config->load_step * round * 4);
+ }
+
+ printf("approx. test duration: %im\n", (int)((sleep_time + load_time) / 60000000));
+ printf("your terminal may hardly be responsible while the benchmark is running\n");
+
+ for (i = 5; i >= 0; i--) {
+ printf("\rbenchmark starts in %is", i);
+ fflush(stdout);
+ sleep(1);
+ }
+ printf("\n");
+}
+
+/**
+ * sets up the cpu affinity and scheduler priority
+ *
+ * @param config benchmark config values
+ *
+ **/
+
+void prepare_system(const struct config *config)
+{
+ if (config->verbose)
+ printf("set cpu affinity to cpu #%u\n", config->cpu);
+
+ set_cpu_affinity(config->cpu);
+
+ switch (config->prio) {
+ case SCHED_HIGH:
+ if (config->verbose)
+ printf("high priority condition requested\n");
+
+ set_process_priority(PRIORITY_HIGH);
+ break;
+ case SCHED_LOW:
+ if (config->verbose)
+ printf("low priority condition requested\n");
+
+ set_process_priority(PRIORITY_LOW);
+ break;
+ default:
+ if (config->verbose)
+ printf("default priority condition requested\n");
+
+ set_process_priority(PRIORITY_DEFAULT);
+ }
+}
+
diff --git a/bench/system.h b/bench/system.h
new file mode 100644
index 0000000..3a8c858
--- /dev/null
+++ b/bench/system.h
@@ -0,0 +1,29 @@
+/* cpufreq-bench CPUFreq microbenchmark
+ *
+ * Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "parse.h"
+
+long long get_time();
+
+int set_cpufreq_governor(char *governor, unsigned int cpu);
+int set_cpu_affinity(unsigned int cpu);
+int set_process_priority(int priority);
+
+void prepare_user(const struct config *config);
+void prepare_system(const struct config *config);