Bug 1387125 - Create a performance test to compare XBL with Custom Elements in XUL Documents;r=mrbkap draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 27 Sep 2017 14:39:08 -0700
changeset 685473 6f3af19e5713e9b33894070ef63d257e6096a33e
parent 685447 967c95cee709756596860ed2a3e6ac06ea3a053f
child 737149 5a49e690afcc23366e6fdb497c0059d02a49e414
push id85930
push userbgrinstead@mozilla.com
push dateTue, 24 Oct 2017 15:58:29 +0000
reviewersmrbkap
bugs1387125
milestone58.0a1
Bug 1387125 - Create a performance test to compare XBL with Custom Elements in XUL Documents;r=mrbkap MozReview-Commit-ID: FXhDUc9LfiE
dom/base/test/chrome/chrome.ini
dom/base/test/chrome/file_perf_customelement.html
dom/base/test/chrome/file_perf_customelement.xul
dom/base/test/chrome/file_perf_xbl.xul
dom/base/test/chrome/file_perf_xbl_bindings.xml
dom/base/test/chrome/test_perf_xbl_vs_customelement.xul
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -70,8 +70,15 @@ support-files = ../file_bug357450.js
 [test_nsITextInputProcessor.xul]
 [test_range_getClientRectsAndTexts.html]
 [test_title.xul]
 support-files = file_title.xul
 [test_windowroot.xul]
 [test_swapFrameLoaders.xul]
 [test_groupedSHistory.xul]
 [test_bug1339722.html]
+[test_perf_xbl_vs_customelement.xul]
+skip-if = true # only run this when manually invoked
+support-files =
+  file_perf_customelement.html
+  file_perf_customelement.xul
+  file_perf_xbl.xul
+  file_perf_xbl_bindings.xml
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_customelement.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script>
+    // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-autonomous-example
+
+    class TestCustomElement extends HTMLElement {
+      constructor() {
+        super();
+      }
+
+      connectedCallback() {
+        // Not including any content since with XBL all content is anonymous, and we want
+        // an apples to apples comparison. When we have access to Shadow DOM we can set up a
+        // comparison with content.
+      }
+    }
+
+    customElements.define("test-custom-element", TestCustomElement);
+  </script>
+  <script type="application/javascript">
+  const openerWin = opener.wrappedJSObject;
+  const ok = openerWin.ok.bind(openerWin);
+  const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+  const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+  function run() {
+    let container = document.getElementById("element-container");
+    let iterationTimes = [];
+
+    for (var i = 0; i < NUM_ITERATIONS; i++) {
+      const measure = openerWin.startMeasure("custom elements");
+      for (var j = 0; j < NUM_ELEMENTS; j++) {
+        var element = document.createElement("test-custom-element");
+        container.appendChild(element);
+      }
+      const {duration} = measure.stop();
+      iterationTimes.push(duration);
+      container.innerHTML = '';
+    }
+
+    openerWin.customElementHTMLDone(iterationTimes);
+  }
+  </script>
+  <body onload="run()">
+    <div id="element-container"></div>
+  </body>
+</head>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_customelement.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="run()">
+  <label value="Mozilla Bug 1209621"/>
+  <script type="application/javascript">
+  // https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-autonomous-example
+
+  class TestCustomElement extends XULElement {
+    constructor() {
+      super();
+    }
+
+    connectedCallback() {
+      // Not including any content since with XBL all content is anonymous, and we want
+      // an apples to apples comparison. When we have access to Shadow DOM we can set up a
+      // comparison with content.
+    }
+  }
+
+  customElements.define("test-custom-element", TestCustomElement);
+  </script>
+  <script type="application/javascript"><![CDATA[
+  const openerWin = opener.wrappedJSObject;
+  const ok = openerWin.ok.bind(openerWin);
+  const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+  const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+  function run() {
+    let container = document.getElementById("element-container");
+    let iterationTimes = [];
+
+    for (var i = 0; i < NUM_ITERATIONS; i++) {
+      const measure = openerWin.startMeasure("custom elements");
+      for (var j = 0; j < NUM_ELEMENTS; j++) {
+        var element = document.createElement("test-custom-element");
+        container.appendChild(element);
+      }
+      const {duration} = measure.stop();
+      iterationTimes.push(duration);
+      container.innerHTML = '';
+    }
+
+    openerWin.customElementXULDone(iterationTimes);
+  }
+  ]]></script>
+  <box id="element-container"></box>
+</window>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_xbl.xul
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="run()">
+  <label value="Mozilla Bug 1209621"/>
+  <style xmlns="http://www.w3.org/1999/xhtml">
+  foo {
+    -moz-binding: url("file_perf_xbl_bindings.xml#test");
+  }
+  </style>
+  <script type="application/javascript"><![CDATA[
+  const openerWin = opener.wrappedJSObject;
+  const ok = openerWin.ok.bind(openerWin);
+  const NUM_ITERATIONS = openerWin.NUM_ITERATIONS;
+  const NUM_ELEMENTS = openerWin.NUM_ELEMENTS;
+
+  function run() {
+    let container = document.getElementById("element-container");
+      let iterationTimes = [];
+
+    for (var i = 0; i < NUM_ITERATIONS; i++) {
+      const measure = openerWin.startMeasure("xbl elements");
+      for (var j = 0; j < NUM_ELEMENTS; j++) {
+        var element = document.createElement("foo");
+        container.appendChild(element);
+      }
+      const {duration} = measure.stop();
+      iterationTimes.push(duration);
+      container.innerHTML = '';
+    }
+
+    openerWin.xblDone(iterationTimes);
+  }
+  ]]></script>
+  <box id="element-container"></box>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_perf_xbl_bindings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+<bindings xmlns="http://www.mozilla.org/xbl"
+          xmlns:html="http://www.w3.org/1999/xhtml"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+    <binding id="test">
+    </binding>
+</bindings>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_perf_xbl_vs_customelement.xul
@@ -0,0 +1,155 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1387125
+-->
+<window title="Mozilla Bug 1387125"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1387125"
+     target="_blank">Mozilla Bug 1387125</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 1387125 **/
+
+  Components.utils.import("resource://gre/modules/FileUtils.jsm");
+  Components.utils.import("resource://gre/modules/Services.jsm");
+  window.NUM_ELEMENTS = 1000;
+  window.NUM_ITERATIONS = 5;
+
+  let xblWindow;
+  let customElementXULWindow;
+  let customElementHTMLWindow;
+  let xblTimes;
+  let customElementXULTimes;
+  let customElementHTMLTimes;
+
+  SimpleTest.waitForExplicitFinish();
+
+  function startMeasure(label) {
+    const startLabel = label + "start";
+    performance.mark(startLabel);
+    return {
+      stop: (clear = true) => {
+        performance.measure(label, startLabel);
+        const entries = performance.getEntriesByName(label);
+        if (clear) {
+          performance.clearMeasures(label);
+        }
+        return entries[entries.length - 1];
+      }
+    };
+  }
+
+  function getTimes(times) {
+    times = times.sort();
+    let totalTime = times.reduce((sum, t) => sum + t);
+    let min = times[0];
+    let max = times[times.length - 1];
+    let avg = totalTime / times.length;
+    let median = times.length % 2 !== 0
+      ? times[Math.floor(times.length / 2)]
+      : (times[(times.length / 2) - 1] + times[times.length / 2]) / 2;
+
+    return {
+      avg: Math.round(avg * 100) / 100,
+      median: Math.round(median * 100) / 100,
+      min: Math.round(min * 100) / 100,
+      max: Math.round(max * 100) / 100,
+    };
+  }
+
+  function printTimes(label, times) {
+    let {avg, median, min, max} = times;
+    let avgPerElement = Math.round(avg / NUM_ELEMENTS * 1000) / 1000;
+
+    info(`${label}: On average, it took ${avg} ms to create ${NUM_ELEMENTS} elements (median ${median} ms, max ${max} ms, min ${min} ms, per element ${avgPerElement} ms)`);
+  }
+
+  function wait(time) {
+    return new Promise(resolve => setTimeout(resolve, time));
+  }
+
+  async function xblDone(times) {
+    xblTimes = getTimes(times);
+    xblWindow.close();
+    done();
+  }
+  async function customElementHTMLDone(times) {
+    customElementHTMLTimes = getTimes(times);
+    customElementHTMLWindow.close();
+
+    await wait(500);
+    customElementXULWindow = window.open("file_perf_customelement.xul", "", "chrome,width=600,height=400");
+  }
+  async function customElementXULDone(times) {
+    customElementXULTimes = getTimes(times);
+    customElementXULWindow.close();
+    await wait(500);
+    xblWindow = window.open("file_perf_xbl.xul", "", "chrome,width=600,height=400");
+  }
+  addLoadEvent(async function() {
+    await SpecialPowers.pushPrefEnv({ set: [
+      [ "dom.webcomponents.customelements.enabled", true ],
+      [ "dom.webcomponents.enabled", true ],
+    ]});
+
+    // From https://github.com/devtools-html/perf.html/blob/b73eb73df04c7df51464fa50eeadef3dc7f5d4e2/docs/gecko-profile-format.md#L21
+    const settings = {
+      entries: 100000000,
+      interval: 1,
+      features: ["js", "stackwalk", "threads", "leaf"],
+      threads: ["GeckoMain", "Compositor"]
+    };
+    Services.profiler.StartProfiler(
+      settings.entries,
+      settings.interval,
+      settings.features,
+      settings.features.length,
+      settings.threads,
+      settings.threads.length
+    );
+
+    info("Profiler has started");
+    await wait(500);
+
+    customElementHTMLWindow = window.open("file_perf_customelement.html", "", "chrome,width=600,height=400");
+  });
+
+  async function done() {
+    ok(true, "done");
+
+    printTimes("XBL", xblTimes);
+    printTimes("CUSTOM ELEMENTS (XUL)", customElementXULTimes);
+    printTimes("CUSTOM ELEMENTS (HTML)", customElementHTMLTimes);
+
+    let file = FileUtils.getFile("TmpD", [`test_perf_xbl_vs_customelement_${Date.now()}.json`]);
+    await Services.profiler.dumpProfileToFileAsync(file.path);
+    Services.profiler.StopProfiler();
+
+info(`
+
+SAVING PROFILE: ${file.path}
+
+To upload the profile, run the following commands:
+
+  gzip ${file.path}
+  curl 'https://profile-store.appspot.com/compressed-store' --compressed --data-binary @${file.path}.gz | awk '{print "Hosted at: https://perf-html.io/public/"$1}'
+
+
+`);
+
+
+    SimpleTest.finish();
+  }
+
+  ]]>
+  </script>
+</window>