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('9090b0')),
15 ('stdmap', colorTuple('606080')),
16 ('nbds', colorTuple('9090b0')),
17 ('michael', colorTuple('606080')),
18 ('tbb', colorTuple('9090b0')),
19 ('linear', colorTuple('ff4040')),
20 ('grampa', colorTuple('ff4040')),
21 ('leapfrog', colorTuple('ff4040')),
24 #---------------------------------------------------
25 # Cairo drawing helpers
26 #---------------------------------------------------
27 def createScaledFont(family, size, slant=cairo.FONT_SLANT_NORMAL, weight=cairo.FONT_WEIGHT_NORMAL):
28 """ Simple helper function to create a cairo ScaledFont. """
29 face = cairo.ToyFontFace(family, slant, weight)
30 DEFAULT_FONT_OPTIONS = cairo.FontOptions()
31 DEFAULT_FONT_OPTIONS.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
32 return cairo.ScaledFont(face, cairo.Matrix(xx=size, yy=size), cairo.Matrix(), DEFAULT_FONT_OPTIONS)
34 def fillAlignedText(cr, x, y, scaledFont, text, alignment = 0):
35 """ Draw some aligned text at the specified co-ordinates.
36 alignment = 0: left-justify
37 alignment = 0.5: center
38 alignment = 1: right-justify """
39 ascent, descent = scaledFont.extents()[:2]
40 x_bearing, y_bearing, width, height = scaledFont.text_extents(text)[:4]
42 cr.set_scaled_font(scaledFont)
43 cr.move_to(math.floor(x + 0.5 - width * alignment), math.floor(y + 0.5))
48 """ Preserve cairo state inside the scope of a with statement. """
49 def __init__(self, cr):
54 def __exit__(self, type, value, traceback):
58 #---------------------------------------------------
60 #---------------------------------------------------
62 """ Describes one axis on the graph. Can be linear or logarithmic. """
64 def __init__(self, size, min, max, step, logarithmic = False, labeler = lambda x: str(int(x + 0.5))):
65 self.size = float(size)
66 self.logarithmic = logarithmic
67 self.labeler = labeler
68 self.toAxis = lambda x: math.log(x) if logarithmic else float(x)
69 self.fromAxis = lambda x: math.exp(x) if logarithmic else float(x)
70 self.min = self.toAxis(min)
71 self.max = self.toAxis(max)
72 self.step = self.toAxis(step)
74 def setMinMax(self, min, max):
75 self.min = self.toAxis(min)
76 self.max = self.toAxis(max)
78 def mapAxisValue(self, x):
79 """ Maps x to a point along the axis. """
80 return (self.toAxis(x) - self.min) / (self.max - self.min) * self.size
83 """ Helper to iterate through all the tick marks along the axis. """
84 lo = int(math.floor(self.min / self.step + 1 - 1e-9))
85 hi = int(math.floor(self.max / self.step + 1e-9))
86 for i in xrange(lo, hi + 1):
88 if self.min == 0 and i == 0:
90 yield self.mapAxisValue(self.fromAxis(value)), self.labeler(self.fromAxis(value))
93 #---------------------------------------------------
95 #---------------------------------------------------
96 def makeNamedTuples(results, typeName = 'Point', labels = 'labels', points = 'points'):
97 namedTupleType = collections.namedtuple(typeName, results[labels])
98 numLabels = len(results[labels])
99 return [namedTupleType(*p[:numLabels]) for p in results[points]]
102 def __init__(self, name, points, color):
108 """ Renders a graph. """
110 def __init__(self, xAttribs, yAttribs):
111 self.xAttribs = xAttribs
112 self.yAttribs = yAttribs
117 def addCurve(self, curve):
118 self.curves.append(curve)
119 xMin = min(x for x, y in curve.points)
120 if self.xMin is None or xMin < self.xMin:
122 xMax = max(x for x, y in curve.points)
123 if self.xMax is None or xMax > self.xMax:
126 def renderTo(self, fileName):
127 xAttribs = self.xAttribs
128 yAttribs = self.yAttribs
129 xAttribs.setMinMax(self.xMin, self.xMax)
131 # Create the image surface and cairo context
132 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 330 + int(xAttribs.size + 0.5), 55 + int(yAttribs.size + 0.5))
133 cr = cairo.Context(surface)
134 cr.set_source_rgb(1, 1, 1)
136 cr.set_miter_limit(1.414)
137 cr.translate(80, 8 + yAttribs.size)
140 labelFont = createScaledFont('Arial', 13)
143 cr.set_source_rgb(.4, .4, .4)
147 cr.rel_line_to(xAttribs.size + 1, 0)
149 for pos, label in xAttribs.iterLabels(): # Labels
150 x = math.floor(pos + 0.5)
153 fillAlignedText(cr, 0, 6, labelFont, label, 0.5)
156 cr.set_source_rgb(*colorTuple('f0f0f0'))
157 for pos, label in yAttribs.iterLabels(): # Background lines
160 y = -math.floor(pos + 0.5) - 0.5
162 cr.rel_line_to(xAttribs.size + 1, 0)
164 cr.set_source_rgb(.4, .4, .4)
166 cr.rel_line_to(0, -yAttribs.size - 0.5)
168 for pos, label in yAttribs.iterLabels(): # Tick marks
171 y = -math.floor(pos + 0.5) - 0.5
173 cr.rel_line_to(-4, 0)
175 for pos, label in yAttribs.iterLabels(): # Labels
178 fillAlignedText(cr, -4, -pos + 4, labelFont, label, 1)
182 x = xAttribs.size - 70.5 + 80
184 cr.rectangle(x, y, 120, 82)
185 cr.set_source_rgb(*colorTuple('ffffff'))
187 cr.set_source_rgb(*colorTuple('f0f0f0'))
188 cr.rectangle(x, y, 120, 82)
194 for cn, curve in enumerate(self.curves):
195 points = curve.points
198 #if color == colorTuple('ff4040'):
201 cr.set_line_width(width)
202 cr.set_source_rgba(*color)
203 #if color == colorTuple('9090b0'):
204 # cr.set_dash([9, 2])
206 cr.rectangle(0, 5, xAttribs.size, -yAttribs.size - 15)
209 cr.move_to(xAttribs.mapAxisValue(x), -yAttribs.mapAxisValue(y))
210 for x, y in points[1:]:
211 cr.line_to(xAttribs.mapAxisValue(x) + 0.5, -yAttribs.mapAxisValue(y) - 0.5)
214 cr.rectangle(xAttribs.mapAxisValue(x) - 2.5, -yAttribs.mapAxisValue(y) - 2.5, 5, 5)
217 x = xAttribs.size + 40
218 y = -120 + (5 - cn) * 14
219 cr.move_to(x - 4.5, y - 4.5)
220 cr.rel_line_to(-21, 0)
224 weight = cairo.FONT_WEIGHT_NORMAL
225 if color == colorTuple('ff4040'):
226 weight = cairo.FONT_WEIGHT_BOLD
227 labelFont = createScaledFont('Arial', 13, weight=weight)
230 #fillAlignedText(cr, xAttribs.mapAxisValue(x) + 3, -yAttribs.mapAxisValue(y) + 4, labelFont, label, 0)
231 x = xAttribs.size + 40
232 y = -120 + (5 - cn) * 14
233 fillAlignedText(cr, x, y, labelFont, label, 0)
236 cr.set_source_rgb(0, 0, 0)
237 axisFont = createScaledFont('Helvetica', 16, weight=cairo.FONT_WEIGHT_BOLD)
239 cr.translate(-66, -yAttribs.size / 2.0)
240 cr.rotate(-math.pi / 2)
241 fillAlignedText(cr, 0, 0, axisFont, 'Map Operations / Sec', 0.5)
243 axisFont2 = createScaledFont('Helvetica', 13)
244 cr.translate(-50, -yAttribs.size / 2.0)
245 cr.rotate(-math.pi / 2)
246 cr.set_source_rgba(*colorTuple('808080'))
247 fillAlignedText(cr, 0, 0, axisFont2, '(Total Across All Threads)', 0.5)
248 fillAlignedText(cr, xAttribs.size / 2.0, 42, axisFont, 'Threads', 0.5)
251 surface.write_to_png(fileName)
254 #---------------------------------------------------
256 #---------------------------------------------------
259 return '%d ns' % (x * 1e9 + 0.5)
261 return '%d us' % (x * 1e6 + 0.5) # FIXME: Micro symbol
263 return '%d ms' % (x * 1e3 + 0.5)
265 return '%d s' % (x + 0.5)
267 graph = Graph(AxisAttribs(250, 1, 6, 1),
268 AxisAttribs(240, 0, 150, 10, False, lambda x: '%dM' % x if x % 50 == 0 else ''))
270 for suffix, color in ALL_MAPS:
271 resultsPath = 'build-%s/results.txt' % suffix
272 if os.path.exists(resultsPath):
273 with open(resultsPath, 'r') as f:
274 results = eval(f.read())
275 dataPoints = makeNamedTuples(results)
276 def makeGraphPoint(pt):
277 mapOpsPerSec = pt.mapOpsDone / pt.totalTime
278 return (pt.numThreads, mapOpsPerSec * pt.numThreads / 1000000)
279 graphPoints = [makeGraphPoint(pt) for pt in dataPoints]
280 graph.addCurve(Curve(results['mapType'], graphPoints, color))
282 graph.renderTo('out.png')