From 9b975c48726c0a9ac19c3c9b0e701970b3dab8ea Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Mon, 25 Apr 2016 00:59:01 +0200 Subject: [PATCH hurd 3/3] fstests: new micro benchmark * fstests/Makefile: Build 'benchmark'. * fstests/benchmark.c: New file. --- fstests/Makefile | 6 +- fstests/benchmark.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 fstests/benchmark.c diff --git a/fstests/Makefile b/fstests/Makefile index 6374242..8058247 100644 --- a/fstests/Makefile +++ b/fstests/Makefile @@ -19,7 +19,9 @@ dir := fstests makemode := utilities SRCS = fstests.c fdtests.c timertest.c opendisk.c -targets = timertest fstests # opendisk fdtests +targets = timertest fstests benchmark # opendisk fdtests +HURDLIBS = shouldbeinlibc +benchmark-LDLIBS = -lpthread include ../Makeconf @@ -27,3 +29,5 @@ timertest: timertest.o fstests: fstests.o opendisk: opendisk.o fdtests: fdtests.o +benchmark: benchmark.o \ + ../libshouldbeinlibc/libshouldbeinlibc.a diff --git a/fstests/benchmark.c b/fstests/benchmark.c new file mode 100644 index 0000000..a4c501e --- /dev/null +++ b/fstests/benchmark.c @@ -0,0 +1,208 @@ +/* Performance testing. + + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *target_path = NULL; + +size_t iterations = 1l<<18; +size_t threads = 2; + +const char *argp_program_version = STANDARD_HURD_VERSION (benchmark); + +static const struct argp_option options[] = +{ + {"iterations", 'n', "N", 0, "Repeat tests N times"}, + {"threads", 't', "N", 0, "Run N threads"}, + {} +}; + +/* Parse a command line option. */ +error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + char *arg_end; + unsigned long *ptr = NULL; + + switch (key) + { + case 't': + ptr = ptr ?: &threads; + case 'n': + ptr = ptr ?: &iterations; + + *ptr = strtoul (arg, &arg_end, 10); + if (*arg == '\0' || *arg_end != '\0') + argp_error (state, "invalid integer: %s", arg); + break; + + case ARGP_KEY_ARG: + target_path = strdup (arg); + if (! target_path) + argp_error (state, "Error while canonicalizing path"); + break; + + case ARGP_KEY_NO_ARGS: + argp_usage (state); + return EINVAL; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = + { + options, + parse_opt, + "TARGET\tFile name of a node with an active translator", + "A translator providing mtab compatible information about active " + "and passive translators below TARGET.", + }; + + + +volatile struct mapped_time_value *mtime; + +struct worker_cookie +{ + pthread_t thread; + error_t (*dost) (void *); + error_t err; + size_t count; + struct timeval runtime; +}; + +void * +worker_thread (void *cookie) +{ + error_t err = 0; + struct worker_cookie *args = cookie; + struct timeval start_time; + struct timeval end_time; + size_t count; + + maptime_read (mtime, &start_time); + + for (count = args->count; count; count--) + { + err = args->dost (cookie); + if (err) + break; + } + + maptime_read (mtime, &end_time); + timersub (&end_time, &start_time, &args->runtime); + + args->count = count; + args->err = err; + return NULL; +} + +void +print_stats (const char *id, size_t nt, size_t n, struct timeval *tv) +{ + double duration = + (tv->tv_sec + (tv->tv_usec / 1000000.)) / (double) nt; + fprintf (stderr, + "% 10s: %.2fs\t%fns\t%9.3f (1/s)\n", + id, + duration, + 1000000000. * duration / n, + n / duration); +} + + + +static error_t +do_dir_lookup (void *cookie) +{ + file_t f; + f = file_name_lookup (target_path, 0, 0); + if (! MACH_PORT_VALID (f)) + return errno; + mach_port_deallocate (mach_task_self (), f); + return 0; +} + + + +int +main (int argc, char *argv[]) +{ + error_t err; + size_t i; + struct worker_cookie *cookies; + struct timeval accumulated_time = {0}; + + err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); + if (err) + error (1, err, "argument parsing"); + + fprintf (stderr, "N: %u (1<<%d)\n", + iterations, __builtin_ffs (iterations) - 1); + + err = maptime_map (0, NULL, &mtime); + if (err) + error (1, err, "maptime_map"); + + cookies = calloc (threads, sizeof *cookies); + if (cookies == NULL) + error (1, errno, "calloc"); + + for (i = 0; i < threads; i++) + { + cookies[i].count = iterations / threads; + cookies[i].dost = do_dir_lookup; + err = pthread_create (&cookies[i].thread, NULL, + &worker_thread, &cookies[i]); + if (err) + error (2, err, "pthread_create"); + } + + for (i = 0; i < threads; i++) + { + err = pthread_join (cookies[i].thread, NULL); + if (err) + error (3, err, "pthread_join"); + + if (cookies[i].err) + error (3, err, "thread %d", i); + + timeradd (&accumulated_time, &cookies[i].runtime, &accumulated_time); + } + + print_stats ("dir_lookup", threads, iterations, &accumulated_time); + + return 0; +} -- 2.1.4