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

« back to all changes in this revision

Viewing changes to scripts/bench.py

  • Committer: Michael Hope
  • Date: 2010-09-10 01:34:33 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: michael.hope@linaro.org-20100910013433-3ehxlbloizs3pqwg
Fixed up the Makefile so a 'make dist' includes everything required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
 
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
 
 
13
 
import subprocess
14
 
import math
15
 
import sys
16
 
 
17
 
# Prefix to the executables
18
 
build = '../build/try-'
19
 
 
20
 
ALL = 'memchr memcmp memcpy memset strchr strcmp strcpy strlen'
21
 
 
22
 
HAS = {
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',
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',
33
 
    'plain': 'memset memcpy strcmp strcpy',
34
 
}
35
 
 
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
 
NUM_RUNS = 5
53
 
 
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))
57
 
 
58
 
    if key in cache:
59
 
        got = cache[key]
60
 
    else:
61
 
        xbuild = build
62
 
        cmd = '%(xbuild)s%(variant)s -t %(function)s -c %(bytes)s -l %(loops)s -a %(alignment)s -r %(run_id)s' % locals()
63
 
 
64
 
        try:
65
 
            got = subprocess.check_output(cmd.split()).strip()
66
 
        except OSError, ex:
67
 
            assert False, 'Error %s while running %s' % (ex, cmd)
68
 
 
69
 
    parts = got.split(':')
70
 
    took = float(parts[7])
71
 
 
72
 
    cache[key] = got
73
 
 
74
 
    if not quiet:
75
 
        print got
76
 
        sys.stdout.flush()
77
 
 
78
 
    return took
79
 
 
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
83
 
    bytes = sorted(bytes)
84
 
    mid = bytes[int(len(bytes)/1.5)]
85
 
 
86
 
    if not all_functions:
87
 
        # Use the ordering in 'this' as the default
88
 
        all_functions = HAS['this'].split()
89
 
 
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)
95
 
 
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():
100
 
                    continue
101
 
 
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
105
 
                # do
106
 
                f = 50000000
107
 
                want = 5.0
108
 
 
109
 
                loops = int(f / math.sqrt(max(1, mid)))
110
 
                took = run(cache, variant, function, mid, loops, alignment, 0,
111
 
                           quiet=True)
112
 
                # Keep it reasonable for silly routines like bounce
113
 
                factor = min(20, max(0.05, want/took))
114
 
                f = f * factor
115
 
                
116
 
                # Round f to a few significant figures
117
 
                scale = 10**int(math.log10(f) - 1)
118
 
                f = scale*int(f/scale)
119
 
 
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,
125
 
                            run_id)
126
 
 
127
 
def run_top(cache):
128
 
    variants = sorted(HAS.keys())
129
 
    functions = sys.argv[1:]
130
 
 
131
 
    # Upper limit in bytes to test to
132
 
    top = 512*1024
133
 
    # Test all powers of 2
134
 
    step1 = 2.0
135
 
    # Test intermediate powers of 1.4
136
 
    step2 = 1.4
137
 
    
138
 
    bytes = []
139
 
    
140
 
    for step in [step1, step2]:
141
 
        if step:
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)])
145
 
 
146
 
    run_many(cache, variants, bytes, functions)
147
 
 
148
 
def main():
149
 
    cachename = 'cache.txt'
150
 
 
151
 
    cache = {}
152
 
 
153
 
    try:
154
 
        with open(cachename) as f:
155
 
            for line in f:
156
 
                line = line.strip()
157
 
                parts = line.split(':')
158
 
                cache[':'.join(parts[:7])] = line
159
 
    except:
160
 
        pass
161
 
 
162
 
    try:
163
 
        run_top(cache)
164
 
    finally:
165
 
        with open(cachename, 'w') as f:
166
 
            for line in sorted(cache.values()):
167
 
                print >> f, line
168
 
 
169
 
if __name__ == '__main__':
170
 
    main()