[LNT] r239891 - Add spark plots to the daily report page.

Kristof Beyls kristof.beyls at arm.com
Wed Jun 17 02:08:18 PDT 2015


Author: kbeyls
Date: Wed Jun 17 04:08:18 2015
New Revision: 239891

URL: http://llvm.org/viewvc/llvm-project?rev=239891&view=rev
Log:
Add spark plots to the daily report page.

In the table, performance differences are highlighted as a single percentage
difference.  Due to the noise in many benchmarks and boards, often the true
performance characteristic cannot be summarized well into a single percentage
number, and a human has to look at all the samples/data points collected by the
benchmarking runs to make a further assessment of whether a performance change
should be categorized as "noise" or "real".

To make that assessment, you need to look at the chart of performance data
points over time. By adding mini-charts with spark lines to the daily report
page, that human analysis can be done without having to open many separate
pages - speeding up that analysis massively.

This is implemented by adding SVG images to the daily report page, which means
that for the email report, this part probably won't show up in most email
clients. The only solution I can think of to also make it work for email
clients is to create images on the server - leading to more overloading of
the server.


Modified:
    lnt/trunk/lnt/server/reporting/dailyreport.py
    lnt/trunk/lnt/server/ui/templates/reporting/daily_report.html

Modified: lnt/trunk/lnt/server/reporting/dailyreport.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/reporting/dailyreport.py?rev=239891&r1=239890&r2=239891&view=diff
==============================================================================
--- lnt/trunk/lnt/server/reporting/dailyreport.py (original)
+++ lnt/trunk/lnt/server/reporting/dailyreport.py Wed Jun 17 04:08:18 2015
@@ -15,6 +15,55 @@ from collections import namedtuple
 OrderAndHistory = namedtuple('OrderAndHistory', ['max_order', 'recent_orders'])
 
 
+# Helper classes to make the sparkline chart construction easier in the jinja
+# template.
+class DayResult:
+    def __init__(self, comparisonResult):
+        self.cr = comparisonResult
+        self.samples = self.cr.samples
+        if self.samples is None:
+            self.samples = []
+
+
+class DayResults:
+    """
+    DayResults contains pre-processed data to easily construct the HTML for
+    a single row in the results table, showing how one test on one board
+    evolved over a number of runs/days.
+    """
+    def __init__(self):
+        self.day_results = []
+        self._complete = False
+        self.min_sample = None
+        self.max_sample = None
+
+    def __getitem__(self, i):
+        return self.day_results[i]
+
+    def __len__(self):
+        return len(self.day_results)
+
+    def append(self, day_result):
+        assert not self._complete
+        self.day_results.append(day_result)
+
+    def complete(self):
+        """
+        complete() needs to be called after all appends to this object, but
+        before the data is used the jinja template.
+        """
+        self._complete = True
+        all_samples = []
+        for dr in self.day_results:
+            if dr is None:
+                continue
+            if dr.cr.samples is not None:
+                all_samples.extend(dr.cr.samples)
+        if len(all_samples) > 0:
+            self.min_sample = min(all_samples)
+            self.max_sample = max(all_samples)
+
+
 class DailyReport(object):
     def __init__(self, ts, year, month, day, num_prior_days_to_include=3,
                  day_start_offset_hours=16, for_mail=False,
@@ -207,7 +256,7 @@ class DailyReport(object):
             had_failures = False
             sum_abs_day0_deltas = 0.
             for machine, day_results in results:
-                day0_cr = day_results[0]
+                day0_cr = day_results[0].cr
 
                 test_status = day0_cr.get_test_status()
 
@@ -239,7 +288,8 @@ class DailyReport(object):
                         continue
 
                     # Otherwise, compute the results for all the days.
-                    day_results = [cr]
+                    day_results = DayResults()
+                    day_results.append(DayResult(cr))
                     for i in range(1, self.num_prior_days_to_include):
                         day_runs = prev_day_run
                         prev_day_run = machine_runs.get((machine.id, i+1), ())
@@ -247,7 +297,9 @@ class DailyReport(object):
                                        (machine.id, i+1), ())
                         cr = sri.get_comparison_result(day_runs, prev_runs,
                                                        test.id, field)
-                        day_results.append(cr)
+                        day_results.append(DayResult(cr))
+
+                    day_results.complete()
 
                     # Append the result for the machine.
                     visible_results.append((machine, day_results))

Modified: lnt/trunk/lnt/server/ui/templates/reporting/daily_report.html
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/reporting/daily_report.html?rev=239891&r1=239890&r2=239891&view=diff
==============================================================================
--- lnt/trunk/lnt/server/ui/templates/reporting/daily_report.html (original)
+++ lnt/trunk/lnt/server/ui/templates/reporting/daily_report.html Wed Jun 17 04:08:18 2015
@@ -80,7 +80,8 @@
 {% endfor %}
 </table>
 
-{% macro get_initial_cell_value(cr) %}
+{% macro get_initial_cell_value(day_result) %}
+{% set cr = day_result.cr %}
 {% set test_status = cr.get_test_status() %}
 
 {% if (test_status == analysis.REGRESSED or
@@ -93,7 +94,8 @@
 
 {% endmacro %}
 
-{% macro get_cell_value(cr) %}
+{% macro get_cell_value(day_result) %}
+{% set cr = day_result.cr %}
 {% set test_status = cr.get_test_status() %}
 {% set value_status = cr.get_value_status() %}
 
@@ -130,10 +132,65 @@
 {% for i in range(report.num_prior_days_to_include)|reverse %}
       <th style="{{ styles.th }}">Day - {{i}}</th>
 {% endfor %}
+      <th style="{{ styles.th }}">Sparkline</th>
     </tr>
   </thead>
 {% endmacro %}
 
+{% macro spark_plot(day_results) %}
+{% set x_border_size = 5 %}
+{% set y_border_size = 2 %}
+{% set height = 18 %}
+{% set full_height = height + 2*y_border_size %}
+{% set x_day_spacing = 10 %}
+{% set sample_fuzzing = 0.5 %}
+{% set nr_days = day_results|length %}
+{% set width = x_day_spacing * nr_days + 2*x_border_size %}
+{% if day_results.max_sample != day_results.min_sample %}
+  {% set scaling_factor = (1.0*height)
+         / (day_results.max_sample-day_results.min_sample) %}
+{% else %}
+  {% set scaling_factor = 1.0 %}
+{% endif %}
+{% macro spark_y_coord(day_nr, value) %}
+  {{ (value-day_results.min_sample) * scaling_factor + y_border_size }}
+{% endmacro %}
+{% macro spark_x_coord(day_nr) %}
+  {{ (nr_days - day_nr) * x_day_spacing + x_border_size }}
+{% endmacro %}
+<span>
+<svg width="{{width}}" height="{{full_height}}">
+<rect width="{{width}}" height="{{full_height}}" fill="beige"/>
+{# Make y-axis go upwards instead of downwards: #}
+<g transform="translate(0, {{full_height}}) scale(1, -1) ">
+{% for dr in day_results %}
+  {% set day_nr = loop.index %}
+  {% set nr_samples_for_day = dr.samples|length %}
+  {% for sample in dr.samples %}
+    {# fuzz the x-coordinate slightly so that multiple samples with the same
+       value can be noticed #}
+    {% set sample_fuzz = (-sample_fuzzing*1.25) +
+                     (2.0*sample_fuzzing/nr_samples_for_day) * loop.index %}
+    <circle cx="{{ spark_x_coord(day_nr)|float + sample_fuzz }}"
+            cy="{{ spark_y_coord(day_nr, sample) }}" r="1"
+            stroke-width="1" stroke="black" fill="black" />
+  {% endfor %}
+{% endfor %}
+  <polyline points="
+  {% for dr in day_results %}
+    {% set cr = dr.cr %}
+    {% set day_nr = loop.index %}
+    {% if cr.current is not none %}
+      {{ spark_x_coord(day_nr) }}
+      {{ spark_y_coord(day_nr, cr.current) }}
+    {% endif %}
+  {% endfor %}
+  " fill="none" stroke="red" stroke-width="1"/>
+</g>
+</svg>
+</span>
+{% endmacro %}
+
 {% for field,field_results in report.result_table|reverse %}
 <h3>Result Table ({{ field.name }})</h3>
 {{ result_header() if not report.for_mail }}
@@ -141,7 +198,7 @@
 {{ result_header() if report.for_mail }}
   <tr>
     <td style="{{ styles.td }}" colspan="2"> <b>{{test.name}}</b></td>
-    <td style="{{ styles.td }}" colspan="{{report.num_prior_days_to_include}}"> </td>
+    <td style="{{ styles.td }}" colspan="{{report.num_prior_days_to_include + 1}}"> </td>
   </tr>
 {%   for machine,day_results in visible_results %}
 {%   set key_run = report.get_key_run(machine, 0) %}
@@ -155,6 +212,7 @@
 {%     for day_result in day_results[:-1]|reverse %}
     {{ get_cell_value(day_result) }}
 {%     endfor %}
+    <td>{{ spark_plot(day_results) }}</td>
   </tr>
 {%   endfor %}
 {{ "</table><p>" if report.for_mail }}





More information about the llvm-commits mailing list