diff options
-rw-r--r-- | benchmarks/multi/harness.c | 109 | ||||
-rw-r--r-- | scripts/bench.py | 32 | ||||
-rw-r--r-- | scripts/libplot.py | 16 | ||||
-rw-r--r-- | scripts/plot-align.py | 10 | ||||
-rw-r--r-- | scripts/plot-sizes.py | 22 | ||||
-rw-r--r-- | scripts/plot-top.py | 2 | ||||
-rw-r--r-- | scripts/plot.py | 2 |
7 files changed, 140 insertions, 53 deletions
diff --git a/benchmarks/multi/harness.c b/benchmarks/multi/harness.c index 6fb3aa2..7987bce 100644 --- a/benchmarks/multi/harness.c +++ b/benchmarks/multi/harness.c @@ -39,7 +39,7 @@ #include <stdbool.h> #include <assert.h> #include <unistd.h> -#include <assert.h> +#include <errno.h> #define NUM_ELEMS(_x) (sizeof(_x) / sizeof((_x)[0])) @@ -153,7 +153,7 @@ static const struct test tests[] = static void usage(const char* name) { printf("%s %s: run a string related benchmark.\n" - "usage: %s [-c block-size] [-l loop-count] [-a alignment] [-f] [-t test-name]\n" + "usage: %s [-c block-size] [-l loop-count] [-a alignment|src_alignment:dst_alignment] [-f] [-t test-name]\n" , name, VERSION, name); printf("Tests:"); @@ -189,22 +189,81 @@ static const struct test *find_test(const char *name) return NULL; } +#define MIN_BUFFER_SIZE 1024*1024 +#define MAX_ALIGNMENT 256 + /** Take a pointer and ensure that the lower bits == alignment */ static char *realign(char *p, int alignment) { - if (alignment < 0) - { - return p; - } - uintptr_t pp = (uintptr_t)p; - pp = (pp + 255) & ~255; + pp = (pp + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1); pp += alignment; return (char *)pp; } -#define MIN_BUFFER_SIZE 1024*1024 +static int parse_int_arg(const char *arg, const char *exe_name) +{ + long int ret; + + errno = 0; + ret = strtol(arg, NULL, 0); + + if (errno) + { + usage(exe_name); + } + + return (int)ret; +} + +static void parse_alignment_arg(const char *arg, const char *exe_name, + int *src_alignment, int *dst_alignment) +{ + long int ret; + char *endptr; + + errno = 0; + ret = strtol(arg, &endptr, 0); + + if (errno) + { + usage(exe_name); + } + + *src_alignment = (int)ret; + + if (ret > 256 || ret < 1) + { + printf("Alignment should be in the range [1, 256].\n"); + usage(exe_name); + } + + if (ret == 256) + ret = 0; + + if (endptr && *endptr == ':') + { + errno = 0; + ret = strtol(endptr + 1, NULL, 0); + + if (errno) + { + usage(exe_name); + } + + if (ret > 256 || ret < 1) + { + printf("Alignment should be in the range [1, 256].\n"); + usage(exe_name); + } + + if (ret == 256) + ret = 0; + } + + *dst_alignment = (int)ret; +} /** Setup and run a test */ int main(int argc, char **argv) @@ -220,8 +279,9 @@ int main(int argc, char **argv) int flush = 0; /* Name of the test */ const char *name = NULL; - /* Alignment of both buffers */ - int alignment = 8; + /* Alignment of buffers */ + int src_alignment = 8; + int dst_alignment = 8; int opt; @@ -230,13 +290,13 @@ int main(int argc, char **argv) switch (opt) { case 'c': - count = atoi(optarg); + count = parse_int_arg(optarg, argv[0]); break; case 'l': - loops = atoi(optarg); + loops = parse_int_arg(optarg, argv[0]); break; case 'a': - alignment = atoi(optarg); + parse_alignment_arg(optarg, argv[0], &src_alignment, &dst_alignment); break; case 'f': flush = 1; @@ -261,18 +321,9 @@ int main(int argc, char **argv) usage(argv[0]); } - if (alignment > 256 || alignment < 1) - { - printf("Alignment should be in the range [1, 256].\n"); - usage(argv[0]); - } - - if (alignment == 256) - alignment = 0; - - if (count + alignment + 256 > MIN_BUFFER_SIZE) + if (count + MAX_ALIGNMENT * 2 > MIN_BUFFER_SIZE) { - buffer_size = count + alignment + 256; + buffer_size = count + MAX_ALIGNMENT * 2; } /* Buffers to read and write from */ @@ -281,8 +332,8 @@ int main(int argc, char **argv) assert(src != NULL && dest != NULL); - src = realign(src, alignment); - dest = realign(dest, alignment); + src = realign(src, src_alignment); + dest = realign(dest, dst_alignment); /* Fill the buffer with non-zero, reproducable random data */ srandom(1539); @@ -340,9 +391,9 @@ int main(int argc, char **argv) double bounced = 0.448730 * loops / 50000000; /* Dump both machine and human readable versions */ - printf("%s:%s:%u:%u:%d:%.6f: took %.6f s for %u calls to %s of %u bytes. ~%.3f MB/s corrected.\n", + printf("%s:%s:%u:%u:%d:%d:%.6f: took %.6f s for %u calls to %s of %u bytes. ~%.3f MB/s corrected.\n", variant + 4, ptest->name, - count, loops, alignment, + count, loops, src_alignment, dst_alignment, elapsed, elapsed, loops, ptest->name, count, (double)loops*count/(elapsed - bounced)/(1024*1024)); diff --git a/scripts/bench.py b/scripts/bench.py index ac2233c..dd96778 100644 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -33,7 +33,23 @@ HAS = { 'plain': 'memset memcpy strcmp strcpy', } -def run(cache, variant, function, bytes, loops, alignment=8, quiet=False): +BOUNCE_ALIGNMENTS = ['1'] +SINGLE_BUFFER_ALIGNMENTS = ['1', '2', '4', '8', '16', '32'] +DUAL_BUFFER_ALIGNMENTS = ['1:32', '2:32', '4:32', '8:32', '16:32', '32:32'] + +ALIGNMENTS = { + 'bounce': BOUNCE_ALIGNMENTS, + 'memchr': SINGLE_BUFFER_ALIGNMENTS, + 'memset': SINGLE_BUFFER_ALIGNMENTS, + 'strchr': SINGLE_BUFFER_ALIGNMENTS, + 'strlen': SINGLE_BUFFER_ALIGNMENTS, + 'memcmp': DUAL_BUFFER_ALIGNMENTS, + 'memcpy': DUAL_BUFFER_ALIGNMENTS, + 'strcmp': DUAL_BUFFER_ALIGNMENTS, + 'strcpy': DUAL_BUFFER_ALIGNMENTS, +} + +def run(cache, variant, function, bytes, loops, alignment, quiet=False): """Perform a single run, exercising the cache as appropriate.""" key = ':'.join('%s' % x for x in (variant, function, bytes, loops, alignment)) @@ -49,7 +65,7 @@ def run(cache, variant, function, bytes, loops, alignment=8, quiet=False): assert False, 'Error %s while running %s' % (ex, cmd) parts = got.split(':') - took = float(parts[5]) + took = float(parts[6]) cache[key] = got @@ -59,11 +75,11 @@ def run(cache, variant, function, bytes, loops, alignment=8, quiet=False): return took -def run_many(cache, variants, bytes, alignments, all_functions): +def run_many(cache, variants, bytes, all_functions): # We want the data to come out in a useful order. So fix an # alignment and function, and do all sizes for a variant first bytes = sorted(bytes) - mid = bytes[len(bytes)/2] + mid = bytes[int(len(bytes)/1.5)] if not all_functions: # Use the ordering in 'this' as the default @@ -75,8 +91,8 @@ def run_many(cache, variants, bytes, alignments, all_functions): if function not in all_functions: all_functions.append(function) - for alignment in alignments: - for function in all_functions: + for function in all_functions: + for alignment in ALIGNMENTS[function]: for variant in variants: if function not in HAS[variant].split(): continue @@ -122,9 +138,7 @@ def run_top(cache): steps = int(round(math.log(top) / math.log(step))) bytes.extend([int(step**x) for x in range(0, steps+1)]) - alignments = [8, 16, 4, 1, 2, 32] - - run_many(cache, variants, bytes, alignments, functions) + run_many(cache, variants, bytes, functions) def main(): cachename = 'cache.txt' diff --git a/scripts/libplot.py b/scripts/libplot.py index 4c0760d..ccd4fca 100644 --- a/scripts/libplot.py +++ b/scripts/libplot.py @@ -3,7 +3,7 @@ import fileinput import collections -Record = collections.namedtuple('Record', 'variant function bytes loops alignment elapsed rest') +Record = collections.namedtuple('Record', 'variant function bytes loops src_alignment dst_alignment elapsed rest') def make_colours(): @@ -19,10 +19,16 @@ def parse_value(v): except ValueError: return v +def create_column_tuple(record, names): + cols = [getattr(record, name) for name in names] + return tuple(cols) def unique(records, name, prefer=''): """Return the unique values of a column in the records""" - values = list(set(getattr(x, name) for x in records)) + if type(name) == tuple: + values = list(set(create_column_tuple(x, name) for x in records)) + else: + values = list(set(getattr(x, name) for x in records)) if not values: return values @@ -31,6 +37,12 @@ def unique(records, name, prefer=''): else: return sorted(values) +def alignments_equal(alignments): + for alignment in alignments: + if alignment[0] != alignment[1]: + return False + return True + def parse_row(line): return Record(*[parse_value(y) for y in line.split(':')]) diff --git a/scripts/plot-align.py b/scripts/plot-align.py index 38affcb..e66ace6 100644 --- a/scripts/plot-align.py +++ b/scripts/plot-align.py @@ -12,7 +12,7 @@ def plot(records, bytes, function): records = [x for x in records if x.bytes==bytes and x.function==function] variants = libplot.unique(records, 'variant', prefer='this') - alignments = libplot.unique(records, 'alignment') + alignments = libplot.unique(records, ('src_alignment', 'dst_alignment')) X = pylab.arange(len(alignments)) width = 1.0/(len(variants)+1) @@ -26,7 +26,7 @@ def plot(records, bytes, function): heights = [] for alignment in alignments: - matches = [x for x in records if x.variant==variant and x.alignment==alignment] + matches = [x for x in records if x.variant==variant and x.src_alignment==alignment[0] and x.dst_alignment==alignment[1]] if matches: match = matches[0] @@ -38,7 +38,11 @@ def plot(records, bytes, function): axes = pylab.axes() - axes.set_xticklabels(alignments) + if libplot.alignments_equal(alignments): + alignment_labels = ["%s" % x[0] for x in alignments] + else: + alignment_labels = ["%s:%s" % (x[0], x[1]) for x in alignments] + axes.set_xticklabels(alignment_labels) axes.set_xticks(X + 0.5) pylab.title('Performance of different variants of %(function)s for %(bytes)d byte blocks' % locals()) diff --git a/scripts/plot-sizes.py b/scripts/plot-sizes.py index 2b6023b..82fa65f 100644 --- a/scripts/plot-sizes.py +++ b/scripts/plot-sizes.py @@ -24,11 +24,16 @@ def plot(records, function, alignment=None, scale=1): records = [x for x in records if x.function==function] if alignment != None: - records = [x for x in records if x.alignment==alignment] - - alignments = libplot.unique(records, 'alignment') - assert len(alignments) == 1 - aalignment = alignments[0] + records = [x for x in records if x.src_alignment==alignment[0] and + x.dst_alignment==alignment[1]] + + alignments = libplot.unique(records, ('src_alignment', 'dst_alignment')) + if len(alignments) != 1: + return False + if libplot.alignments_equal(alignments): + aalignment = alignments[0][0] + else: + aalignment = "%s:%s" % (alignments[0][0], alignments[0][1]) bytes = libplot.unique(records, 'bytes')[0] @@ -76,18 +81,19 @@ def plot(records, function, alignment=None, scale=1): pylab.axes().set_xticklabels([pretty_kb(2**x) for x in range(0, power+1)]) pylab.xlim(0, top) pylab.ylim(0, pylab.ylim()[1]) + return True def main(): records = libplot.parse() functions = libplot.unique(records, 'function') - alignments = libplot.unique(records, 'alignment') + alignments = libplot.unique(records, ('src_alignment', 'dst_alignment')) for function in functions: for alignment in alignments: for scale in [1, 2.5]: - plot(records, function, alignment, scale) - pylab.savefig('sizes-%s-%02d-%.1f.png' % (function, alignment, scale), dpi=72) + if plot(records, function, alignment, scale): + pylab.savefig('sizes-%s-%02d-%02d-%.1f.png' % (function, alignment[0], alignment[1], scale), dpi=72) pylab.show() diff --git a/scripts/plot-top.py b/scripts/plot-top.py index a138b7e..9de6e7f 100644 --- a/scripts/plot-top.py +++ b/scripts/plot-top.py @@ -27,7 +27,7 @@ def plot(records, bytes): heights = [] for function in functions: - matches = [x for x in records if x.variant==variant and x.function==function and x.alignment==8] + matches = [x for x in records if x.variant==variant and x.function==function and x.src_alignment==8] if matches: match = matches[0] diff --git a/scripts/plot.py b/scripts/plot.py index 8a7385d..c7b0650 100644 --- a/scripts/plot.py +++ b/scripts/plot.py @@ -8,7 +8,7 @@ import pprint import pylab -Record = collections.namedtuple('Record', 'variant test size loops alignment rawtime comment time bytes rate') +Record = collections.namedtuple('Record', 'variant test size loops src_alignment dst_alignment rawtime comment time bytes rate') def unique(rows, name): """Takes a list of values, pulls out the named field, and returns |