aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmarks/multi/harness.c109
-rw-r--r--scripts/bench.py32
-rw-r--r--scripts/libplot.py16
-rw-r--r--scripts/plot-align.py10
-rw-r--r--scripts/plot-sizes.py22
-rw-r--r--scripts/plot-top.py2
-rw-r--r--scripts/plot.py2
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