Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: tools/perf-to-html.py

Issue 1033603004: perf-to-html.py - render JSON try perf jobs in a pleasing way. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Retrieve number of runs from the JSON. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2015 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 '''
6 python %prog
7
8 Convert a perf trybot JSON file into a pleasing HTML page. It can read
9 from standard input or via the --filename option. Examples:
10
11 cat results.json | %prog --title "ia32 results"
12 %prog -f results.json -t "ia32 results" -o results.html
13 '''
14
15 from optparse import OptionParser
16 import os
Michael Achenbach 2015/03/25 14:49:42 nit: sort
mvstanton 2015/03/25 15:37:49 Done.
17 import sys
18 import json
19 import math
20 import tempfile
21 import commands
22 import shutil
23
24 PERCENT_CONSIDERED_SIGNIFICANT = 0.5
25 PROBABILITY_CONSIDERED_SIGNIFICANT = 0.02
26 PROBABILITY_CONSIDERED_MEANINGLESS = 0.05
27
28 v8_benchmarks = ["V8", "Octane", "Richards", "DeltaBlue", "Crypto",
29 "EarleyBoyer", "RayTrace", "RegExp", "Splay", "SplayLatency",
30 "NavierStokes", "PdfJS", "Mandreel", "MandreelLatency",
31 "Gameboy", "CodeLoad", "Box2D", "zlib", "Typescript"]
32
33 print_output = ""
34
35 def Print(str_data):
36 global print_output
37 print_output += str_data
38 print_output += "\n"
39
40
41 def PrintBuffer(opts):
42 global print_output
43 if opts.output <> None:
Michael Achenbach 2015/03/25 14:49:42 if opts.output: ...
mvstanton 2015/03/25 15:37:49 Done.
44 # create a file
45 text_file = open(opts.output, "w")
Michael Achenbach 2015/03/25 14:49:42 with open(opts.output, "w") as text_file: text_f
mvstanton 2015/03/25 15:37:49 Done.
46 text_file.write(print_output)
47 text_file.close()
48 else:
49 print(print_output)
50 print_output = ""
51
52
53 def ComputeZ(baseline_avg, baseline_sigma, mean, n):
54 if baseline_sigma == 0:
55 return 1000.0;
56 return abs((mean - baseline_avg) / (baseline_sigma / math.sqrt(n)))
57
58
59 # Values from http://www.fourmilab.ch/rpkp/experiments/analysis/zCalc.html
60 def ComputeProbability(z):
61 if z > 2.575829: # p 0.005: two sided < 0.01
62 return 0
63 if z > 2.326348: # p 0.010
64 return 0.01
65 if z > 2.170091: # p 0.015
66 return 0.02
67 if z > 2.053749: # p 0.020
68 return 0.03
69 if z > 1.959964: # p 0.025: two sided < 0.05
70 return 0.04
71 if z > 1.880793: # p 0.030
72 return 0.05
73 if z > 1.811910: # p 0.035
74 return 0.06
75 if z > 1.750686: # p 0.040
76 return 0.07
77 if z > 1.695397: # p 0.045
78 return 0.08
79 if z > 1.644853: # p 0.050: two sided < 0.10
80 return 0.09
81 if z > 1.281551: # p 0.100: two sided < 0.20
82 return 0.10
83 return 0.20 # two sided p >= 0.20
84
85
86 def PercentColor(change_percent, flakyness):
Michael Achenbach 2015/03/25 14:49:42 This function seems to be unused.
mvstanton 2015/03/25 15:37:49 Done.
87 result = ""
88 if change_percent >= PERCENT_CONSIDERED_SIGNIFICANT:
89 result = "$GREEN"
90 elif change_percent <= -PERCENT_CONSIDERED_SIGNIFICANT:
91 result = "$RED"
92 else:
Michael Achenbach 2015/03/25 14:49:42 Why return here? Isn't bold black a valid and usef
mvstanton 2015/03/25 15:37:49 Done.
93 return ""
94 if flakyness < PROBABILITY_CONSIDERED_SIGNIFICANT:
95 result += "$BOLD"
96 elif flakyness > PROBABILITY_CONSIDERED_MEANINGLESS:
97 result = ""
98 return result
99
100
101 class Result:
102 def __init__(self, test_name, count, result, sigma,
103 master_result, master_sigma):
104 self.result_ = float(result)
105 self.sigma_ = float(sigma)
106 self.master_result_ = float(master_result)
107 self.master_sigma_ = float(master_sigma)
108 self.significant_ = False
109 self.notable_ = 0
110 self.percentage_string_ = ""
111 # compute notability and significance.
112 if test_name in v8_benchmarks:
Michael Achenbach 2015/03/25 14:49:42 Please make this decision based on the units prope
mvstanton 2015/03/25 15:37:49 Done.
113 compare_num = 100*self.result_/self.master_result_ - 100
114 else:
115 compare_num = 100*self.master_result_/self.result_ - 100
116 if abs(compare_num) > 0.1:
117 self.percentage_string_ = "%3.1f" % (compare_num)
118 z = ComputeZ(self.master_result_, self.master_sigma_, self.result_, count)
119 p = ComputeProbability(z)
120 if p < PROBABILITY_CONSIDERED_SIGNIFICANT:
121 self.significant_ = True
122 if compare_num >= PERCENT_CONSIDERED_SIGNIFICANT:
123 self.notable_ = 1
124 elif compare_num <= -PERCENT_CONSIDERED_SIGNIFICANT:
125 self.notable_ = -1
126
127 def result(self):
128 return self.result_
129
130 def sigma(self):
131 return self.sigma_
132
133 def master_result(self):
134 return self.master_result_
135
136 def master_sigma(self):
137 return self.master_sigma_
138
139 def percentage_string(self):
140 return self.percentage_string_;
141
142 def isSignificant(self):
143 return self.significant_
144
145 def isNotablyPositive(self):
146 return self.notable_ > 0
147
148 def isNotablyNegative(self):
149 return self.notable_ < 0
150
151
152 class Benchmark:
153 def __init__(self, name):
154 self.name_ = name
155 self.tests_ = {}
156
157 # tests is a dictionary of Results
158 def tests(self):
159 return self.tests_
160
161 def SortedTestKeys(self):
162 keys = self.tests_.keys()
163 keys.sort()
164 t = "Total"
165 if t in keys:
166 keys.remove(t)
167 keys.append(t)
168 return keys
169
170 def name(self):
171 return self.name_
172
173 def appendResult(self, test_name, test_data):
174 with_string = test_data["result with patch "]
175 data = with_string.split()
176 master_string = test_data["result without patch"]
177 master_data = master_string.split()
178 runs = int(test_data["runs"])
Michael Achenbach 2015/03/25 14:49:42 Here you could also access test_data["unit"].
mvstanton 2015/03/25 15:37:49 Done.
179 self.tests_[test_name] = Result(test_name,
180 runs,
181 data[0], data[2],
182 master_data[0], master_data[2])
183
184
185 def CreateBenchmark(name, data):
186 benchmark = Benchmark(name)
187 saved_data = None
188 for test in data:
189 # strip off "<name>/" prefix
190 test_name = test.split("/")[1]
191 benchmark.appendResult(test_name, data[test])
192
193 return benchmark
194
195
196 def bold(data):
197 return "<b>" + data + "</b>"
198
199
200 def red(data):
201 return "<font color=\"red\">" + data + "</font>"
202
203
204 def green(data):
205 return "<font color=\"green\">" + data + "</font>"
206
207
208 def RenderBenchmark(benchmark):
209 Print("<h2>")
210 Print("<a name=\"" + benchmark.name() + "\">")
211 Print(benchmark.name() + "</a> <a href=\"#top\">(top)</a>")
212 Print("</h2>");
213 Print("<table class=\"benchmark\">")
214 Print("<thead>")
215 Print(" <th>Test</th>")
216 Print(" <th>Result</th>")
217 Print(" <th>Master</th>")
218 Print(" <th>%</th>")
219 Print("</thead>")
220 Print("<tbody>")
221 tests = benchmark.tests()
222 for test in benchmark.SortedTestKeys():
223 t = tests[test]
224 Print(" <tr>")
225 Print(" <td>" + test + "</td>")
226 Print(" <td>" + str(t.result()) + "</td>")
227 Print(" <td>" + str(t.master_result()) + "</td>")
228 t = tests[test]
229 res = t.percentage_string()
230 if t.isSignificant():
231 res = bold(res)
232 if t.isNotablyPositive():
233 res = green(res)
234 elif t.isNotablyNegative():
235 res = red(res)
236 Print(" <td>" + res + "</td>")
237 Print(" </tr>")
238 Print("</tbody>")
239 Print("</table>")
240
241 def ProcessJSONData(data, title):
242 Print("<h1>" + title + "</h1>")
243 Print("<ul>")
244 for benchmark in data:
245 if benchmark != "errors":
246 Print("<li><a href=\"#" + benchmark + "\">" + benchmark + "</a></li>")
247 Print("</ul>")
248 for benchmark in data:
249 if benchmark != "errors":
250 benchmark_object = CreateBenchmark(benchmark, data[benchmark])
251 RenderBenchmark(benchmark_object)
252
253
254 def PrintHeader():
255 data = """<html>
256 <head>
257 <title>Output</title>
258 <style type="text/css">
259 /*
260 Style inspired by Andy Ferra's gist at https://gist.github.com/andyferra/2554919
261 */
262 body {
263 font-family: Helvetica, arial, sans-serif;
264 font-size: 14px;
265 line-height: 1.6;
266 padding-top: 10px;
267 padding-bottom: 10px;
268 background-color: white;
269 padding: 30px;
270 }
271 h1, h2, h3, h4, h5, h6 {
272 margin: 20px 0 10px;
273 padding: 0;
274 font-weight: bold;
275 -webkit-font-smoothing: antialiased;
276 cursor: text;
277 position: relative;
278 }
279 h1 {
280 font-size: 28px;
281 color: black;
282 }
283
284 h2 {
285 font-size: 24px;
286 border-bottom: 1px solid #cccccc;
287 color: black;
288 }
289
290 h3 {
291 font-size: 18px;
292 }
293
294 h4 {
295 font-size: 16px;
296 }
297
298 h5 {
299 font-size: 14px;
300 }
301
302 h6 {
303 color: #777777;
304 font-size: 14px;
305 }
306
307 p, blockquote, ul, ol, dl, li, table, pre {
308 margin: 15px 0;
309 }
310
311 li p.first {
312 display: inline-block;
313 }
314
315 ul, ol {
316 padding-left: 30px;
317 }
318
319 ul :first-child, ol :first-child {
320 margin-top: 0;
321 }
322
323 ul :last-child, ol :last-child {
324 margin-bottom: 0;
325 }
326
327 table {
328 padding: 0;
329 }
330
331 table tr {
332 border-top: 1px solid #cccccc;
333 background-color: white;
334 margin: 0;
335 padding: 0;
336 }
337
338 table tr:nth-child(2n) {
339 background-color: #f8f8f8;
340 }
341
342 table tr th {
343 font-weight: bold;
344 border: 1px solid #cccccc;
345 text-align: left;
346 margin: 0;
347 padding: 6px 13px;
348 }
349 table tr td {
350 border: 1px solid #cccccc;
351 text-align: left;
352 margin: 0;
353 padding: 6px 13px;
354 }
355 table tr th :first-child, table tr td :first-child {
356 margin-top: 0;
357 }
358 table tr th :last-child, table tr td :last-child {
359 margin-bottom: 0;
360 }
361 </style>
362 </head>
363 <body>
364 """
365 Print(data)
366
367
368 def PrintFooter():
369 data = """</body>
370 </html>
371 """
372 Print(data)
373
374
375 def CreateFile(opts, args):
376 PrintHeader()
377 if opts.filename <> None:
Michael Achenbach 2015/03/25 14:49:43 if opts.filename:
mvstanton 2015/03/25 15:37:49 Done.
378 json_data = open(opts.filename)
Michael Achenbach 2015/03/25 14:49:42 with open(opts.filename) as json_data: data = js
mvstanton 2015/03/25 15:37:49 Done.
379 data = json.load(json_data)
380 json_data.close()
381 else:
382 # load data from stdin
383 data = json.load(sys.stdin)
384
385 if opts.title <> None:
Michael Achenbach 2015/03/25 14:49:42 if opts.title:
mvstanton 2015/03/25 15:37:49 Done.
386 title = opts.title
387 elif opts.filename <> None:
Michael Achenbach 2015/03/25 14:49:42 elif opts.filename:
mvstanton 2015/03/25 15:37:49 Done.
388 title = opts.filename
389 else:
390 title = "Benchmark results"
391 ProcessJSONData(data, title)
392 PrintFooter()
393
394
395 if __name__ == '__main__':
396 parser = OptionParser(usage=__doc__)
397 parser.add_option("-f", "--filename", dest="filename",
398 help="Specifies the filename for the JSON results "\
Michael Achenbach 2015/03/25 14:49:42 No need for \ - just align the strings e.g.: parse
mvstanton 2015/03/25 15:37:49 Done.
399 "rather than reading from stdin.")
400 parser.add_option("-t", "--title", dest="title",
401 help="Optional title of the web page.")
402 parser.add_option("-o", "--output", dest="output",
403 help="Write html output to this file rather than stdout.")
404
405 (opts, args) = parser.parse_args()
406 CreateFile(opts, args)
407 PrintBuffer(opts)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698