deps: update v8 to 4.3.61.21
[platform/upstream/nodejs.git] / deps / v8 / tools / perf-to-html.py
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 import commands
16 import json
17 import math
18 from optparse import OptionParser
19 import os
20 import shutil
21 import sys
22 import tempfile
23
24 PERCENT_CONSIDERED_SIGNIFICANT = 0.5
25 PROBABILITY_CONSIDERED_SIGNIFICANT = 0.02
26 PROBABILITY_CONSIDERED_MEANINGLESS = 0.05
27
28
29 def ComputeZ(baseline_avg, baseline_sigma, mean, n):
30   if baseline_sigma == 0:
31     return 1000.0;
32   return abs((mean - baseline_avg) / (baseline_sigma / math.sqrt(n)))
33
34
35 # Values from http://www.fourmilab.ch/rpkp/experiments/analysis/zCalc.html
36 def ComputeProbability(z):
37   if z > 2.575829: # p 0.005: two sided < 0.01
38     return 0
39   if z > 2.326348: # p 0.010
40     return 0.01
41   if z > 2.170091: # p 0.015
42     return 0.02
43   if z > 2.053749: # p 0.020
44     return 0.03
45   if z > 1.959964: # p 0.025: two sided < 0.05
46     return 0.04
47   if z > 1.880793: # p 0.030
48     return 0.05
49   if z > 1.811910: # p 0.035
50     return 0.06
51   if z > 1.750686: # p 0.040
52     return 0.07
53   if z > 1.695397: # p 0.045
54     return 0.08
55   if z > 1.644853: # p 0.050: two sided < 0.10
56     return 0.09
57   if z > 1.281551: # p 0.100: two sided < 0.20
58     return 0.10
59   return 0.20 # two sided p >= 0.20
60
61
62 class Result:
63   def __init__(self, test_name, count, hasScoreUnits, result, sigma,
64                master_result, master_sigma):
65     self.result_ = float(result)
66     self.sigma_ = float(sigma)
67     self.master_result_ = float(master_result)
68     self.master_sigma_ = float(master_sigma)
69     self.significant_ = False
70     self.notable_ = 0
71     self.percentage_string_ = ""
72     # compute notability and significance.
73     if hasScoreUnits:
74       compare_num = 100*self.result_/self.master_result_ - 100
75     else:
76       compare_num = 100*self.master_result_/self.result_ - 100
77     if abs(compare_num) > 0.1:
78       self.percentage_string_ = "%3.1f" % (compare_num)
79       z = ComputeZ(self.master_result_, self.master_sigma_, self.result_, count)
80       p = ComputeProbability(z)
81       if p < PROBABILITY_CONSIDERED_SIGNIFICANT:
82         self.significant_ = True
83       if compare_num >= PERCENT_CONSIDERED_SIGNIFICANT:
84         self.notable_ = 1
85       elif compare_num <= -PERCENT_CONSIDERED_SIGNIFICANT:
86         self.notable_ = -1
87
88   def result(self):
89     return self.result_
90
91   def sigma(self):
92     return self.sigma_
93
94   def master_result(self):
95     return self.master_result_
96
97   def master_sigma(self):
98     return self.master_sigma_
99
100   def percentage_string(self):
101     return self.percentage_string_;
102
103   def isSignificant(self):
104     return self.significant_
105
106   def isNotablyPositive(self):
107     return self.notable_ > 0
108
109   def isNotablyNegative(self):
110     return self.notable_ < 0
111
112
113 class Benchmark:
114   def __init__(self, name, data):
115     self.name_ = name
116     self.tests_ = {}
117     for test in data:
118       # strip off "<name>/" prefix
119       test_name = test.split("/")[1]
120       self.appendResult(test_name, data[test])
121
122   # tests is a dictionary of Results
123   def tests(self):
124     return self.tests_
125
126   def SortedTestKeys(self):
127     keys = self.tests_.keys()
128     keys.sort()
129     t = "Total"
130     if t in keys:
131       keys.remove(t)
132       keys.append(t)
133     return keys
134
135   def name(self):
136     return self.name_
137
138   def appendResult(self, test_name, test_data):
139     with_string = test_data["result with patch   "]
140     data = with_string.split()
141     master_string = test_data["result without patch"]
142     master_data = master_string.split()
143     runs = int(test_data["runs"])
144     units = test_data["units"]
145     hasScoreUnits = units == "score"
146     self.tests_[test_name] = Result(test_name,
147                                     runs,
148                                     hasScoreUnits,
149                                     data[0], data[2],
150                                     master_data[0], master_data[2])
151
152
153 class BenchmarkRenderer:
154   def __init__(self, output_file):
155     self.print_output_ = []
156     self.output_file_ = output_file
157
158   def Print(self, str_data):
159     self.print_output_.append(str_data)
160
161   def FlushOutput(self):
162     string_data = "\n".join(self.print_output_)
163     print_output = []
164     if self.output_file_:
165       # create a file
166       with open(self.output_file_, "w") as text_file:
167         text_file.write(string_data)
168     else:
169       print(string_data)
170
171   def RenderOneBenchmark(self, benchmark):
172     self.Print("<h2>")
173     self.Print("<a name=\"" + benchmark.name() + "\">")
174     self.Print(benchmark.name() + "</a> <a href=\"#top\">(top)</a>")
175     self.Print("</h2>");
176     self.Print("<table class=\"benchmark\">")
177     self.Print("<thead>")
178     self.Print("  <th>Test</th>")
179     self.Print("  <th>Result</th>")
180     self.Print("  <th>Master</th>")
181     self.Print("  <th>%</th>")
182     self.Print("</thead>")
183     self.Print("<tbody>")
184     tests = benchmark.tests()
185     for test in benchmark.SortedTestKeys():
186       t = tests[test]
187       self.Print("  <tr>")
188       self.Print("    <td>" + test + "</td>")
189       self.Print("    <td>" + str(t.result()) + "</td>")
190       self.Print("    <td>" + str(t.master_result()) + "</td>")
191       t = tests[test]
192       res = t.percentage_string()
193       if t.isSignificant():
194         res = self.bold(res)
195       if t.isNotablyPositive():
196         res = self.green(res)
197       elif t.isNotablyNegative():
198         res = self.red(res)
199       self.Print("    <td>" + res + "</td>")
200       self.Print("  </tr>")
201     self.Print("</tbody>")
202     self.Print("</table>")
203
204   def ProcessJSONData(self, data, title):
205     self.Print("<h1>" + title + "</h1>")
206     self.Print("<ul>")
207     for benchmark in data:
208      if benchmark != "errors":
209        self.Print("<li><a href=\"#" + benchmark + "\">" + benchmark + "</a></li>")
210     self.Print("</ul>")
211     for benchmark in data:
212       if benchmark != "errors":
213         benchmark_object = Benchmark(benchmark, data[benchmark])
214         self.RenderOneBenchmark(benchmark_object)
215
216   def bold(self, data):
217     return "<b>" + data + "</b>"
218
219   def red(self, data):
220     return "<font color=\"red\">" + data + "</font>"
221
222
223   def green(self, data):
224     return "<font color=\"green\">" + data + "</font>"
225
226   def PrintHeader(self):
227     data = """<html>
228 <head>
229 <title>Output</title>
230 <style type="text/css">
231 /*
232 Style inspired by Andy Ferra's gist at https://gist.github.com/andyferra/2554919
233 */
234 body {
235   font-family: Helvetica, arial, sans-serif;
236   font-size: 14px;
237   line-height: 1.6;
238   padding-top: 10px;
239   padding-bottom: 10px;
240   background-color: white;
241   padding: 30px;
242 }
243 h1, h2, h3, h4, h5, h6 {
244   margin: 20px 0 10px;
245   padding: 0;
246   font-weight: bold;
247   -webkit-font-smoothing: antialiased;
248   cursor: text;
249   position: relative;
250 }
251 h1 {
252   font-size: 28px;
253   color: black;
254 }
255
256 h2 {
257   font-size: 24px;
258   border-bottom: 1px solid #cccccc;
259   color: black;
260 }
261
262 h3 {
263   font-size: 18px;
264 }
265
266 h4 {
267   font-size: 16px;
268 }
269
270 h5 {
271   font-size: 14px;
272 }
273
274 h6 {
275   color: #777777;
276   font-size: 14px;
277 }
278
279 p, blockquote, ul, ol, dl, li, table, pre {
280   margin: 15px 0;
281 }
282
283 li p.first {
284   display: inline-block;
285 }
286
287 ul, ol {
288   padding-left: 30px;
289 }
290
291 ul :first-child, ol :first-child {
292   margin-top: 0;
293 }
294
295 ul :last-child, ol :last-child {
296   margin-bottom: 0;
297 }
298
299 table {
300   padding: 0;
301 }
302
303 table tr {
304   border-top: 1px solid #cccccc;
305   background-color: white;
306   margin: 0;
307   padding: 0;
308 }
309
310 table tr:nth-child(2n) {
311   background-color: #f8f8f8;
312 }
313
314 table tr th {
315   font-weight: bold;
316   border: 1px solid #cccccc;
317   text-align: left;
318   margin: 0;
319   padding: 6px 13px;
320 }
321 table tr td {
322   border: 1px solid #cccccc;
323   text-align: left;
324   margin: 0;
325   padding: 6px 13px;
326 }
327 table tr th :first-child, table tr td :first-child {
328   margin-top: 0;
329 }
330 table tr th :last-child, table tr td :last-child {
331   margin-bottom: 0;
332 }
333 </style>
334 </head>
335 <body>
336 """
337     self.Print(data)
338
339   def PrintFooter(self):
340     data = """</body>
341 </html>
342 """
343     self.Print(data)
344
345
346 def Render(opts, args):
347   if opts.filename:
348     with open(opts.filename) as json_data:
349       data = json.load(json_data)
350   else:
351     # load data from stdin
352     data = json.load(sys.stdin)
353
354   if opts.title:
355     title = opts.title
356   elif opts.filename:
357     title = opts.filename
358   else:
359     title = "Benchmark results"
360   renderer = BenchmarkRenderer(opts.output)
361   renderer.PrintHeader()
362   renderer.ProcessJSONData(data, title)
363   renderer.PrintFooter()
364   renderer.FlushOutput()
365
366
367 if __name__ == '__main__':
368   parser = OptionParser(usage=__doc__)
369   parser.add_option("-f", "--filename", dest="filename",
370                     help="Specifies the filename for the JSON results "
371                          "rather than reading from stdin.")
372   parser.add_option("-t", "--title", dest="title",
373                     help="Optional title of the web page.")
374   parser.add_option("-o", "--output", dest="output",
375                     help="Write html output to this file rather than stdout.")
376
377   (opts, args) = parser.parse_args()
378   Render(opts, args)