1"""Plot the results for each test.  Spits out a set of images into the
2current directory.
3"""
4
5import libplot
6
7import fileinput
8import collections
9import pprint
10
11import pylab
12
13Record = collections.namedtuple('Record', 'variant test size loops src_alignment dst_alignment run_id rawtime comment time bytes rate')
14
15def unique(rows, name):
16    """Takes a list of values, pulls out the named field, and returns
17    a list of the unique values of this field.
18    """
19    return sorted(set(getattr(x, name) for x in rows))
20
21def to_float(v):
22    """Convert a string into a better type.
23
24    >>> to_float('foo')
25    'foo'
26    >>> to_float('1.23')
27    1.23
28    >>> to_float('45')
29    45
30    """
31    try:
32        if '.' in v:
33            return float(v)
34        else:
35            return int(v)
36    except:
37        return v
38
39def parse():
40    # Split the input up
41    rows = [x.strip().split(':') for x in fileinput.input()]
42    # Automatically turn numbers into the base type
43    rows = [[to_float(y) for y in x] for x in rows]
44
45    # Scan once to calculate the overhead
46    r = [Record(*(x + [0, 0, 0])) for x in rows]
47    bounces = pylab.array([(x.loops, x.rawtime) for x in r if x.test == 'bounce'])
48    fit = pylab.polyfit(bounces[:,0], bounces[:,1], 1)
49
50    records = []
51
52    for row in rows:
53        # Make a dummy record so we can use the names
54        r1 = Record(*(row + [0, 0, 0]))
55
56        bytes = r1.size * r1.loops
57        # Calculate the bounce time
58        delta = pylab.polyval(fit, [r1.loops])
59        time = r1.rawtime - delta
60        rate = bytes / time
61
62        records.append(Record(*(row + [time, bytes, rate])))
63
64    return records
65
66def plot(records, field, scale, ylabel):
67    variants = unique(records, 'variant')
68    tests = unique(records, 'test')
69
70    colours = libplot.make_colours()
71
72    # A little hack.  We want the 'all' record to be drawn last so
73    # that it's obvious on the graph.  Assume that no tests come
74    # before it alphabetically
75    variants.reverse()
76
77    for test in tests:
78        for variant in variants:
79            v = [x for x in records if x.test==test and x.variant==variant]
80            v.sort(key=lambda x: x.size)
81            V = pylab.array([(x.size, getattr(x, field)) for x in v])
82
83            # Ensure our results appear
84            order = 1 if variant == 'this' else 0
85
86            try:
87                # A little hack.  We want the 'all' to be obvious on
88                # the graph
89                if variant == 'all':
90                    pylab.scatter(V[:,0], V[:,1]/scale, label=variant)
91                    pylab.plot(V[:,0], V[:,1]/scale)
92                else:
93                    pylab.plot(V[:,0], V[:,1]/scale, label=variant,
94                            zorder=order, c = colours.next())
95
96            except Exception, ex:
97                # michaelh1 likes to run this script while the test is
98                # still running which can lead to bad data
99                print ex, 'on %s of %s' % (variant, test)
100
101        pylab.legend(loc='lower right', ncol=2, prop={'size': 'small'})
102        pylab.xlabel('Block size (B)')
103        pylab.ylabel(ylabel)
104        pylab.title('%s %s' % (test, field))
105        pylab.grid()
106
107        pylab.savefig('%s-%s.png' % (test, field), dpi=100)
108        pylab.semilogx(basex=2)
109        pylab.savefig('%s-%s-semilog.png' % (test, field), dpi=100)
110        pylab.clf()
111
112def test():
113    import doctest
114    doctest.testmod()
115
116def main():
117    records = parse()
118
119    plot(records, 'rate', 1024**2, 'Rate (MB/s)')
120    plot(records, 'time', 1, 'Total time (s)')
121
122if __name__ == '__main__':
123    main()
124