Bug 1143508 (part 2) - Optimize pair returning from makeNodeElement() in JSDOMParser. r=bnicholson. a=readinglist
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 16 Mar 2015 15:13:16 -0700
changeset 248427 f2e7171388b1c3ba174393506f56001926d81de7
parent 248426 3af32770b0f3628c0a4e17ae4518bb2a34b7dd31
child 248428 1155419379269a1a638c772e92421aafd84bbead
push id7837
push userjwein@mozilla.com
push dateFri, 27 Mar 2015 00:27:16 +0000
treeherdermozilla-aurora@cb0db44ce60e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson, readinglist
bugs1143508
milestone38.0a2
Bug 1143508 (part 2) - Optimize pair returning from makeNodeElement() in JSDOMParser. r=bnicholson. a=readinglist In one workload this avoids allocating 2.3 MB of short-lived arrays.
toolkit/components/reader/JSDOMParser.js
--- a/toolkit/components/reader/JSDOMParser.js
+++ b/toolkit/components/reader/JSDOMParser.js
@@ -627,16 +627,21 @@
 
     // In makeElementNode() we build up many strings one char at a time. Using
     // += for this results in lots of short-lived intermediate strings. It's
     // better to build an array of single-char strings and then join() them
     // together at the end. And reusing a single array (i.e. |this.strBuf|)
     // over and over for this purpose uses less memory than using a new array
     // for each string.
     this.strBuf = [];
+
+    // Similarly, we reuse this array to return the two arguments from
+    // makeElementNode(), which saves us from having to allocate a new array
+    // every time.
+    this.retPair = [];
   };
 
   JSDOMParser.prototype = {
     /**
      * Look at the next character without advancing the index.
      */
     peekNext: function () {
       return this.html[this.currentChar];
@@ -707,59 +712,61 @@
     /**
      * Parses and returns an Element node. This is called after a '<' has been
      * read.
      *
      * @returns an array; the first index of the array is the parsed node;
      *          the second index is a boolean indicating whether this is a void
      *          Element
      */
-    makeElementNode: function () {
+    makeElementNode: function (retPair) {
       let c = this.nextChar();
 
       // Read the Element tag name
       let strBuf = this.strBuf;
       strBuf.length = 0;
       while (c !== " " && c !== ">" && c !== "/") {
         if (c === undefined)
-          return null;
+          return false;
         strBuf.push(c);
         c = this.nextChar();
       }
       let tag = strBuf.join('');
 
       if (!tag)
-        return null;
+        return false;
 
       let node = new Element(tag);
 
       // Read Element attributes
       while (c !== "/" && c !== ">") {
         if (c === undefined)
-          return null;
+          return false;
         while (this.match(" "));
         c = this.nextChar();
         if (c !== "/" && c !== ">") {
           --this.currentChar;
           this.readAttribute(node);
         }
       }
 
       // If this is a self-closing tag, read '/>'
       let closed = tag in voidElems;
       if (c === "/") {
         closed = true;
         c = this.nextChar();
         if (c !== ">") {
           error("expected '>'");
-          return null;
+          return false;
         }
       }
 
-      return [node, closed];
+      retPair[0] = node;
+      retPair[1] = closed;
+      return true
     },
 
     /**
      * If the current input matches this string, advance the input index;
      * otherwise, do nothing.
      *
      * @returns whether input matched string
      */
@@ -850,21 +857,22 @@
       // If we're reading a closing tag, return null. This means we've reached
       // the end of this set of child nodes.
       if (c === "/") {
         --this.currentChar;
         return null;
       }
 
       // Otherwise, we're looking at an Element node
-      let result = this.makeElementNode();
-      if (result === null)
+      let result = this.makeElementNode(this.retPair);
+      if (!result)
         return null;
 
-      let [node, closed] = result;
+      let node = this.retPair[0];
+      let closed = this.retPair[1];
       let localName = node.localName;
 
       // If this isn't a void Element, read its child nodes
       if (!closed) {
         this.readChildren(node);
         let closingTag = "</" + localName + ">";
         if (!this.match(closingTag)) {
           error("expected '" + closingTag + "'");