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': 'memcmp memcpy memset strcmp strcpy strlen',
26
'csl': 'memcpy memset',
27
'glibc': 'memcpy memset strlen',
29
'newlib': 'memcpy strcmp strcpy strlen',
31
'newlib-xscale': 'memchr memcpy memset strchr strcmp strcpy strlen',
32
'plain': 'memset memcpy strcmp strcpy',
35
def run(cache, variant, function, bytes, loops, alignment=8, quiet=False):
36
"""Perform a single run, exercising the cache as appropriate."""
37
key = ':'.join('%s' % x for x in (variant, function, bytes, loops, alignment))
43
cmd = '%(xbuild)s%(variant)s -t %(function)s -c %(bytes)s -l %(loops)s -a %(alignment)s' % locals()
46
got = subprocess.check_output(cmd.split()).strip()
48
assert False, 'Error %s while running %s' % (ex, cmd)
50
parts = got.split(':')
51
took = float(parts[5])
61
def run_many(cache, variants, bytes, alignments):
62
# We want the data to come out in a useful order. So fix an
63
# alignment and function, and do all sizes for a variant first
65
mid = bytes[len(bytes)/2]
67
# Use the ordering in 'this' as the default
68
all_functions = HAS['this'].split()
70
# Find all other functions
71
for functions in HAS.values():
72
for function in functions.split():
73
if function not in all_functions:
74
all_functions.append(function)
76
for alignment in alignments:
77
for function in all_functions:
78
for variant in variants:
79
if function not in HAS[variant].split():
82
# Run a tracer through and see how long it takes and
83
# adjust the number of loops based on that. Not great
84
# for memchr() and similar which are O(n), but it will
89
loops = int(f / math.sqrt(max(1, mid)))
90
took = run(cache, variant, function, mid, loops, alignment, quiet=True)
91
# Keep it reasonable for silly routines like bounce
92
factor = min(20, max(0.05, want/took))
95
# Round f to a few significant figures
96
scale = 10**int(math.log10(f) - 1)
97
f = scale*int(f/scale)
99
for b in sorted(bytes):
100
# Figure out the number of loops to give a roughly consistent run
101
loops = int(f / math.sqrt(max(1, b)))
102
run(cache, variant, function, b, loops, alignment)
105
variants = sorted(HAS.keys())
107
# Upper limit in bytes to test to
109
# Test all powers of 2
111
# Test intermediate powers of 1.4
116
for step in [step1, step2]:
118
# Figure out how many steps get us up to the top
119
steps = int(round(math.log(top) / math.log(step)))
120
bytes.extend([int(step**x) for x in range(0, steps+1)])
122
alignments = [8, 16, 4, 1, 2, 32]
124
run_many(cache, variants, bytes, alignments)
127
cachename = 'cache.txt'
132
with open(cachename) as f:
135
parts = line.split(':')
136
cache[':'.join(parts[:5])] = line
143
with open(cachename, 'w') as f:
144
for line in sorted(cache.values()):
147
if __name__ == '__main__':