[MutationObservers] Microbenchmarks for appendChild, removeChild, and innerHTML
authoradamk@chromium.org <adamk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2011 18:39:39 +0000 (18:39 +0000)
committeradamk@chromium.org <adamk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Nov 2011 18:39:39 +0000 (18:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=71939

Reviewed by Ojan Vafai.

These benchmarks time both the DOM mutations themselves and the time taken
to call the MutationCallback. This measurement is achieved by
executing the code entirely within the MutationCallback itself.

* Mutation/append-child-deep.html: Added. Covers appendChild in a 200-node-deep tree with subtree observation.
* Mutation/append-child.html: Added. Covers appendChild in a flat tree.
* Mutation/inner-html.html: Added. Covers innerHTML adding and removing many nodes at a time.
* Mutation/remove-child-deep.html: Added. Covers removeChild in a 200-node-deep tree with subtree observation.
* Mutation/remove-child.html: Added. Covers removeChild in a flat tree.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@99879 268f45cc-cd09-0410-ab3c-d52691b4dbfc

PerformanceTests/ChangeLog
PerformanceTests/Mutation/append-child-deep.html [new file with mode: 0644]
PerformanceTests/Mutation/append-child.html [new file with mode: 0644]
PerformanceTests/Mutation/inner-html.html [new file with mode: 0644]
PerformanceTests/Mutation/remove-child-deep.html [new file with mode: 0644]
PerformanceTests/Mutation/remove-child.html [new file with mode: 0644]

index 3e5bb46..f1c01b8 100644 (file)
@@ -1,3 +1,20 @@
+2011-11-09  Adam Klein  <adamk@chromium.org>
+
+        [MutationObservers] Microbenchmarks for appendChild, removeChild, and innerHTML
+        https://bugs.webkit.org/show_bug.cgi?id=71939
+
+        Reviewed by Ojan Vafai.
+
+        These benchmarks time both the DOM mutations themselves and the time taken
+        to call the MutationCallback. This measurement is achieved by
+        executing the code entirely within the MutationCallback itself.
+
+        * Mutation/append-child-deep.html: Added. Covers appendChild in a 200-node-deep tree with subtree observation.
+        * Mutation/append-child.html: Added. Covers appendChild in a flat tree.
+        * Mutation/inner-html.html: Added. Covers innerHTML adding and removing many nodes at a time.
+        * Mutation/remove-child-deep.html: Added. Covers removeChild in a 200-node-deep tree with subtree observation.
+        * Mutation/remove-child.html: Added. Covers removeChild in a flat tree.
+
 2011-10-25  Adam Barth  <abarth@webkit.org>
 
         EventTargetFactory.in is not sorted
diff --git a/PerformanceTests/Mutation/append-child-deep.html b/PerformanceTests/Mutation/append-child-deep.html
new file mode 100644 (file)
index 0000000..b115f43
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<body>
+<pre id="log"></pre>
+<script src="../Parser/resources/runner.js"></script>
+<div id="sandbox" style="display:none"></div>
+<script>
+var sandbox = document.getElementById('sandbox');
+var node = sandbox;
+for (var i = 0; i < 200; ++i)
+    node = node.appendChild(document.createElement('div'));
+var elems = [];
+for (var i = 0; i < 50000; ++i)
+    elems[i] = document.createElement('div');
+var observing = false;
+
+var observer = new WebKitMutationObserver(listener);
+var tickledSpan = document.createElement('span');
+observer.observe(tickledSpan, {attributes: true});
+
+function resetState() {
+    window.start = null;
+    window.numRuns = 25;
+    window.times = [];
+}
+
+function runAgain() {
+    tickledSpan.setAttribute('data-foo', numRuns);
+}
+
+function listener(mutations) {
+    if (start) {
+        var time = Date.now() - start;
+        times.push(time);
+        log(time);
+    }
+    if (numRuns-- >= 0) {
+        runAgain();
+        if (observing)
+            observer.disconnect()
+        node.innerHTML = '';
+        if (observing)
+            observer.observe(sandbox, {childList: true, subtree: true});
+        start = Date.now();
+        for (var i = 0; i < elems.length; ++i)
+            node.appendChild(elems[i]);
+    } else {
+        logStatistics(times);
+        if (!observing) {
+            observing = true;
+            resetState();
+            log('\n------------\n');
+            log('Running ' + numRuns + ' times with observation');
+            setTimeout(runAgain, 0);
+        }
+    }
+}
+
+resetState();
+log('Running ' + numRuns + ' times without observation');
+window.addEventListener('load', runAgain);
+</script>
+</body>
diff --git a/PerformanceTests/Mutation/append-child.html b/PerformanceTests/Mutation/append-child.html
new file mode 100644 (file)
index 0000000..d692ae1
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<body>
+<pre id="log"></pre>
+<script src="../Parser/resources/runner.js"></script>
+<div id="sandbox" style="display:none"></div>
+<script>
+var sandbox = document.getElementById('sandbox');
+var observing = false;
+
+var elems = [];
+for (var i = 0; i < 50000; ++i)
+    elems[i] = document.createElement('div');
+var observer = new WebKitMutationObserver(listener);
+var tickledSpan = document.createElement('span');
+observer.observe(tickledSpan, {attributes: true});
+
+function resetState() {
+    window.start = null;
+    window.numRuns = 25;
+    window.times = [];
+}
+
+function runAgain() {
+    tickledSpan.setAttribute('data-foo', numRuns);
+}
+
+function listener(mutations) {
+    if (start) {
+        var time = Date.now() - start;
+        times.push(time);
+        log(time);
+    }
+    if (numRuns-- >= 0) {
+        runAgain();
+        if (observing)
+            observer.disconnect()
+        sandbox.innerHTML = '';
+        if (observing)
+            observer.observe(sandbox, {childList: true});
+        start = Date.now();
+        for (var i = 0; i < elems.length; ++i)
+            sandbox.appendChild(elems[i]);
+    } else {
+        logStatistics(times);
+        if (!observing) {
+            observing = true;
+            resetState();
+            log('\n------------\n');
+            log('Running ' + numRuns + ' times with observation');
+            setTimeout(runAgain, 0);
+        }
+    }
+}
+
+resetState();
+log('Running ' + numRuns + ' times without observation');
+window.addEventListener('load', runAgain);
+</script>
+</body>
diff --git a/PerformanceTests/Mutation/inner-html.html b/PerformanceTests/Mutation/inner-html.html
new file mode 100644 (file)
index 0000000..6821c95
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<body>
+<pre id="log"></pre>
+<script src="../Parser/resources/runner.js"></script>
+<div id="sandbox" style="display:none"></div>
+<script>
+var sandbox = document.getElementById('sandbox');
+var observing = false;
+for (var i = 0; i < 1000; ++i)
+    sandbox.appendChild(document.createElement('div'));
+var html = sandbox.innerHTML;
+
+var observer = new WebKitMutationObserver(listener);
+var tickledSpan = document.createElement('span');
+observer.observe(tickledSpan, {attributes: true});
+
+function resetState() {
+    window.start = null;
+    window.numRuns = 25;
+    window.times = [];
+}
+
+function runAgain() {
+    tickledSpan.setAttribute('data-foo', numRuns);
+}
+
+function listener(mutations) {
+    if (start) {
+        var time = Date.now() - start;
+        times.push(time);
+        log(time);
+    }
+    if (numRuns-- >= 0) {
+        runAgain();
+        start = Date.now();
+        for (var i = 0; i < 100; ++i)
+            sandbox.innerHTML = html;
+    } else {
+        logStatistics(times);
+        if (!observing) {
+            observer.observe(sandbox, {childList: true});
+            observing = true;
+            resetState();
+            log('\n------------\n');
+            log('Running ' + numRuns + ' times with observation');
+            setTimeout(runAgain, 0);
+        }
+    }
+}
+
+resetState();
+log('Running ' + numRuns + ' times without observation');
+window.addEventListener('load', runAgain);
+</script>
+</body>
diff --git a/PerformanceTests/Mutation/remove-child-deep.html b/PerformanceTests/Mutation/remove-child-deep.html
new file mode 100644 (file)
index 0000000..b702adb
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<body>
+<pre id="log"></pre>
+<script src="../Parser/resources/runner.js"></script>
+<div id="sandbox" style="display:none"></div>
+<script>
+var sandbox = document.getElementById('sandbox');
+var node = sandbox;
+for (var i = 0; i < 200; ++i)
+    node = node.appendChild(document.createElement('div'));
+var observing = false;
+
+var observer = new WebKitMutationObserver(listener);
+var tickledSpan = document.createElement('span');
+observer.observe(tickledSpan, {attributes: true});
+
+function resetState() {
+    window.start = null;
+    window.numRuns = 25;
+    window.times = [];
+}
+
+function runAgain() {
+    tickledSpan.setAttribute('data-foo', numRuns);
+}
+
+function hideFromObservation(func) {
+    if (observing)
+        observer.disconnect();
+    func();
+    if (observing)
+        observer.observe(sandbox, {childList: true, subtree: true});
+}
+
+function listener(mutations) {
+    if (start) {
+        var time = Date.now() - start;
+        times.push(time);
+        log(time);
+    }
+    if (numRuns-- >= 0) {
+        runAgain();
+        hideFromObservation(function() {
+            for (var i = 0; i < 50000; ++i)
+                node.appendChild(document.createElement('div'));
+        });
+        start = Date.now();
+        while (node.firstChild)
+            node.removeChild(node.firstChild);
+    } else {
+        logStatistics(times);
+        if (!observing) {
+            observing = true;
+            resetState();
+            log('\n------------\n');
+            log('Running ' + numRuns + ' times with observation');
+            setTimeout(runAgain, 0);
+        }
+    }
+}
+
+resetState();
+log('Running ' + numRuns + ' times without observation');
+window.addEventListener('load', runAgain);
+</script>
+</body>
diff --git a/PerformanceTests/Mutation/remove-child.html b/PerformanceTests/Mutation/remove-child.html
new file mode 100644 (file)
index 0000000..38673d1
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<body>
+<pre id="log"></pre>
+<script src="../Parser/resources/runner.js"></script>
+<div id="sandbox" style="display:none"></div>
+<script>
+var sandbox = document.getElementById('sandbox');
+var observing = false;
+
+var observer = new WebKitMutationObserver(listener);
+var tickledSpan = document.createElement('span');
+observer.observe(tickledSpan, {attributes: true});
+
+function resetState() {
+    window.start = null;
+    window.numRuns = 25;
+    window.times = [];
+}
+
+function runAgain() {
+    tickledSpan.setAttribute('data-foo', numRuns);
+}
+
+function hideFromObservation(func) {
+    if (observing)
+        observer.disconnect()
+    func();
+    if (observing)
+        observer.observe(sandbox, {childList: true});
+}
+
+function listener(mutations) {
+    if (start) {
+        var time = Date.now() - start;
+        times.push(time);
+        log(time);
+    }
+    if (numRuns-- >= 0) {
+        runAgain();
+        hideFromObservation(function() {
+            for (var i = 0; i < 50000; ++i)
+                sandbox.appendChild(document.createElement('div'));
+        });
+        start = Date.now();
+        while (sandbox.firstChild)
+            sandbox.removeChild(sandbox.firstChild);
+    } else {
+        logStatistics(times);
+        if (!observing) {
+            observer.observe(sandbox, {childList: true});
+            observing = true;
+            resetState();
+            log('\n------------\n');
+            log('Running ' + numRuns + ' times with observation');
+            setTimeout(runAgain, 0);
+        }
+    }
+}
+
+resetState();
+log('Running ' + numRuns + ' times without observation');
+window.addEventListener('load', runAgain);
+</script>
+</body>