Add percentiles to runtime perf data

Change-Id: I75dfadcddb4baed3ee1e78e9168f6eaa866226a6
diff --git a/tools/perf/d8.html b/tools/perf/d8.html
index bd9c867..d495324 100644
--- a/tools/perf/d8.html
+++ b/tools/perf/d8.html
@@ -6,6 +6,18 @@
   <link rel="stylesheet" href="stylesheet.css">
 </head>
 <body>
+  <div id="runtime-fn">
+    <input type="radio" id="runtime-min" name="runtime-fn" checked>
+    <label for="runtime-min">Min</label>
+    <input type="radio" id="runtime-p50" name="runtime-fn">
+    <label for="runtime-p50">P50</label>
+    <input type="radio" id="runtime-p90" name="runtime-fn">
+    <label for="runtime-p90">P90</label>
+    <input type="radio" id="runtime-p95" name="runtime-fn">
+    <label for="runtime-p95">P95</label>
+    <input type="radio" id="runtime-max" name="runtime-fn">
+    <label for="runtime-max">Max</label>
+  </div>
   <div id="benchmark-selectors"></div>
   <div>
       <canvas id="myChart"></canvas>
@@ -78,8 +90,8 @@
               filteredCommits.map(
                   (c, i) =>
                       selectedBenchmark in filteredCommits[i].benchmarks
-                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
-                              .min()
+                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime",
+                                results => dom.transformRuntimeData(results))
                               .ns_to_s()
                           : NaN);
           const runtimeScatterData = [];
diff --git a/tools/perf/dom.js b/tools/perf/dom.js
index 101c588..fd5cdc9 100644
--- a/tools/perf/dom.js
+++ b/tools/perf/dom.js
@@ -7,6 +7,11 @@
 // DOM references.
 const benchmarkSelectors = document.getElementById('benchmark-selectors');
 const canvas = document.getElementById('myChart');
+const runtimeMin = document.getElementById('runtime-min');
+const runtimeP50 = document.getElementById('runtime-p50');
+const runtimeP90 = document.getElementById('runtime-p90');
+const runtimeP95 = document.getElementById('runtime-p95');
+const runtimeMax = document.getElementById('runtime-max');
 const showMoreLeft = document.getElementById('show-more-left');
 const showLessLeft = document.getElementById('show-less-left');
 const showLessRight = document.getElementById('show-less-right');
@@ -54,6 +59,12 @@
     }
   };
 
+  runtimeMin.onclick = event => chart.update(true, false);
+  runtimeP50.onclick = event => chart.update(true, false);
+  runtimeP90.onclick = event => chart.update(true, false);
+  runtimeP95.onclick = event => chart.update(true, false);
+  runtimeMax.onclick = event => chart.update(true, false);
+
   showMoreLeft.onclick = event => {
     if (zoom.left == 0) {
       return;
@@ -97,6 +108,21 @@
   };
 }
 
+function transformRuntimeData(results) {
+  if (runtimeMin.checked) {
+    return results.min();
+  } else if (runtimeP50.checked) {
+    return results.p(50);
+  } else if (runtimeP90.checked) {
+    return results.p(90);
+  } else if (runtimeP95.checked) {
+    return results.p(95);
+  } else {
+    console.assert(runtimeMax.checked);
+    return results.max();
+  }
+}
+
 function updateBenchmarkColors(benchmarkColors) {
   state.forEachBenchmark(
     benchmark => {
@@ -120,6 +146,7 @@
   canvas: canvas,
   initializeBenchmarkSelectors: initializeBenchmarkSelectors,
   initializeChartNavigation: initializeChartNavigation,
+  transformRuntimeData: transformRuntimeData,
   updateBenchmarkColors: updateBenchmarkColors,
   updateChartNavigation: updateChartNavigation
 };
\ No newline at end of file
diff --git a/tools/perf/extensions.js b/tools/perf/extensions.js
index 1aa20e0..cd5b5cb 100644
--- a/tools/perf/extensions.js
+++ b/tools/perf/extensions.js
@@ -15,9 +15,22 @@
 Array.prototype.avg = function() {
   return this.reduce(function(x, y) { return x + y; }, 0) / this.length;
 };
+Array.prototype.max = function() {
+  return this.reduce(function(x, y) { return x === null ? y : Math.max(x, y); }, null);
+};
 Array.prototype.min = function() {
   return this.reduce(function(x, y) { return x === null ? y : Math.min(x, y); }, null);
 };
+Array.prototype.p = function(value) {
+  const copy = [...this];
+  copy.sort();
+  const index = Math.floor(copy.length * value / 100);
+  if (copy.length % 2 == 0) {
+    return (copy[index - 1] + copy[index]) / 2;
+  } else {
+    return copy[index];
+  }
+};
 Array.prototype.reverseInPlace = function() {
   for (var i = 0; i < Math.floor(this.length / 2); i++) {
     var temp = this[i];
diff --git a/tools/perf/r8.html b/tools/perf/r8.html
index a7da848..23b6c7b 100644
--- a/tools/perf/r8.html
+++ b/tools/perf/r8.html
@@ -6,6 +6,18 @@
   <link rel="stylesheet" href="stylesheet.css">
 </head>
 <body>
+  <div id="runtime-fn">
+    <input type="radio" id="runtime-min" name="runtime-fn" checked>
+    <label for="runtime-min">Min</label>
+    <input type="radio" id="runtime-p50" name="runtime-fn">
+    <label for="runtime-p50">P50</label>
+    <input type="radio" id="runtime-p90" name="runtime-fn">
+    <label for="runtime-p90">P90</label>
+    <input type="radio" id="runtime-p95" name="runtime-fn">
+    <label for="runtime-p95">P95</label>
+    <input type="radio" id="runtime-max" name="runtime-fn">
+    <label for="runtime-max">Max</label>
+  </div>
   <div id="benchmark-selectors"></div>
   <div>
       <canvas id="myChart"></canvas>
@@ -90,8 +102,8 @@
               filteredCommits.map(
                   (c, i) =>
                       selectedBenchmark in filteredCommits[i].benchmarks
-                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
-                              .min()
+                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime",
+                                results => dom.transformRuntimeData(results))
                               .ns_to_s()
                           : NaN);
           const runtimeScatterData = [];
diff --git a/tools/perf/retrace.html b/tools/perf/retrace.html
index 98a6661..7437025 100644
--- a/tools/perf/retrace.html
+++ b/tools/perf/retrace.html
@@ -6,6 +6,18 @@
   <link rel="stylesheet" href="stylesheet.css">
 </head>
 <body>
+  <div id="runtime-fn">
+    <input type="radio" id="runtime-min" name="runtime-fn" checked>
+    <label for="runtime-min">Min</label>
+    <input type="radio" id="runtime-p50" name="runtime-fn">
+    <label for="runtime-p50">P50</label>
+    <input type="radio" id="runtime-p90" name="runtime-fn">
+    <label for="runtime-p90">P90</label>
+    <input type="radio" id="runtime-p95" name="runtime-fn">
+    <label for="runtime-p95">P95</label>
+    <input type="radio" id="runtime-max" name="runtime-fn">
+    <label for="runtime-max">Max</label>
+  </div>
   <div id="benchmark-selectors"></div>
   <div>
       <canvas id="myChart"></canvas>
@@ -58,8 +70,8 @@
               filteredCommits.map(
                   (c, i) =>
                       selectedBenchmark in filteredCommits[i].benchmarks
-                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
-                              .min()
+                          ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime",
+                                results => dom.transformRuntimeData(results))
                               .ns_to_s()
                           : NaN);
           const runtimeScatterData = [];
diff --git a/tools/perf/stylesheet.css b/tools/perf/stylesheet.css
index d458469..de995cf 100644
--- a/tools/perf/stylesheet.css
+++ b/tools/perf/stylesheet.css
@@ -2,4 +2,6 @@
 
 body {
   font-family: 'Roboto';
-}
\ No newline at end of file
+}
+
+div#runtime-fn { float: right; }
diff --git a/tools/perf/utils.js b/tools/perf/utils.js
index 46890d0..13cd09c 100644
--- a/tools/perf/utils.js
+++ b/tools/perf/utils.js
@@ -15,11 +15,14 @@
   return resultsForIteration[resultName];
 }
 
-function getAllResults(benchmark, commit, resultName) {
+function getAllResults(benchmark, commit, resultName, transformation) {
   const result = [];
   const allResults = commit.benchmarks[benchmark].results;
   for (var iteration = 0; iteration < allResults.length; iteration++) {
     result.push(getSingleResult(benchmark, commit, resultName, iteration));
   }
+  if (transformation) {
+    return transformation(result);
+  }
   return result;
 }