3
"""Simple harness that benchmarks different variants of the routines,
4
caches the results, and emits all of the records at the end.
6
Results are generated for different values of:
17
# Prefix to the executables
18
build = '../build/try-'
20
ALL = 'memchr memcmp memcpy memset strchr strcmp strcpy strlen'
23
'this': 'bounce memchr memcpy memset strchr strcpy strlen',
24
'bionic-a9': 'memcmp memcpy memset strcmp strcpy strlen',
25
'bionic-a15': 'memcmp memcpy memset strcmp strcpy strlen',
27
'csl': 'memcpy memset',
28
'glibc': 'memcpy memset strlen',
30
'newlib': 'memcpy strcmp strcpy strlen',
32
'newlib-xscale': 'memchr memcpy memset strchr strcmp strcpy strlen',
33
'plain': 'memset memcpy strcmp strcpy',
36
BOUNCE_ALIGNMENTS = ['1']
37
SINGLE_BUFFER_ALIGNMENTS = ['1', '2', '4', '8', '16', '32']
38
DUAL_BUFFER_ALIGNMENTS = ['1:32', '2:32', '4:32', '8:32', '16:32', '32:32']
41
'bounce': BOUNCE_ALIGNMENTS,
42
'memchr': SINGLE_BUFFER_ALIGNMENTS,
43
'memset': SINGLE_BUFFER_ALIGNMENTS,
44
'strchr': SINGLE_BUFFER_ALIGNMENTS,
45
'strlen': SINGLE_BUFFER_ALIGNMENTS,
46
'memcmp': DUAL_BUFFER_ALIGNMENTS,
47
'memcpy': DUAL_BUFFER_ALIGNMENTS,
48
'strcmp': DUAL_BUFFER_ALIGNMENTS,
49
'strcpy': DUAL_BUFFER_ALIGNMENTS,
54
def run(cache, variant, function, bytes, loops, alignment, run_id, quiet=False):
55
"""Perform a single run, exercising the cache as appropriate."""
56
key = ':'.join('%s' % x for x in (variant, function, bytes, loops, alignment, run_id))
62
cmd = '%(xbuild)s%(variant)s -t %(function)s -c %(bytes)s -l %(loops)s -a %(alignment)s -r %(run_id)s' % locals()
65
got = subprocess.check_output(cmd.split()).strip()
67
assert False, 'Error %s while running %s' % (ex, cmd)
69
parts = got.split(':')
70
took = float(parts[7])
80
def run_many(cache, variants, bytes, all_functions):
81
# We want the data to come out in a useful order. So fix an
82
# alignment and function, and do all sizes for a variant first
84
mid = bytes[int(len(bytes)/1.5)]
87
# Use the ordering in 'this' as the default
88
all_functions = HAS['this'].split()
90
# Find all other functions
91
for functions in HAS.values():
92
for function in functions.split():
93
if function not in all_functions:
94
all_functions.append(function)
96
for function in all_functions:
97
for alignment in ALIGNMENTS[function]:
98
for variant in variants:
99
if function not in HAS[variant].split():
102
# Run a tracer through and see how long it takes and
103
# adjust the number of loops based on that. Not great
104
# for memchr() and similar which are O(n), but it will
109
loops = int(f / math.sqrt(max(1, mid)))
110
took = run(cache, variant, function, mid, loops, alignment, 0,
112
# Keep it reasonable for silly routines like bounce
113
factor = min(20, max(0.05, want/took))
116
# Round f to a few significant figures
117
scale = 10**int(math.log10(f) - 1)
118
f = scale*int(f/scale)
120
for b in sorted(bytes):
121
# Figure out the number of loops to give a roughly consistent run
122
loops = int(f / math.sqrt(max(1, b)))
123
for run_id in range(0, NUM_RUNS):
124
run(cache, variant, function, b, loops, alignment,
128
variants = sorted(HAS.keys())
129
functions = sys.argv[1:]
131
# Upper limit in bytes to test to
133
# Test all powers of 2
135
# Test intermediate powers of 1.4
140
for step in [step1, step2]:
142
# Figure out how many steps get us up to the top
143
steps = int(round(math.log(top) / math.log(step)))
144
bytes.extend([int(step**x) for x in range(0, steps+1)])
146
run_many(cache, variants, bytes, functions)
149
cachename = 'cache.txt'
154
with open(cachename) as f:
157
parts = line.split(':')
158
cache[':'.join(parts[:7])] = line
165
with open(cachename, 'w') as f:
166
for line in sorted(cache.values()):
169
if __name__ == '__main__':