summaryrefslogtreecommitdiffstats
path: root/testing/talos/talos/scripts/talos-debug.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/talos/talos/scripts/talos-debug.js')
-rw-r--r--testing/talos/talos/scripts/talos-debug.js215
1 files changed, 215 insertions, 0 deletions
diff --git a/testing/talos/talos/scripts/talos-debug.js b/testing/talos/talos/scripts/talos-debug.js
new file mode 100644
index 0000000000..90c102071e
--- /dev/null
+++ b/testing/talos/talos/scripts/talos-debug.js
@@ -0,0 +1,215 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/** ****************************************************************
+ * window.talosDebug provides some statistical functions
+ * (sum, average, median, stddev) and a tpRecordTime method which
+ * reports some statistics about the data set, including detected
+ * stability point of the data (detecting first few noisy elements).
+ * If tpRecordTime doesn't exist globally (i.e. running outside of
+ * talos, e.g. in a browser), then it points it to window.talosDebug.tpRecordTime
+ * Can be controlled by few properties (disable display, hardcoded
+ * stability point, etc)
+ *
+ * talos-debug.js: Bug 849558
+ *****************************************************************/
+window.talosDebug = {
+ // Optional config properties
+ disabled: false,
+ ignore: -1, // Number of items to ignore at the begining of the set. -1 for auto-detect.
+ displayData: false, // If true, will also display all the data points.
+ fixed: 2, // default floating point digits for display.
+ // End of config
+
+ sum(values) {
+ return values.reduce(function (a, b) {
+ return a + b;
+ });
+ },
+
+ average(values) {
+ var d = window.talosDebug;
+ return values.length ? d.sum(values) / values.length : 999999;
+ },
+
+ median(values) {
+ var clone = values.slice(0);
+ var sorted = clone.sort(function (a, b) {
+ // eslint-disable-next-line no-nested-ternary
+ return a > b ? 1 : a < b ? -1 : 0;
+ });
+ var len = values.length;
+ if (!len) {
+ return 999999;
+ }
+ if (len % 2) {
+ // We have a middle number
+ return sorted[(len - 1) / 2];
+ }
+ return (sorted[len / 2] + sorted[len / 2 - 1]) / 2; // Average value of the two middle items.
+ },
+
+ stddev(values, avg) {
+ if (values.length <= 1) {
+ return 0;
+ }
+
+ return Math.sqrt(
+ values
+ .map(function (v) {
+ return Math.pow(v - avg, 2);
+ })
+ .reduce(function (a, b) {
+ return a + b;
+ }) /
+ (values.length - 1)
+ );
+ },
+
+ // Estimate the number of warmup iterations of this data set (in a completely unscrientific way).
+ // returns -1 if could not find a stability point (supposedly meaning that the values
+ // are still in the process of convergence).
+ // Algorithm (not based on a scientific method which I know of):
+ // Target: Find a locally noisy value after the values show stability
+ // (stddev of a moving window stops decreasing), while trying to make sure
+ // that it wasn't a glitch and the next 2 floating window stddev are, indeed, not
+ // still decreasing.
+ // Do the above for few different window widths (3-7), get the maximum result of those.
+ // Now we should have an index which is at least the 2nd value within the stable part.
+ // We get stddev for that index..end (baseStd), and then go backwards as long as stddev is
+ // decreasing or within ~1% of baseStd, and return the earliest index for which it is.
+ detectWarmup(values) {
+ var MIN_WIDTH = 3;
+ var MAX_WIDTH = 7;
+ var d = window.talosDebug;
+
+ function windowStd(from, winSize) {
+ var win = values.slice(from, from + winSize);
+ return d.stddev(win, d.median(values));
+ }
+
+ var stableFrom = -1;
+ var overallAverage = d.median(values);
+ // var overallStd = d.stddev(values, overallAverage);
+ for (var winWidth = MIN_WIDTH; winWidth < MAX_WIDTH + 1; winWidth++) {
+ var prevStd = windowStd(0, winWidth);
+ for (var i = 1; i < values.length - winWidth - 3; i++) {
+ var w0 = windowStd(i + 0, winWidth);
+ var w1 = windowStd(i + 1, winWidth);
+ var w2 = windowStd(i + 2, winWidth);
+ // var currWindow = values.slice(i, i + winWidth);
+ if (w0 >= prevStd && !(w1 < w0 && w2 < w1)) {
+ if (i > stableFrom) {
+ stableFrom = i;
+ }
+ break;
+ }
+ prevStd = w0;
+ }
+ }
+
+ function withinPercentage(base, value, percentage) {
+ return Math.abs(base - value) < (base * percentage) / 100;
+ }
+ // Now go backwards as long as stddev decreases or doesn't increase in more than 1%.
+ var baseStd = d.stddev(values.slice(stableFrom), overallAverage);
+ var len = values.length;
+ while (true) {
+ var current = d.stddev(values.slice(stableFrom - 1), overallAverage);
+ // 100/len : the more items we have, the more sensitively we compare:
+ // for 100 items, we're allowing 2% over baseStd.
+ if (
+ stableFrom > 0 &&
+ (current < baseStd ||
+ withinPercentage(baseStd, current, 200 / (len ? len : 100)))
+ ) {
+ stableFrom--;
+ } else {
+ break;
+ }
+ }
+
+ return stableFrom;
+ },
+
+ statsDisplay(collection) {
+ var d = window.talosDebug;
+ var std = d.stddev(collection, d.average(collection));
+ var avg = d.average(collection);
+ var med = d.median(collection);
+ return (
+ "Count: " +
+ collection.length +
+ "\nAverage: " +
+ avg.toFixed(d.fixed) +
+ "\nMedian: " +
+ med.toFixed(d.fixed) +
+ "\nStdDev: " +
+ std.toFixed(d.fixed) +
+ " (" +
+ ((100 * std) / (avg ? avg : 1)).toFixed(d.fixed) +
+ "% of average)"
+ );
+ },
+
+ tpRecordTime(dataCSV) {
+ var d = window.talosDebug;
+ if (d.disabled) {
+ return;
+ }
+
+ var collection = ("" + dataCSV).split(",").map(function (item) {
+ return parseFloat(item);
+ });
+ var res = d.statsDisplay(collection);
+
+ var warmup = d.ignore >= 0 ? d.ignore : d.detectWarmup(collection);
+ if (warmup >= 0) {
+ res +=
+ "\n\nWarmup " +
+ (d.ignore >= 0 ? "requested: " : "auto-detected: ") +
+ warmup;
+ var warmedUp = collection.slice(warmup);
+ if (warmup) {
+ res +=
+ "\nAfter ignoring first " +
+ (warmup > 1 ? warmup + " items" : "item") +
+ ":\n";
+ res += d.statsDisplay(warmedUp);
+ }
+ } else {
+ res += "\n\nWarmup auto-detection: Failed.";
+ }
+
+ if (d.displayData) {
+ var disp = collection.map(function (item) {
+ return item.toFixed(d.fixed);
+ });
+ if (warmup >= 0) {
+ disp.splice(warmup, 0, "[warmed-up:]");
+ }
+ res += "\n\nRecorded:\n" + disp.join(", ");
+
+ res += "\n\nStddev from item NN to last:\n";
+ disp = collection.map(function (value, index) {
+ return d
+ .stddev(collection.slice(index), d.average(collection.slice(index)))
+ .toFixed(d.fixed);
+ });
+ if (warmup >= 0) {
+ disp.splice(warmup, 0, "[warmed-up:]");
+ }
+ res += disp.join(", ");
+ } else {
+ res += "\n\n[set window.talosDebug.displayData=true for data]";
+ }
+
+ alert(res);
+ },
+};
+
+// Enable testing outside of talos by providing an alternative report function.
+if (typeof tpRecordTime === "undefined") {
+ tpRecordTime = window.talosDebug.tpRecordTime;
+}