You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2013/12/20 05:13:26 UTC

spec commit: Use setImmediate in exec bridge benchmark instead of setTimeout.

Updated Branches:
  refs/heads/master 7e771d58f -> f01dcadcf


Use setImmediate in exec bridge benchmark instead of setTimeout.

The artificial delay of setTimeout turned out to be affecting timings.


Project: http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/commit/f01dcadc
Tree: http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/tree/f01dcadc
Diff: http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/diff/f01dcadc

Branch: refs/heads/master
Commit: f01dcadcfabca3c80c6ff9073464d4cdfe989562
Parents: 7e771d5
Author: Andrew Grieve <ag...@chromium.org>
Authored: Thu Dec 19 23:12:14 2013 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Thu Dec 19 23:12:14 2013 -0500

----------------------------------------------------------------------
 benchmarks/index.html      |   9 +-
 benchmarks/setImmediate.js | 218 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 223 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/blob/f01dcadc/benchmarks/index.html
----------------------------------------------------------------------
diff --git a/benchmarks/index.html b/benchmarks/index.html
index 91b3fea..8941167 100644
--- a/benchmarks/index.html
+++ b/benchmarks/index.html
@@ -27,8 +27,8 @@
     <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
     <title>Cordova Mobile Spec</title>
     <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
-    <script type="text/javascript" charset="utf-8" src="../cordova-incl.js"></script>      
-      
+    <script src="../cordova-incl.js"></script>
+    <script src="setImmediate.js"></script>
 <script>
     var exec = cordova.require('cordova/exec'),
         appLogElem = null,
@@ -68,7 +68,7 @@
             var elapsedMs = new Date - startTime;
             if (elapsedMs < durationMs) {
                 if (useSetTimeout) {
-                    setTimeout(echoMessage, 0);
+                    setImmediate(echoMessage);
                 } else {
                     echoMessage();
                 }
@@ -148,12 +148,13 @@
       <li>ENABLE_LOCATION_CHANGE_EXEC_MODE = true
       <li>DISABLE_EXEC_CHAINING = true
     </ul>
+    As of iOS 7.0.2, Andrew found that the iframe bridge is still the fastest on iOS.
     <fieldset>
         <legend>Settings</legend>
         <label>Test Duration: <select id="test-duration"><option>1 Second</option><option>5 Seconds</option></select><br></label>
         <label style="display:none">JS-&gt;Native Bridge Mode: <select id="js-native-modes"></select><br></label>
         <label style="display:none">Native-&gt;JS Bridge Mode: <select id="native-js-modes"></select><br></label>
-        <label><input type="checkbox" id="use-setTimeout"> Force async JS-&gt;Native</label><br>
+        <label><input type="checkbox" id="use-setTimeout"> Force async JS-&gt;Native (avoids evalAndFetch optimization on iOS)</label><br>
         <label><input type="checkbox" id="async-echo"> Force async Native-&gt;JS</label><br>
         <label>Payload size (in 100s of bytes) <input id="payload-size" value="5" style="width:100px"></label><br>
         <button onclick="benchExec()">Benchmark exec</button><br>

http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/blob/f01dcadc/benchmarks/setImmediate.js
----------------------------------------------------------------------
diff --git a/benchmarks/setImmediate.js b/benchmarks/setImmediate.js
new file mode 100644
index 0000000..f034bb4
--- /dev/null
+++ b/benchmarks/setImmediate.js
@@ -0,0 +1,218 @@
+(function (global, undefined) {
+    "use strict";
+
+    var tasks = (function () {
+        function Task(handler, args) {
+            this.handler = handler;
+            this.args = args;
+        }
+        Task.prototype.run = function () {
+            // See steps in section 5 of the spec.
+            if (typeof this.handler === "function") {
+                // Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though:
+                // http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
+                this.handler.apply(undefined, this.args);
+            } else {
+                var scriptSource = "" + this.handler;
+                /*jshint evil: true */
+                eval(scriptSource);
+            }
+        };
+
+        var nextHandle = 1; // Spec says greater than zero
+        var tasksByHandle = {};
+        var currentlyRunningATask = false;
+
+        return {
+            addFromSetImmediateArguments: function (args) {
+                var handler = args[0];
+                var argsToHandle = Array.prototype.slice.call(args, 1);
+                var task = new Task(handler, argsToHandle);
+
+                var thisHandle = nextHandle++;
+                tasksByHandle[thisHandle] = task;
+                return thisHandle;
+            },
+            runIfPresent: function (handle) {
+                // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
+                // So if we're currently running a task, we'll need to delay this invocation.
+                if (!currentlyRunningATask) {
+                    var task = tasksByHandle[handle];
+                    if (task) {
+                        currentlyRunningATask = true;
+                        try {
+                            task.run();
+                        } finally {
+                            delete tasksByHandle[handle];
+                            currentlyRunningATask = false;
+                        }
+                    }
+                } else {
+                    // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
+                    // "too much recursion" error.
+                    global.setTimeout(function () {
+                        tasks.runIfPresent(handle);
+                    }, 0);
+                }
+            },
+            remove: function (handle) {
+                delete tasksByHandle[handle];
+            }
+        };
+    }());
+
+    function canUseNextTick() {
+        // Don't get fooled by e.g. browserify environments.
+        return typeof process === "object" &&
+               Object.prototype.toString.call(process) === "[object process]";
+    }
+
+    function canUseMessageChannel() {
+        return !!global.MessageChannel;
+    }
+
+    function canUsePostMessage() {
+        // The test against `importScripts` prevents this implementation from being installed inside a web worker,
+        // where `global.postMessage` means something completely different and can't be used for this purpose.
+
+        if (!global.postMessage || global.importScripts) {
+            return false;
+        }
+
+        var postMessageIsAsynchronous = true;
+        var oldOnMessage = global.onmessage;
+        global.onmessage = function () {
+            postMessageIsAsynchronous = false;
+        };
+        global.postMessage("", "*");
+        global.onmessage = oldOnMessage;
+
+        return postMessageIsAsynchronous;
+    }
+
+    function canUseReadyStateChange() {
+        return "document" in global && "onreadystatechange" in global.document.createElement("script");
+    }
+
+    function installNextTickImplementation(attachTo) {
+        attachTo.setImmediate = function () {
+            var handle = tasks.addFromSetImmediateArguments(arguments);
+
+            process.nextTick(function () {
+                tasks.runIfPresent(handle);
+            });
+
+            return handle;
+        };
+    }
+
+    function installMessageChannelImplementation(attachTo) {
+        var channel = new global.MessageChannel();
+        channel.port1.onmessage = function (event) {
+            var handle = event.data;
+            tasks.runIfPresent(handle);
+        };
+        attachTo.setImmediate = function () {
+            var handle = tasks.addFromSetImmediateArguments(arguments);
+
+            channel.port2.postMessage(handle);
+
+            return handle;
+        };
+    }
+
+    function installPostMessageImplementation(attachTo) {
+        // Installs an event handler on `global` for the `message` event: see
+        // * https://developer.mozilla.org/en/DOM/window.postMessage
+        // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
+
+        var MESSAGE_PREFIX = "com.bn.NobleJS.setImmediate" + Math.random();
+
+        function isStringAndStartsWith(string, putativeStart) {
+            return typeof string === "string" && string.substring(0, putativeStart.length) === putativeStart;
+        }
+
+        function onGlobalMessage(event) {
+            // This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to
+            // avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a
+            // (randomly generated) unpredictable identifying prefix is present.
+            if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
+                var handle = event.data.substring(MESSAGE_PREFIX.length);
+                tasks.runIfPresent(handle);
+            }
+        }
+        if (global.addEventListener) {
+            global.addEventListener("message", onGlobalMessage, false);
+        } else {
+            global.attachEvent("onmessage", onGlobalMessage);
+        }
+
+        attachTo.setImmediate = function () {
+            var handle = tasks.addFromSetImmediateArguments(arguments);
+
+            // Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously
+            // invoking our onGlobalMessage listener above.
+            global.postMessage(MESSAGE_PREFIX + handle, "*");
+
+            return handle;
+        };
+    }
+
+    function installReadyStateChangeImplementation(attachTo) {
+        attachTo.setImmediate = function () {
+            var handle = tasks.addFromSetImmediateArguments(arguments);
+
+            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
+            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
+            var scriptEl = global.document.createElement("script");
+            scriptEl.onreadystatechange = function () {
+                tasks.runIfPresent(handle);
+
+                scriptEl.onreadystatechange = null;
+                scriptEl.parentNode.removeChild(scriptEl);
+                scriptEl = null;
+            };
+            global.document.documentElement.appendChild(scriptEl);
+
+            return handle;
+        };
+    }
+
+    function installSetTimeoutImplementation(attachTo) {
+        attachTo.setImmediate = function () {
+            var handle = tasks.addFromSetImmediateArguments(arguments);
+
+            global.setTimeout(function () {
+                tasks.runIfPresent(handle);
+            }, 0);
+
+            return handle;
+        };
+    }
+
+    if (!global.setImmediate) {
+        // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
+        var attachTo = typeof Object.getPrototypeOf === "function" && "setTimeout" in Object.getPrototypeOf(global) ?
+                          Object.getPrototypeOf(global)
+                        : global;
+
+        if (canUseNextTick()) {
+            // For Node.js before 0.9
+            installNextTickImplementation(attachTo);
+        } else if (canUsePostMessage()) {
+            // For non-IE10 modern browsers
+            installPostMessageImplementation(attachTo);
+        } else if (canUseMessageChannel()) {
+            // For web workers, where supported
+            installMessageChannelImplementation(attachTo);
+        } else if (canUseReadyStateChange()) {
+            // For IE 6–8
+            installReadyStateChangeImplementation(attachTo);
+        } else {
+            // For older browsers
+            installSetTimeoutImplementation(attachTo);
+        }
+
+        attachTo.clearImmediate = tasks.remove;
+    }
+}(typeof global === "object" && global ? global : this));