From 827f3b4974c5db2968d4979fe6a0ae00ab37bdd8 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Wed, 18 Nov 2009 00:20:09 +0900 Subject: perf bench: Add memcpy() benchmark 'perf bench mem memcpy' is a benchmark suite for measuring memcpy() performance. Example on a Intel(R) Core(TM)2 Duo CPU E6850 @ 3.00GHz: | % perf bench mem memcpy -l 1GB | # Running mem/memcpy benchmark... | # Copying 1MB Bytes from 0xb7d98008 to 0xb7e99008 ... | | 726.216412 MB/Sec Signed-off-by: Hitoshi Mitake Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Frederic Weisbecker LKML-Reference: <1258471212-30281-1-git-send-email-mitake@dcl.info.waseda.ac.jp> [ v2: updated changelog, clarified history of builtin-bench.c ] Signed-off-by: Ingo Molnar --- tools/perf/Makefile | 1 + tools/perf/bench/bench.h | 1 + tools/perf/bench/mem-memcpy.c | 186 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/builtin-bench.c | 15 +++- 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 tools/perf/bench/mem-memcpy.c (limited to 'tools/perf') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3f0666a..53e663a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -432,6 +432,7 @@ BUILTIN_OBJS += builtin-bench.o # Benchmark modules BUILTIN_OBJS += bench/sched-messaging.o BUILTIN_OBJS += bench/sched-pipe.o +BUILTIN_OBJS += bench/mem-memcpy.o BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-sched.o diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index 9fbd8d7..f7781c6 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -3,6 +3,7 @@ extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); +extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used); #define BENCH_FORMAT_DEFAULT_STR "default" #define BENCH_FORMAT_DEFAULT 0 diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c new file mode 100644 index 0000000..d4f4f98 --- /dev/null +++ b/tools/perf/bench/mem-memcpy.c @@ -0,0 +1,186 @@ +/* + * mem-memcpy.c + * + * memcpy: Simple memory copy in various ways + * + * Written by Hitoshi Mitake + */ +#include + +#include "../perf.h" +#include "../util/util.h" +#include "../util/parse-options.h" +#include "../util/string.h" +#include "../util/header.h" +#include "bench.h" + +#include +#include +#include +#include +#include + +#define K 1024 + +static const char *length_str = "1MB"; +static const char *routine = "default"; +static int use_clock = 0; + +static const struct option options[] = { + OPT_STRING('l', "length", &length_str, "1MB", + "Specify length of memory to copy. " + "available unit: B, MB, GB (upper and lower)"), + OPT_STRING('r', "routine", &routine, "default", + "Specify routine to copy"), + OPT_BOOLEAN('c', "clock", &use_clock, + "Use CPU clock for measuring"), + OPT_END() +}; + +struct routine { + const char *name; + const char *desc; + void * (*fn)(void *dst, const void *src, size_t len); +}; + +struct routine routines[] = { + { "default", + "Default memcpy() provided by glibc", + memcpy }, + { NULL, + NULL, + NULL } +}; + +static const char * const bench_mem_memcpy_usage[] = { + "perf bench mem memcpy ", + NULL +}; + +static int clock_fd; + +static struct perf_event_attr clock_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES +}; + +static void init_clock(void) +{ + clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); + BUG_ON(clock_fd < 0); +} + +static u64 get_clock(void) +{ + int ret; + u64 clk; + + ret = read(clock_fd, &clk, sizeof(u64)); + BUG_ON(ret != sizeof(u64)); + + return clk; +} + +static double timeval2double(struct timeval *ts) +{ + return (double)ts->tv_sec + + (double)ts->tv_usec / (double)1000000; +} + +int bench_mem_memcpy(int argc, const char **argv, + const char *prefix __used) +{ + int i; + void *dst, *src; + size_t length; + double bps = 0.0; + struct timeval tv_start, tv_end, tv_diff; + u64 clock_start, clock_end, clock_diff; + + clock_start = clock_end = clock_diff = 0ULL; + argc = parse_options(argc, argv, options, + bench_mem_memcpy_usage, 0); + + tv_diff.tv_sec = 0; + tv_diff.tv_usec = 0; + length = (size_t)perf_atoll((char *)length_str); + if ((long long int)length <= 0) { + fprintf(stderr, "Invalid length:%s\n", length_str); + return 1; + } + + for (i = 0; routines[i].name; i++) { + if (!strcmp(routines[i].name, routine)) + break; + } + if (!routines[i].name) { + printf("Unknown routine:%s\n", routine); + printf("Available routines...\n"); + for (i = 0; routines[i].name; i++) { + printf("\t%s ... %s\n", + routines[i].name, routines[i].desc); + } + return 1; + } + + dst = calloc(length, sizeof(char)); + assert(dst); + src = calloc(length, sizeof(char)); + assert(src); + + if (bench_format == BENCH_FORMAT_DEFAULT) { + printf("# Copying %s Bytes from %p to %p ...\n\n", + length_str, src, dst); + } + + if (use_clock) { + init_clock(); + clock_start = get_clock(); + } else + BUG_ON(gettimeofday(&tv_start, NULL)); + + routines[i].fn(dst, src, length); + + if (use_clock) { + clock_end = get_clock(); + clock_diff = clock_end - clock_start; + } else { + BUG_ON(gettimeofday(&tv_end, NULL)); + timersub(&tv_end, &tv_start, &tv_diff); + bps = (double)((double)length / timeval2double(&tv_diff)); + } + + switch (bench_format) { + case BENCH_FORMAT_DEFAULT: + if (use_clock) { + printf(" %14lf Clock/Byte\n", + (double)clock_diff / (double)length); + } else { + if (bps < K) + printf(" %14lf B/Sec\n", bps); + else if (bps < K * K) + printf(" %14lfd KB/Sec\n", bps / 1024); + else if (bps < K * K * K) + printf(" %14lf MB/Sec\n", bps / 1024 / 1024); + else { + printf(" %14lf GB/Sec\n", + bps / 1024 / 1024 / 1024); + } + } + break; + case BENCH_FORMAT_SIMPLE: + if (use_clock) { + printf("%14lf\n", + (double)clock_diff / (double)length); + } else + printf("%lf\n", bps); + break; + default: + /* reaching here is something disaster */ + fprintf(stderr, "Unknown format:%d\n", bench_format); + exit(1); + break; + } + + return 0; +} diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 90c39ba..e043eb8 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -12,6 +12,7 @@ * * Available subsystem list: * sched ... scheduler and IPC mechanism + * mem ... memory access performance * */ @@ -43,6 +44,15 @@ static struct bench_suite sched_suites[] = { NULL } }; +static struct bench_suite mem_suites[] = { + { "memcpy", + "Simple memory copy in various ways", + bench_mem_memcpy }, + { NULL, + NULL, + NULL } +}; + struct bench_subsys { const char *name; const char *summary; @@ -53,9 +63,12 @@ static struct bench_subsys subsystems[] = { { "sched", "scheduler and IPC mechanism", sched_suites }, + { "mem", + "memory access performance", + mem_suites }, { NULL, NULL, - NULL } + NULL } }; static void dump_suites(int subsys_index) -- cgit v1.1 From 12eac0bf0461910ae6dd7f071f156f75461a37cf Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Fri, 20 Nov 2009 12:37:17 +0900 Subject: perf bench: Make the mem/memcpy tests more user-friendly mem-memcpy.c uses perf event system calls to obtain CPU clocks. And it suddenly dies with BUG_ON() when it running on Linux doesn't support perf event. Also fail at calloc() can occur easily when too large length is passed. Fail of calloc() causes sudden death with assert(). These behaviours are not friendly. So I fixed the treating of errors. Signed-off-by: Hitoshi Mitake Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Frederic Weisbecker LKML-Reference: <1258688237-3797-1-git-send-email-mitake@dcl.info.waseda.ac.jp> [ v2: improved a few small details ] Signed-off-by: Ingo Molnar --- tools/perf/bench/mem-memcpy.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'tools/perf') diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index d4f4f98..5165fd1 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -22,9 +22,10 @@ #define K 1024 -static const char *length_str = "1MB"; -static const char *routine = "default"; -static int use_clock = 0; +static const char *length_str = "1MB"; +static const char *routine = "default"; +static int use_clock = 0; +static int clock_fd; static const struct option options[] = { OPT_STRING('l', "length", &length_str, "1MB", @@ -57,17 +58,19 @@ static const char * const bench_mem_memcpy_usage[] = { NULL }; -static int clock_fd; - static struct perf_event_attr clock_attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES }; static void init_clock(void) { clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); - BUG_ON(clock_fd < 0); + + if (clock_fd < 0 && errno == ENOSYS) + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + else + BUG_ON(clock_fd < 0); } static u64 get_clock(void) @@ -104,7 +107,8 @@ int bench_mem_memcpy(int argc, const char **argv, tv_diff.tv_sec = 0; tv_diff.tv_usec = 0; length = (size_t)perf_atoll((char *)length_str); - if ((long long int)length <= 0) { + + if ((s64)length <= 0) { fprintf(stderr, "Invalid length:%s\n", length_str); return 1; } @@ -124,9 +128,12 @@ int bench_mem_memcpy(int argc, const char **argv, } dst = calloc(length, sizeof(char)); - assert(dst); + if (!dst) + die("memory allocation failed - maybe length is too large?\n"); + src = calloc(length, sizeof(char)); - assert(src); + if (!src) + die("memory allocation failed - maybe length is too large?\n"); if (bench_format == BENCH_FORMAT_DEFAULT) { printf("# Copying %s Bytes from %p to %p ...\n\n", @@ -136,8 +143,9 @@ int bench_mem_memcpy(int argc, const char **argv, if (use_clock) { init_clock(); clock_start = get_clock(); - } else + } else { BUG_ON(gettimeofday(&tv_start, NULL)); + } routines[i].fn(dst, src, length); @@ -176,9 +184,8 @@ int bench_mem_memcpy(int argc, const char **argv, printf("%lf\n", bps); break; default: - /* reaching here is something disaster */ - fprintf(stderr, "Unknown format:%d\n", bench_format); - exit(1); + /* reaching this means there's some disaster: */ + die("unknown format: %d\n", bench_format); break; } -- cgit v1.1