~linaro-toolchain-dev/cortex-strings/trunk

29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
1
#!/usr/bin/env python
2
58 by Michael Hope
Bumped the version as we're almost at 1.0. Tidied up the README. Added an overview comment to one of the scripts.
3
"""Simple harness that benchmarks different variants of the routines,
4
caches the results, and emits all of the records at the end.
5
6
Results are generated for different values of:
7
 * Source
8
 * Routine
9
 * Length
10
 * Alignment
11
"""
12
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
13
import subprocess
14
import math
15
import sys
16
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
17
# Prefix to the executables
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
18
build = '../build/try-'
19
79 by Michael Hope
Update the list of variants and routines in each. Rework how the
20
ALL = 'memchr memcmp memcpy memset strchr strcmp strcpy strlen'
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
21
22
HAS = {
79 by Michael Hope
Update the list of variants and routines in each. Rework how the
23
    'this': 'bounce memchr memcpy memset strchr strcpy strlen',
104 by Will Newton
Add support for bionic-a9 and bionic-a15.
24
    'bionic-a9': 'memcmp memcpy memset strcmp strcpy strlen',
25
    'bionic-a15': 'memcmp memcpy memset strcmp strcpy strlen',
79 by Michael Hope
Update the list of variants and routines in each. Rework how the
26
    'bionic-c': ALL,
27
    'csl': 'memcpy memset',
28
    'glibc': 'memcpy memset strlen',
29
    'glibc-c': ALL,
30
    'newlib': 'memcpy strcmp strcpy strlen',
31
    'newlib-c': ALL,
32
    'newlib-xscale': 'memchr memcpy memset strchr strcmp strcpy strlen',
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
33
    'plain': 'memset memcpy strcmp strcpy',
34
}
35
113 by Will Newton
Allow aligning source and destination buffers separately.
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']
39
40
ALIGNMENTS = {
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,
50
}
51
52
def run(cache, variant, function, bytes, loops, alignment, quiet=False):
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
53
    """Perform a single run, exercising the cache as appropriate."""
54
    key = ':'.join('%s' % x for x in (variant, function, bytes, loops, alignment))
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
55
56
    if key in cache:
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
57
        got = cache[key]
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
58
    else:
59
        xbuild = build
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
60
        cmd = '%(xbuild)s%(variant)s -t %(function)s -c %(bytes)s -l %(loops)s -a %(alignment)s' % locals()
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
61
30 by Michael Hope
Added more ranges. changed everything to MB/s. Account for the loop overhead.
62
        try:
63
            got = subprocess.check_output(cmd.split()).strip()
64
        except OSError, ex:
65
            assert False, 'Error %s while running %s' % (ex, cmd)
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
66
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
67
    parts = got.split(':')
113 by Will Newton
Allow aligning source and destination buffers separately.
68
    took = float(parts[6])
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
69
70
    cache[key] = got
71
72
    if not quiet:
73
        print got
74
        sys.stdout.flush()
75
76
    return took
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
77
113 by Will Newton
Allow aligning source and destination buffers separately.
78
def run_many(cache, variants, bytes, all_functions):
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
79
    # We want the data to come out in a useful order.  So fix an
80
    # alignment and function, and do all sizes for a variant first
81
    bytes = sorted(bytes)
113 by Will Newton
Allow aligning source and destination buffers separately.
82
    mid = bytes[int(len(bytes)/1.5)]
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
83
105 by Will Newton
Allow running a subset of benchmarks.
84
    if not all_functions:
85
        # Use the ordering in 'this' as the default
86
        all_functions = HAS['this'].split()
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
87
105 by Will Newton
Allow running a subset of benchmarks.
88
        # Find all other functions
89
        for functions in HAS.values():
90
            for function in functions.split():
91
                if function not in all_functions:
92
                    all_functions.append(function)
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
93
113 by Will Newton
Allow aligning source and destination buffers separately.
94
    for function in all_functions:
95
        for alignment in ALIGNMENTS[function]:
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
96
            for variant in variants:
97
                if function not in HAS[variant].split():
98
                    continue
99
100
                # Run a tracer through and see how long it takes and
101
                # adjust the number of loops based on that.  Not great
102
                # for memchr() and similar which are O(n), but it will
103
                # do
104
                f = 50000000
105
                want = 5.0
106
107
                loops = int(f / math.sqrt(max(1, mid)))
108
                took = run(cache, variant, function, mid, loops, alignment, quiet=True)
109
                # Keep it reasonable for silly routines like bounce
110
                factor = min(20, max(0.05, want/took))
111
                f = f * factor
112
                
113
                # Round f to a few significant figures
114
                scale = 10**int(math.log10(f) - 1)
115
                f = scale*int(f/scale)
116
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
117
                for b in sorted(bytes):
118
                    # Figure out the number of loops to give a roughly consistent run
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
119
                    loops = int(f / math.sqrt(max(1, b)))
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
120
                    run(cache, variant, function, b, loops, alignment)
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
121
122
def run_top(cache):
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
123
    variants = sorted(HAS.keys())
105 by Will Newton
Allow running a subset of benchmarks.
124
    functions = sys.argv[1:]
32 by Michael Hope
Added support for different alignments. Flattened out the loops.
125
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
126
    # Upper limit in bytes to test to
127
    top = 512*1024
128
    # Test all powers of 2
129
    step1 = 2.0
130
    # Test intermediate powers of 1.4
131
    step2 = 1.4
132
    
133
    bytes = []
79 by Michael Hope
Update the list of variants and routines in each. Rework how the
134
    
135
    for step in [step1, step2]:
136
        if step:
137
            # Figure out how many steps get us up to the top
138
            steps = int(round(math.log(top) / math.log(step)))
139
            bytes.extend([int(step**x) for x in range(0, steps+1)])
49 by Michael Hope
Benchmark more sizes and alignments. Make the figures bigger to make the text smaller. Put the 'this' results first.
140
113 by Will Newton
Allow aligning source and destination buffers separately.
141
    run_many(cache, variants, bytes, functions)
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
142
143
def main():
144
    cachename = 'cache.txt'
145
146
    cache = {}
147
148
    try:
149
        with open(cachename) as f:
150
            for line in f:
151
                line = line.strip()
152
                parts = line.split(':')
114 by Will Newton
Fix cache handling after alignment changes.
153
                cache[':'.join(parts[:6])] = line
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
154
    except:
155
        pass
156
157
    try:
158
        run_top(cache)
159
    finally:
160
        with open(cachename, 'w') as f:
79 by Michael Hope
Update the list of variants and routines in each. Rework how the
161
            for line in sorted(cache.values()):
29 by Michael Hope
Added some scripts to run the benchmarks and to plot the results.
162
                print >> f, line
163
30 by Michael Hope
Added more ranges. changed everything to MB/s. Account for the loop overhead.
164
if __name__ == '__main__':
165
    main()