10 return (int(h[0:2], 16) / 255.0, int(h[2:4], 16) / 255.0, int(h[4:6], 16) / 255.0)
14 ('folly', colorTuple('606080')),
15 ('tervel', colorTuple('9090b0')),
16 ('stdmap', colorTuple('606080')),
17 ('nbds', colorTuple('9090b0')),
18 ('michael', colorTuple('606080')),
19 ('tbb', colorTuple('9090b0')),
20 ('linear', colorTuple('ff4040')),
21 ('grampa', colorTuple('ff4040')),
22 ('leapfrog', colorTuple('ff4040')),
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, 150 + int(xAttribs.size + 0.5), 65 + 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(58, 11 + yAttribs.size)
141 labelFont = createScaledFont('Arial', 11)
144 cr.set_source_rgb(.4, .4, .4)
148 cr.rel_line_to(xAttribs.size + 1, 0)
149 for pos, label in xAttribs.iterLabels(): # Tick marks
150 x = math.floor(pos + 0.5) + 0.5
154 for pos, label in xAttribs.iterLabels(): # Labels
155 x = math.floor(pos + 0.5)
157 cr.translate(x - 1, 5)
158 cr.rotate(-math.pi / 4)
159 fillAlignedText(cr, 0, 6, labelFont, label, 1)
162 cr.set_source_rgb(*colorTuple('f0f0f0'))
163 for pos, label in yAttribs.iterLabels(): # Background lines
166 y = -math.floor(pos + 0.5) - 0.5
168 cr.rel_line_to(xAttribs.size + 1, 0)
170 cr.set_source_rgb(.4, .4, .4)
172 cr.rel_line_to(0, -yAttribs.size - 0.5)
174 for pos, label in yAttribs.iterLabels(): # Tick marks
177 y = -math.floor(pos + 0.5) - 0.5
179 cr.rel_line_to(-4, 0)
181 for pos, label in yAttribs.iterLabels(): # Labels
184 fillAlignedText(cr, -4, -pos + 4, labelFont, label, 1)
187 x = xAttribs.size - 70.5
189 cr.rectangle(x, y, 120, 82)
190 cr.set_source_rgb(*colorTuple('ffffff'))
192 cr.set_source_rgb(*colorTuple('f0f0f0'))
193 cr.rectangle(x, y, 120, 82)
198 for cn, curve in enumerate(self.curves):
199 points = curve.points
203 cr.set_line_width(width)
204 cr.set_source_rgba(*color)
208 cr.rectangle(0, 5, xAttribs.size, -yAttribs.size - 15)
211 cr.move_to(xAttribs.mapAxisValue(x), -yAttribs.mapAxisValue(y))
212 for x, y in points[1:]:
213 cr.line_to(xAttribs.mapAxisValue(x) + 0.5, -yAttribs.mapAxisValue(y) - 0.5)
214 x = xAttribs.size - 40
216 cr.move_to(x - 4.5, y - 4.5)
217 cr.rel_line_to(-21, 0)
221 labelFont = createScaledFont('Arial', 11)
224 #fillAlignedText(cr, xAttribs.mapAxisValue(x) + 3, -yAttribs.mapAxisValue(y) + 4, labelFont, label, 0)
225 x = xAttribs.size - 40
227 fillAlignedText(cr, x, y, labelFont, label, 0)
230 cr.set_source_rgb(0, 0, 0)
231 axisFont = createScaledFont('Helvetica', 16, weight=cairo.FONT_WEIGHT_BOLD)
233 cr.translate(-44, -yAttribs.size / 2.0)
234 cr.rotate(-math.pi / 2)
235 fillAlignedText(cr, 0, 0, axisFont, 'CPU Time Spent in Map', 0.5)
236 fillAlignedText(cr, xAttribs.size / 2.0, 50, axisFont, 'Interval Between Map Operations', 0.5)
239 surface.write_to_png(fileName)
242 #---------------------------------------------------
244 #---------------------------------------------------
247 return '%d ns' % (x * 1e9 + 0.5)
249 return '%d us' % (x * 1e6 + 0.5) # FIXME: Micro symbol
251 return '%d ms' % (x * 1e3 + 0.5)
253 return '%d s' % (x + 0.5)
255 graph = Graph(AxisAttribs(550, 1e-9, 10000e-9, 10, True, formatTime),
256 AxisAttribs(240, 0, 1, 0.1, False, lambda x: '%d%%' % int(x * 100 + 0.5)))
258 nullResultFn = 'build-%s/results.txt' % NULL_MAP
259 nullResults = eval(open(nullResultFn, 'r').read())
260 nullPoints = makeNamedTuples(nullResults)
262 for suffix, color in ALL_MAPS:
263 resultsPath = 'build-%s/results.txt' % suffix
264 if os.path.exists(resultsPath):
265 with open(resultsPath, 'r') as f:
266 results = eval(f.read())
267 dataPoints = makeNamedTuples(results)
268 def makeGraphPoint(nullPt, pt):
269 wuPerOp = float(nullPt.workUnitsDone) / nullPt.mapOpsDone
270 timePerWU = nullPt.totalTime / nullPt.workUnitsDone
271 timeBetweenOps = wuPerOp * timePerWU
272 mapTime = pt.totalTime - pt.workUnitsDone * timePerWU
273 return (timeBetweenOps, mapTime / pt.totalTime)
274 graphPoints = [makeGraphPoint(*pair) for pair in zip(nullPoints, dataPoints)]
277 for i in xrange(len(pts) - 1):
280 result += [(0.75*x0 + 0.25*x1, 0.75*y0 + 0.25*y1),
281 (0.25*x0 + 0.75*x1, 0.25*y0 + 0.75*y1)]
285 for i in xrange(len(pts) - 1):
288 result += [(0.5*x0 + 0.5*x1, 0.5*y0 + 0.5*y1)]
291 graphPoints = smooth2(graphPoints)
292 graphPoints = smooth2(graphPoints)
293 graphPoints = smooth2(graphPoints)
294 graphPoints = smooth2(graphPoints)
295 graphPoints = smooth2(graphPoints)
296 graphPoints = smooth2(graphPoints)
297 graphPoints = smooth(graphPoints)
298 graphPoints = smooth2(graphPoints)
299 graph.addCurve(Curve(results['mapType'], graphPoints, color))
301 graph.renderTo('out.png')