10 return (int(h[0:2], 16) / 255.0, int(h[2:4], 16) / 255.0, int(h[4:6], 16) / 255.0)
13 ('folly', colorTuple('606080')),
14 ('tervel', colorTuple('408040')),
15 ('stdmap', colorTuple('b0b090')),
16 ('nbds', colorTuple('9090b0')),
17 ('linear', colorTuple('ff4040')),
18 ('michael', colorTuple('202020')),
19 ('tbb', colorTuple('0090b0')),
20 ('cuckoo', colorTuple('d040d0')),
21 ('grampa', colorTuple('ff6040')),
22 ('leapfrog', colorTuple('ff8040')),
25 #---------------------------------------------------
26 # Cairo drawing helpers
27 #---------------------------------------------------
28 def createScaledFont(family, size, slant=cairo.FONT_SLANT_NORMAL, weight=cairo.FONT_WEIGHT_NORMAL):
29 """ Simple helper function to create a cairo ScaledFont. """
30 face = cairo.ToyFontFace(family, slant, weight)
31 DEFAULT_FONT_OPTIONS = cairo.FontOptions()
32 DEFAULT_FONT_OPTIONS.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
33 return cairo.ScaledFont(face, cairo.Matrix(xx=size, yy=size), cairo.Matrix(), DEFAULT_FONT_OPTIONS)
35 def fillAlignedText(cr, x, y, scaledFont, text, alignment = 0):
36 """ Draw some aligned text at the specified co-ordinates.
37 alignment = 0: left-justify
38 alignment = 0.5: center
39 alignment = 1: right-justify """
40 ascent, descent = scaledFont.extents()[:2]
41 x_bearing, y_bearing, width, height = scaledFont.text_extents(text)[:4]
43 cr.set_scaled_font(scaledFont)
44 cr.move_to(math.floor(x + 0.5 - width * alignment), math.floor(y + 0.5))
49 """ Preserve cairo state inside the scope of a with statement. """
50 def __init__(self, cr):
55 def __exit__(self, type, value, traceback):
59 #---------------------------------------------------
61 #---------------------------------------------------
63 """ Describes one axis on the graph. Can be linear or logarithmic. """
65 def __init__(self, size, min, max, step, logarithmic = False, labeler = lambda x: str(int(x + 0.5))):
66 self.size = float(size)
67 self.logarithmic = logarithmic
68 self.labeler = labeler
69 self.toAxis = lambda x: math.log(x) if logarithmic else float(x)
70 self.fromAxis = lambda x: math.exp(x) if logarithmic else float(x)
71 self.min = self.toAxis(min)
72 self.max = self.toAxis(max)
73 self.step = self.toAxis(step)
75 def setMinMax(self, min, max):
76 self.min = self.toAxis(min)
77 self.max = self.toAxis(max)
79 def mapAxisValue(self, x):
80 """ Maps x to a point along the axis. """
81 return (self.toAxis(x) - self.min) / (self.max - self.min) * self.size
84 """ Helper to iterate through all the tick marks along the axis. """
85 lo = int(math.floor(self.min / self.step + 1 - 1e-9))
86 hi = int(math.floor(self.max / self.step + 1e-9))
87 for i in xrange(lo, hi + 1):
89 if self.min == 0 and i == 0:
91 yield self.mapAxisValue(self.fromAxis(value)), self.labeler(self.fromAxis(value))
94 #---------------------------------------------------
96 #---------------------------------------------------
97 def makeNamedTuples(results, typeName = 'Point', labels = 'labels', points = 'points'):
98 namedTupleType = collections.namedtuple(typeName, results[labels])
99 numLabels = len(results[labels])
100 return [namedTupleType(*p[:numLabels]) for p in results[points]]
103 def __init__(self, name, points, color):
109 """ Renders a graph. """
111 def __init__(self, xAttribs, yAttribs):
112 self.xAttribs = xAttribs
113 self.yAttribs = yAttribs
118 def addCurve(self, curve):
119 self.curves.append(curve)
120 xMin = min(x for x, y in curve.points)
121 if self.xMin is None or xMin < self.xMin:
123 xMax = max(x for x, y in curve.points)
124 if self.xMax is None or xMax > self.xMax:
127 def renderTo(self, fileName):
128 xAttribs = self.xAttribs
129 yAttribs = self.yAttribs
130 xAttribs.setMinMax(self.xMin, self.xMax)
132 # Create the image surface and cairo context
133 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 330 + int(xAttribs.size + 0.5), 55 + int(yAttribs.size + 0.5))
134 cr = cairo.Context(surface)
135 cr.set_source_rgb(1, 1, 1)
137 cr.set_miter_limit(1.414)
138 cr.translate(80, 8 + yAttribs.size)
141 labelFont = createScaledFont('Arial', 13)
144 cr.set_source_rgb(.4, .4, .4)
148 cr.rel_line_to(xAttribs.size + 1, 0)
150 for pos, label in xAttribs.iterLabels(): # Labels
151 x = math.floor(pos + 0.5)
154 fillAlignedText(cr, 0, 6, labelFont, label, 0.5)
157 cr.set_source_rgb(*colorTuple('f0f0f0'))
158 for pos, label in yAttribs.iterLabels(): # Background lines
161 y = -math.floor(pos + 0.5) - 0.5
163 cr.rel_line_to(xAttribs.size + 1, 0)
165 cr.set_source_rgb(.4, .4, .4)
167 cr.rel_line_to(0, -yAttribs.size - 0.5)
169 for pos, label in yAttribs.iterLabels(): # Tick marks
172 y = -math.floor(pos + 0.5) - 0.5
174 cr.rel_line_to(-4, 0)
176 for pos, label in yAttribs.iterLabels(): # Labels
179 fillAlignedText(cr, -4, -pos + 4, labelFont, label, 1)
183 x = xAttribs.size - 70.5 + 80
185 cr.rectangle(x, y, 120, 82)
186 cr.set_source_rgb(*colorTuple('ffffff'))
188 cr.set_source_rgb(*colorTuple('f0f0f0'))
189 cr.rectangle(x, y, 120, 82)
195 for cn, curve in enumerate(self.curves):
196 points = curve.points
199 #if color == colorTuple('ff4040'):
202 cr.set_line_width(width)
203 cr.set_source_rgba(*color)
204 #if color == colorTuple('9090b0'):
205 # cr.set_dash([9, 2])
207 cr.rectangle(0, 5, xAttribs.size, -yAttribs.size - 15)
210 cr.move_to(xAttribs.mapAxisValue(x), -yAttribs.mapAxisValue(y))
211 for x, y in points[1:]:
212 cr.line_to(xAttribs.mapAxisValue(x) + 0.5, -yAttribs.mapAxisValue(y) - 0.5)
215 cr.rectangle(xAttribs.mapAxisValue(x) - 2.5, -yAttribs.mapAxisValue(y) - 2.5, 5, 5)
218 x = xAttribs.size + 40
219 y = -120 + (5 - cn) * 14
220 cr.move_to(x - 4.5, y - 4.5)
221 cr.rel_line_to(-21, 0)
225 weight = cairo.FONT_WEIGHT_NORMAL
226 if color == colorTuple('ff4040'):
227 weight = cairo.FONT_WEIGHT_BOLD
228 labelFont = createScaledFont('Arial', 13, weight=weight)
231 #fillAlignedText(cr, xAttribs.mapAxisValue(x) + 3, -yAttribs.mapAxisValue(y) + 4, labelFont, label, 0)
232 x = xAttribs.size + 40
233 y = -120 + (5 - cn) * 14
234 fillAlignedText(cr, x, y, labelFont, label, 0)
237 cr.set_source_rgb(0, 0, 0)
238 axisFont = createScaledFont('Helvetica', 16, weight=cairo.FONT_WEIGHT_BOLD)
240 cr.translate(-66, -yAttribs.size / 2.0)
241 cr.rotate(-math.pi / 2)
242 fillAlignedText(cr, 0, 0, axisFont, 'Map Operations / Sec', 0.5)
244 axisFont2 = createScaledFont('Helvetica', 13)
245 cr.translate(-50, -yAttribs.size / 2.0)
246 cr.rotate(-math.pi / 2)
247 cr.set_source_rgba(*colorTuple('808080'))
248 fillAlignedText(cr, 0, 0, axisFont2, '(Total Across All Threads)', 0.5)
249 fillAlignedText(cr, xAttribs.size / 2.0, 42, axisFont, 'Threads', 0.5)
252 surface.write_to_png(fileName)
255 #---------------------------------------------------
257 #---------------------------------------------------
260 return '%d ns' % (x * 1e9 + 0.5)
262 return '%d us' % (x * 1e6 + 0.5) # FIXME: Micro symbol
264 return '%d ms' % (x * 1e3 + 0.5)
266 return '%d s' % (x + 0.5)
268 graph = Graph(AxisAttribs(250, 1, 6, 1),
269 AxisAttribs(240, 0, 150, 10, False, lambda x: '%dM' % x if x % 50 == 0 else ''))
271 for suffix, color in ALL_MAPS:
272 resultsPath = 'build-%s/results.txt' % suffix
273 if os.path.exists(resultsPath):
274 with open(resultsPath, 'r') as f:
275 results = eval(f.read())
276 dataPoints = makeNamedTuples(results)
277 def makeGraphPoint(pt):
278 mapOpsPerSec = pt.mapOpsDone / pt.totalTime
279 return (pt.numThreads, mapOpsPerSec * pt.numThreads / 1000000)
280 graphPoints = [makeGraphPoint(pt) for pt in dataPoints]
281 graph.addCurve(Curve(results['mapType'], graphPoints, color))
283 graph.renderTo('out.png')