aboutsummaryrefslogtreecommitdiff
path: root/contrib/cortex-strings/scripts
diff options
context:
space:
mode:
authorAndrew Turner <andrew@FreeBSD.org>2016-09-19 13:12:09 +0000
committerAndrew Turner <andrew@FreeBSD.org>2016-09-19 13:12:09 +0000
commit09a53ad8f1318c5daae6cfb19d97f4f6459f0013 (patch)
tree2db2658fb2b0e98f2e5771acfa810aead6052d20 /contrib/cortex-strings/scripts
parentbddfc749fafad1d8ccd1f2b612da2a527fe86c1f (diff)
parent5a194ab47811dee4fd1bd7c1fe163865fb468ae1 (diff)
downloadsrc-09a53ad8f1318c5daae6cfb19d97f4f6459f0013.tar.gz
src-09a53ad8f1318c5daae6cfb19d97f4f6459f0013.zip
Notes
Diffstat (limited to 'contrib/cortex-strings/scripts')
-rwxr-xr-xcontrib/cortex-strings/scripts/add-license.sh79
-rw-r--r--contrib/cortex-strings/scripts/bench.py175
-rw-r--r--contrib/cortex-strings/scripts/fixup.py27
-rw-r--r--contrib/cortex-strings/scripts/libplot.py78
-rw-r--r--contrib/cortex-strings/scripts/plot-align.py67
-rw-r--r--contrib/cortex-strings/scripts/plot-sizes.py120
-rw-r--r--contrib/cortex-strings/scripts/plot-top.py61
-rw-r--r--contrib/cortex-strings/scripts/plot.py123
-rwxr-xr-xcontrib/cortex-strings/scripts/trim.sh9
9 files changed, 739 insertions, 0 deletions
diff --git a/contrib/cortex-strings/scripts/add-license.sh b/contrib/cortex-strings/scripts/add-license.sh
new file mode 100755
index 000000000000..8a6c0710fbbe
--- /dev/null
+++ b/contrib/cortex-strings/scripts/add-license.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# Add the modified BSD license to a file
+#
+
+f=`mktemp -d`
+trap "rm -rf $f" EXIT
+
+year=`date +%Y`
+cat > $f/original <<EOF
+Copyright (c) $year, Linaro Limited
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Linaro nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+EOF
+
+# Translate it to C style
+echo "/*" > $f/c
+sed -r 's/(.*)/ * \1/' $f/original | sed -r 's/ +$//' >> $f/c
+echo " */" >> $f/c
+echo >> $f/c
+
+# ...and shell style
+sed -r 's/(.*)/# \1/' $f/original | sed -r 's/ +$//' >> $f/shell
+echo '#' >> $f/shell
+echo >> $f/shell
+
+for name in $@; do
+ if grep -q Copyright $name; then
+ echo $name already has some type of copyright
+ continue
+ fi
+
+ case $name in
+ # These files don't have an explicit license
+ *autogen.sh*)
+ continue;;
+ *reference/newlib/*)
+ continue;;
+ *reference/newlib-xscale/*)
+ continue;;
+ */dhry/*)
+ continue;;
+
+ *.c)
+ src=$f/c
+ ;;
+ *.sh|*.am|*.ac)
+ src=$f/shell
+ ;;
+ *)
+ echo Unrecognied extension on $name
+ continue
+ esac
+
+ cat $src $name > $f/next
+ mv $f/next $name
+ echo Updated $name
+done
diff --git a/contrib/cortex-strings/scripts/bench.py b/contrib/cortex-strings/scripts/bench.py
new file mode 100644
index 000000000000..476a5322a747
--- /dev/null
+++ b/contrib/cortex-strings/scripts/bench.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+
+"""Simple harness that benchmarks different variants of the routines,
+caches the results, and emits all of the records at the end.
+
+Results are generated for different values of:
+ * Source
+ * Routine
+ * Length
+ * Alignment
+"""
+
+import argparse
+import subprocess
+import math
+import sys
+
+# Prefix to the executables
+build = '../build/try-'
+
+ALL = 'memchr memcmp memcpy memset strchr strcmp strcpy strlen'
+
+HAS = {
+ 'this': 'bounce memchr memcpy memset strchr strcmp strcpy strlen',
+ 'bionic-a9': 'memcmp memcpy memset strcmp strcpy strlen',
+ 'bionic-a15': 'memcmp memcpy memset strcmp strcpy strlen',
+ 'bionic-c': ALL,
+ 'csl': 'memcpy memset',
+ 'glibc': 'memcpy memset strchr strlen',
+ 'glibc-c': ALL,
+ 'newlib': 'memcpy strcmp strcpy strlen',
+ 'newlib-c': ALL,
+ 'newlib-xscale': 'memchr memcpy memset strchr strcmp strcpy strlen',
+ 'plain': 'memset memcpy strcmp strcpy',
+}
+
+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,
+}
+
+VARIANTS = sorted(HAS.keys())
+FUNCTIONS = sorted(ALIGNMENTS.keys())
+
+NUM_RUNS = 5
+
+def run(cache, variant, function, bytes, loops, alignment, run_id, quiet=False):
+ """Perform a single run, exercising the cache as appropriate."""
+ key = ':'.join('%s' % x for x in (variant, function, bytes, loops, alignment, run_id))
+
+ if key in cache:
+ got = cache[key]
+ else:
+ xbuild = build
+ cmd = '%(xbuild)s%(variant)s -t %(function)s -c %(bytes)s -l %(loops)s -a %(alignment)s -r %(run_id)s' % locals()
+
+ try:
+ got = subprocess.check_output(cmd.split()).strip()
+ except OSError, ex:
+ assert False, 'Error %s while running %s' % (ex, cmd)
+
+ parts = got.split(':')
+ took = float(parts[7])
+
+ cache[key] = got
+
+ if not quiet:
+ print got
+ sys.stdout.flush()
+
+ return took
+
+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[int(len(bytes)/1.5)]
+
+ if not all_functions:
+ # Use the ordering in 'this' as the default
+ all_functions = HAS['this'].split()
+
+ # Find all other functions
+ for functions in HAS.values():
+ for function in functions.split():
+ if function not in all_functions:
+ all_functions.append(function)
+
+ for function in all_functions:
+ for alignment in ALIGNMENTS[function]:
+ for variant in variants:
+ if function not in HAS[variant].split():
+ continue
+
+ # Run a tracer through and see how long it takes and
+ # adjust the number of loops based on that. Not great
+ # for memchr() and similar which are O(n), but it will
+ # do
+ f = 50000000
+ want = 5.0
+
+ loops = int(f / math.sqrt(max(1, mid)))
+ took = run(cache, variant, function, mid, loops, alignment, 0,
+ quiet=True)
+ # Keep it reasonable for silly routines like bounce
+ factor = min(20, max(0.05, want/took))
+ f = f * factor
+
+ # Round f to a few significant figures
+ scale = 10**int(math.log10(f) - 1)
+ f = scale*int(f/scale)
+
+ for b in sorted(bytes):
+ # Figure out the number of loops to give a roughly consistent run
+ loops = int(f / math.sqrt(max(1, b)))
+ for run_id in range(0, NUM_RUNS):
+ run(cache, variant, function, b, loops, alignment,
+ run_id)
+
+def run_top(cache):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-v", "--variants", nargs="+", help="library variant to run (run all if not specified)", default = VARIANTS, choices = VARIANTS)
+ parser.add_argument("-f", "--functions", nargs="+", help="function to run (run all if not specified)", default = FUNCTIONS, choices = FUNCTIONS)
+ parser.add_argument("-l", "--limit", type=int, help="upper limit to test to (in bytes)", default = 512*1024)
+ args = parser.parse_args()
+
+ # Test all powers of 2
+ step1 = 2.0
+ # Test intermediate powers of 1.4
+ step2 = 1.4
+
+ bytes = []
+
+ for step in [step1, step2]:
+ if step:
+ # Figure out how many steps get us up to the top
+ steps = int(round(math.log(args.limit) / math.log(step)))
+ bytes.extend([int(step**x) for x in range(0, steps+1)])
+
+ run_many(cache, args.variants, bytes, args.functions)
+
+def main():
+ cachename = 'cache.txt'
+
+ cache = {}
+
+ try:
+ with open(cachename) as f:
+ for line in f:
+ line = line.strip()
+ parts = line.split(':')
+ cache[':'.join(parts[:7])] = line
+ except:
+ pass
+
+ try:
+ run_top(cache)
+ finally:
+ with open(cachename, 'w') as f:
+ for line in sorted(cache.values()):
+ print >> f, line
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/fixup.py b/contrib/cortex-strings/scripts/fixup.py
new file mode 100644
index 000000000000..003783a49838
--- /dev/null
+++ b/contrib/cortex-strings/scripts/fixup.py
@@ -0,0 +1,27 @@
+"""Simple script that enables target specific blocks based on the first argument.
+
+Matches comment blocks like this:
+
+/* For Foo: abc
+def
+*/
+
+and de-comments them giving:
+abc
+def
+"""
+import re
+import sys
+
+def main():
+ key = sys.argv[1]
+ expr = re.compile(r'/\* For %s:\s([^*]+)\*/' % key, re.M)
+
+ for arg in sys.argv[2:]:
+ with open(arg) as f:
+ body = f.read()
+ with open(arg, 'w') as f:
+ f.write(expr.sub(r'\1', body))
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/libplot.py b/contrib/cortex-strings/scripts/libplot.py
new file mode 100644
index 000000000000..034ffd331a59
--- /dev/null
+++ b/contrib/cortex-strings/scripts/libplot.py
@@ -0,0 +1,78 @@
+"""Shared routines for the plotters."""
+
+import fileinput
+import collections
+
+Record = collections.namedtuple('Record', 'variant function bytes loops src_alignment dst_alignment run_id elapsed rest')
+
+
+def make_colours():
+ return iter('m b g r c y k pink orange brown grey'.split())
+
+def parse_value(v):
+ """Turn text into a primitive"""
+ try:
+ if '.' in v:
+ return float(v)
+ else:
+ return int(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"""
+ 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
+ elif type(values[0]) == str:
+ return sorted(values, key=lambda x: '%-06d|%s' % (-prefer.find(x), x))
+ 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(':')])
+
+def parse():
+ """Parse a record file into named tuples, correcting for loop
+ overhead along the way.
+ """
+ records = [parse_row(x) for x in fileinput.input()]
+
+ # Pull out any bounce values
+ costs = {}
+
+ for record in [x for x in records if x.function=='bounce']:
+ costs[(record.bytes, record.loops)] = record.elapsed
+
+ # Fix up all of the records for cost
+ out = []
+
+ for record in records:
+ if record.function == 'bounce':
+ continue
+
+ cost = costs.get((record.bytes, record.loops), None)
+
+ if not cost:
+ out.append(record)
+ else:
+ # Unfortunately you can't update a namedtuple...
+ values = list(record)
+ values[-2] -= cost
+ out.append(Record(*values))
+
+ return out
diff --git a/contrib/cortex-strings/scripts/plot-align.py b/contrib/cortex-strings/scripts/plot-align.py
new file mode 100644
index 000000000000..524aa20a6c12
--- /dev/null
+++ b/contrib/cortex-strings/scripts/plot-align.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+"""Plot the performance of different variants of one routine versus alignment.
+"""
+
+import libplot
+
+import pylab
+
+
+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, ('src_alignment', 'dst_alignment'))
+
+ X = pylab.arange(len(alignments))
+ width = 1.0/(len(variants)+1)
+
+ colours = libplot.make_colours()
+
+ pylab.figure(1).set_size_inches((16, 12))
+ pylab.clf()
+
+ for i, variant in enumerate(variants):
+ heights = []
+
+ for alignment in alignments:
+ matches = [x for x in records if x.variant==variant and x.src_alignment==alignment[0] and x.dst_alignment==alignment[1]]
+
+ if matches:
+ vals = [match.bytes*match.loops/match.elapsed/(1024*1024) for
+ match in matches]
+ mean = sum(vals)/len(vals)
+ heights.append(mean)
+ else:
+ heights.append(0)
+
+ pylab.bar(X+i*width, heights, width, color=colours.next(), label=variant)
+
+
+ axes = pylab.axes()
+ 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())
+ pylab.xlabel('Alignment')
+ pylab.ylabel('Rate (MB/s)')
+ pylab.legend(loc='lower right', ncol=3)
+ pylab.grid()
+ pylab.savefig('alignment-%(function)s-%(bytes)d.png' % locals(), dpi=72)
+
+def main():
+ records = libplot.parse()
+
+ for function in libplot.unique(records, 'function'):
+ for bytes in libplot.unique(records, 'bytes'):
+ plot(records, bytes, function)
+
+ pylab.show()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/plot-sizes.py b/contrib/cortex-strings/scripts/plot-sizes.py
new file mode 100644
index 000000000000..26a22bc4d6ef
--- /dev/null
+++ b/contrib/cortex-strings/scripts/plot-sizes.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+"""Plot the performance for different block sizes of one function across
+variants.
+"""
+
+import libplot
+
+import pylab
+import pdb
+import math
+
+def pretty_kb(v):
+ if v < 1024:
+ return '%d' % v
+ else:
+ if v % 1024 == 0:
+ return '%d k' % (v//1024)
+ else:
+ return '%.1f k' % (v/1024)
+
+def plot(records, function, alignment=None, scale=1):
+ variants = libplot.unique(records, 'variant', prefer='this')
+ records = [x for x in records if x.function==function]
+
+ if alignment != None:
+ 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]
+
+ colours = libplot.make_colours()
+ all_x = []
+
+ pylab.figure(1).set_size_inches((6.4*scale, 4.8*scale))
+ pylab.clf()
+
+ if 'str' in function:
+ # The harness fills out to 16k. Anything past that is an
+ # early match
+ top = 16384
+ else:
+ top = 2**31
+
+ for variant in variants:
+ matches = [x for x in records if x.variant==variant and x.bytes <= top]
+ matches.sort(key=lambda x: x.bytes)
+
+ X = sorted(list(set([x.bytes for x in matches])))
+ Y = []
+ Yerr = []
+ for xbytes in X:
+ vals = [x.bytes*x.loops/x.elapsed/(1024*1024) for x in matches if x.bytes == xbytes]
+ if len(vals) > 1:
+ mean = sum(vals)/len(vals)
+ Y.append(mean)
+ if len(Yerr) == 0:
+ Yerr = [[], []]
+ err1 = max(vals) - mean
+ assert err1 >= 0
+ err2 = min(vals) - mean
+ assert err2 <= 0
+ Yerr[0].append(abs(err2))
+ Yerr[1].append(err1)
+ else:
+ Y.append(vals[0])
+
+ all_x.extend(X)
+ colour = colours.next()
+
+ if X:
+ pylab.plot(X, Y, c=colour)
+ if len(Yerr) > 0:
+ pylab.errorbar(X, Y, yerr=Yerr, c=colour, label=variant, fmt='o')
+ else:
+ pylab.scatter(X, Y, c=colour, label=variant, edgecolors='none')
+
+ pylab.legend(loc='upper left', ncol=3, prop={'size': 'small'})
+ pylab.grid()
+ pylab.title('%(function)s of %(aalignment)s byte aligned blocks' % locals())
+ pylab.xlabel('Size (B)')
+ pylab.ylabel('Rate (MB/s)')
+
+ # Figure out how high the range goes
+ top = max(all_x)
+
+ power = int(round(math.log(max(all_x)) / math.log(2)))
+
+ pylab.semilogx()
+
+ pylab.axes().set_xticks([2**x for x in range(0, power+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, ('src_alignment', 'dst_alignment'))
+
+ for function in functions:
+ for alignment in alignments:
+ for scale in [1, 2.5]:
+ if plot(records, function, alignment, scale):
+ pylab.savefig('sizes-%s-%02d-%02d-%.1f.png' % (function, alignment[0], alignment[1], scale), dpi=72)
+
+ pylab.show()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/plot-top.py b/contrib/cortex-strings/scripts/plot-top.py
new file mode 100644
index 000000000000..4095239ac815
--- /dev/null
+++ b/contrib/cortex-strings/scripts/plot-top.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+"""Plot the performance of different variants of the string routines
+for one size.
+"""
+
+import libplot
+
+import pylab
+
+
+def plot(records, bytes):
+ records = [x for x in records if x.bytes==bytes]
+
+ variants = libplot.unique(records, 'variant', prefer='this')
+ functions = libplot.unique(records, 'function')
+
+ X = pylab.arange(len(functions))
+ width = 1.0/(len(variants)+1)
+
+ colours = libplot.make_colours()
+
+ pylab.figure(1).set_size_inches((16, 12))
+ pylab.clf()
+
+ for i, variant in enumerate(variants):
+ heights = []
+
+ for function in functions:
+ matches = [x for x in records if x.variant==variant and x.function==function and x.src_alignment==8]
+
+ if matches:
+ vals = [match.bytes*match.loops/match.elapsed/(1024*1024) for
+ match in matches]
+ mean = sum(vals)/len(vals)
+ heights.append(mean)
+ else:
+ heights.append(0)
+
+ pylab.bar(X+i*width, heights, width, color=colours.next(), label=variant)
+
+ axes = pylab.axes()
+ axes.set_xticklabels(functions)
+ axes.set_xticks(X + 0.5)
+
+ pylab.title('Performance of different variants for %d byte blocks' % bytes)
+ pylab.ylabel('Rate (MB/s)')
+ pylab.legend(loc='upper left', ncol=3)
+ pylab.grid()
+ pylab.savefig('top-%06d.png' % bytes, dpi=72)
+
+def main():
+ records = libplot.parse()
+
+ for bytes in libplot.unique(records, 'bytes'):
+ plot(records, bytes)
+
+ pylab.show()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/plot.py b/contrib/cortex-strings/scripts/plot.py
new file mode 100644
index 000000000000..aa2bb1adb560
--- /dev/null
+++ b/contrib/cortex-strings/scripts/plot.py
@@ -0,0 +1,123 @@
+"""Plot the results for each test. Spits out a set of images into the
+current directory.
+"""
+
+import libplot
+
+import fileinput
+import collections
+import pprint
+
+import pylab
+
+Record = collections.namedtuple('Record', 'variant test size loops src_alignment dst_alignment run_id rawtime comment time bytes rate')
+
+def unique(rows, name):
+ """Takes a list of values, pulls out the named field, and returns
+ a list of the unique values of this field.
+ """
+ return sorted(set(getattr(x, name) for x in rows))
+
+def to_float(v):
+ """Convert a string into a better type.
+
+ >>> to_float('foo')
+ 'foo'
+ >>> to_float('1.23')
+ 1.23
+ >>> to_float('45')
+ 45
+ """
+ try:
+ if '.' in v:
+ return float(v)
+ else:
+ return int(v)
+ except:
+ return v
+
+def parse():
+ # Split the input up
+ rows = [x.strip().split(':') for x in fileinput.input()]
+ # Automatically turn numbers into the base type
+ rows = [[to_float(y) for y in x] for x in rows]
+
+ # Scan once to calculate the overhead
+ r = [Record(*(x + [0, 0, 0])) for x in rows]
+ bounces = pylab.array([(x.loops, x.rawtime) for x in r if x.test == 'bounce'])
+ fit = pylab.polyfit(bounces[:,0], bounces[:,1], 1)
+
+ records = []
+
+ for row in rows:
+ # Make a dummy record so we can use the names
+ r1 = Record(*(row + [0, 0, 0]))
+
+ bytes = r1.size * r1.loops
+ # Calculate the bounce time
+ delta = pylab.polyval(fit, [r1.loops])
+ time = r1.rawtime - delta
+ rate = bytes / time
+
+ records.append(Record(*(row + [time, bytes, rate])))
+
+ return records
+
+def plot(records, field, scale, ylabel):
+ variants = unique(records, 'variant')
+ tests = unique(records, 'test')
+
+ colours = libplot.make_colours()
+
+ # A little hack. We want the 'all' record to be drawn last so
+ # that it's obvious on the graph. Assume that no tests come
+ # before it alphabetically
+ variants.reverse()
+
+ for test in tests:
+ for variant in variants:
+ v = [x for x in records if x.test==test and x.variant==variant]
+ v.sort(key=lambda x: x.size)
+ V = pylab.array([(x.size, getattr(x, field)) for x in v])
+
+ # Ensure our results appear
+ order = 1 if variant == 'this' else 0
+
+ try:
+ # A little hack. We want the 'all' to be obvious on
+ # the graph
+ if variant == 'all':
+ pylab.scatter(V[:,0], V[:,1]/scale, label=variant)
+ pylab.plot(V[:,0], V[:,1]/scale)
+ else:
+ pylab.plot(V[:,0], V[:,1]/scale, label=variant,
+ zorder=order, c = colours.next())
+
+ except Exception, ex:
+ # michaelh1 likes to run this script while the test is
+ # still running which can lead to bad data
+ print ex, 'on %s of %s' % (variant, test)
+
+ pylab.legend(loc='lower right', ncol=2, prop={'size': 'small'})
+ pylab.xlabel('Block size (B)')
+ pylab.ylabel(ylabel)
+ pylab.title('%s %s' % (test, field))
+ pylab.grid()
+
+ pylab.savefig('%s-%s.png' % (test, field), dpi=100)
+ pylab.semilogx(basex=2)
+ pylab.savefig('%s-%s-semilog.png' % (test, field), dpi=100)
+ pylab.clf()
+
+def test():
+ import doctest
+ doctest.testmod()
+
+def main():
+ records = parse()
+
+ plot(records, 'rate', 1024**2, 'Rate (MB/s)')
+ plot(records, 'time', 1, 'Total time (s)')
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cortex-strings/scripts/trim.sh b/contrib/cortex-strings/scripts/trim.sh
new file mode 100755
index 000000000000..dab1047f34f9
--- /dev/null
+++ b/contrib/cortex-strings/scripts/trim.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+#
+# Trims the whitespace from around any given images
+#
+
+for i in $@; do
+ convert $i -bordercolor white -border 1x1 -trim +repage -alpha off +dither -colors 32 PNG8:next-$i
+ mv next-$i $i
+done