Web Inspector: serialize edge counts instead of indexes in heap snapshot
authorloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Jun 2012 17:08:19 +0000 (17:08 +0000)
committerloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Jun 2012 17:08:19 +0000 (17:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=88324

The serialized node structure currently holds an index
of its first containment edge in the edges array.
The index can be quite big (up to 7 digits for large snapshots).
The patch changes the serialization format to pass
node containment edge count instead. For most nodes the count
is just a single digit number.
This reduces serialized snapshot size and therefore its transfer time.

Patch by Alexei Filippov <alexeif@chromium.org> on 2012-06-05
Reviewed by Yury Semikhatsky.

PerformanceTests:

* inspector/heap-snapshot-performance-test.js:

Source/WebCore:

* inspector/front-end/HeapSnapshot.js:
(WebInspector.HeapSnapshotNode.prototype._edgeIndexesStart):
(WebInspector.HeapSnapshotNode.prototype._edgeIndexesEnd):
(WebInspector.HeapSnapshotNode.prototype._ordinal):
(WebInspector.HeapSnapshotNodeIterator):
(WebInspector.HeapSnapshot.prototype._init):
(WebInspector.HeapSnapshot.prototype._buildEdgeIndexes):
(WebInspector.HeapSnapshot.prototype._buildRetainers):
(WebInspector.HeapSnapshot.prototype._bfs):
(WebInspector.HeapSnapshot.prototype._buildAggregates):
(WebInspector.HeapSnapshot.prototype._buildPostOrderIndex):
(WebInspector.HeapSnapshot.prototype._buildDominatorTree):
(WebInspector.HeapSnapshot.prototype._markPageOwnedNodes):
(WebInspector.HeapSnapshot.prototype._markQueriableHeapObjects):

LayoutTests:

* inspector/profiler/heap-snapshot-expected.txt:
* inspector/profiler/heap-snapshot-test.js:
(initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockObject):
(initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockRaw):
(initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockWithDOM):
(initialize_HeapSnapshotTest.):
* inspector/profiler/heap-snapshot.html:

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

LayoutTests/ChangeLog
LayoutTests/inspector/profiler/heap-snapshot-expected.txt
LayoutTests/inspector/profiler/heap-snapshot-test.js
LayoutTests/inspector/profiler/heap-snapshot.html
PerformanceTests/ChangeLog
PerformanceTests/inspector/heap-snapshot-performance-test.js
Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/HeapSnapshot.js

index b116957..57d4ba1 100644 (file)
@@ -1,3 +1,26 @@
+2012-06-05  Alexei Filippov  <alexeif@chromium.org>
+
+        Web Inspector: serialize edge counts instead of indexes in heap snapshot
+        https://bugs.webkit.org/show_bug.cgi?id=88324
+
+        The serialized node structure currently holds an index
+        of its first containment edge in the edges array.
+        The index can be quite big (up to 7 digits for large snapshots).
+        The patch changes the serialization format to pass
+        node containment edge count instead. For most nodes the count
+        is just a single digit number.
+        This reduces serialized snapshot size and therefore its transfer time.
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/profiler/heap-snapshot-expected.txt:
+        * inspector/profiler/heap-snapshot-test.js:
+        (initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockObject):
+        (initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockRaw):
+        (initialize_HeapSnapshotTest.InspectorTest.createHeapSnapshotMockWithDOM):
+        (initialize_HeapSnapshotTest.):
+        * inspector/profiler/heap-snapshot.html:
+
 2012-06-05  Arpita Bahuguna  <arpitabahuguna@gmail.com>
 
         CSS 2.1 failure: border-conflict-element-021a
index 84716f6..ebae776 100644 (file)
@@ -13,6 +13,8 @@ Running: heapSnapshotNodeAndEdgeTest
 
 Running: heapSnapshotSimpleTest
 
+Running: heapSnapshotContainmentEdgeIndexesTest
+
 Running: heapSnapshotPostOrderIndexTest
 
 Running: heapSnapshotDominatorsTreeTest
index 2a7e4ce..da94cb7 100644 (file)
@@ -6,9 +6,7 @@ InspectorTest.createHeapSnapshotMockObject = function()
         _rootNodeIndex: 0,
         _nodeTypeOffset: 0,
         _nodeNameOffset: 1,
-        _edgesCountOffset: 2,
-        _firstEdgeIndexOffset: 2,
-        _firstEdgeOffset: 3,
+        _nodeEdgeCountOffset: 2,
         _nodeFieldCount: 3,
         _edgeFieldsCount: 3,
         _edgeTypeOffset: 0,
@@ -30,13 +28,12 @@ InspectorTest.createHeapSnapshotMockObject = function()
         //         -> B (6) -bd- D (12)
         //
         _nodes: [
-            0, 0, 0,    //  0: root
-            1, 1, 6,    //  3: A
-            1, 2, 12,   //  6: B
-            1, 3, 18,   //  9: C
-            1, 4, 21,   // 12: D
-            1, 5, 21,   // 15: E
-            0, 0, 21],  // 18: (extra node)
+            0, 0, 2,    //  0: root
+            1, 1, 2,    //  3: A
+            1, 2, 2,    //  6: B
+            1, 3, 1,    //  9: C
+            1, 4, 0,    // 12: D
+            1, 5, 0],   // 15: E
         _containmentEdges: [
             2,  6, 3,   //  0: shortcut 'a' to node 'A'
             1,  7, 6,   //  3: property 'b' to node 'B'
@@ -45,7 +42,8 @@ InspectorTest.createHeapSnapshotMockObject = function()
             1,  9, 9,   // 12: property 'bc' to node 'C'
             1, 10, 12,  // 15: property 'bd' to node 'D'
             1, 11, 15], // 18: property 'ce' to node 'E'
-        _strings: ["", "A", "B", "C", "D", "E", "a", "b", "ac", "bc", "bd", "ce"]
+        _strings: ["", "A", "B", "C", "D", "E", "a", "b", "ac", "bc", "bd", "ce"],
+        _firstEdgeIndexes: [0, 6, 12, 18, 21, 21, 21]
     };
 };
 
@@ -66,7 +64,7 @@ InspectorTest.createHeapSnapshotMockRaw = function()
     return {
         snapshot: {
             meta: {
-                node_fields: ["type", "name", "id", "self_size", "retained_size", "dominator", "edges_index"],
+                node_fields: ["type", "name", "id", "self_size", "retained_size", "dominator", "edge_count"],
                 node_types: [["hidden", "object"], "", "", "", "", "", ""],
                 edge_fields: ["type", "name_or_index", "to_node"],
                 edge_types: [["element", "property", "shortcut"], "", ""]
@@ -74,12 +72,12 @@ InspectorTest.createHeapSnapshotMockRaw = function()
             node_count: 6,
             edge_count: 7},
         nodes: [
-            0, 0, 1, 0, 20,  0,  0, // root (0)
-            1, 1, 2, 2,  2,  0,  6, // A (7)
-            1, 2, 3, 3,  8,  0, 12, // B (14)
-            1, 3, 4, 4, 10,  0, 18, // C (21)
-            1, 4, 5, 5,  5, 14, 21, // D (28)
-            1, 5, 6, 6,  6, 21, 21],// E (35)
+            0, 0, 1, 0, 20,  0, 2,  // root (0)
+            1, 1, 2, 2,  2,  0, 2,  // A (7)
+            1, 2, 3, 3,  8,  0, 2,  // B (14)
+            1, 3, 4, 4, 10,  0, 1 // C (21)
+            1, 4, 5, 5,  5, 14, 0,  // D (28)
+            1, 5, 6, 6,  6, 21, 0], // E (35)
         edges: [
             // root node edges
             2,  6,  7, // shortcut 'a' to node 'A'
@@ -116,7 +114,7 @@ InspectorTest.createHeapSnapshotMockWithDOM = function()
     return InspectorTest._postprocessHeapSnapshotMock({
         snapshot: {
             meta: {
-                node_fields: ["type", "name", "id", "edges_index"],
+                node_fields: ["type", "name", "id", "edge_count"],
                 node_types: [["hidden", "object"], "", "", ""],
                 edge_fields: ["type", "name_or_index", "to_node"],
                 edge_types: [["element", "hidden", "internal"], "", ""]
@@ -137,19 +135,19 @@ InspectorTest.createHeapSnapshotMockWithDOM = function()
             //    |                   v
             //    |----->F--->G       M
             //
-            /* (root) */    0,  0,  1,  0,
-            /* Window */    1, 11,  2, 12,
-            /* Window */    1, 11,  3, 18,
-            /* E */         1,  5,  4, 27,
-            /* F */         1,  6,  5, 27,
-            /* A */         1,  1,  6, 30,
-            /* B */         1,  2,  7, 30,
-            /* D */         1,  4,  8, 33,
-            /* H */         1,  8,  9, 39,
-            /* G */         1,  7, 10, 39,
-            /* C */         1,  3, 11, 39,
-            /* N */         1, 10, 12, 39,
-            /* M */         1,  9, 13, 39
+            /* (root) */    0,  0,  1, 4,
+            /* Window */    1, 11,  2, 2,
+            /* Window */    1, 11,  3, 3,
+            /* E */         1,  5,  4, 0,
+            /* F */         1,  6,  5, 1,
+            /* A */         1,  1,  6, 0,
+            /* B */         1,  2,  7, 1,
+            /* D */         1,  4,  8, 2,
+            /* H */         1,  8,  9, 0,
+            /* G */         1,  7, 10, 0,
+            /* C */         1,  3, 11, 0,
+            /* N */         1, 10, 12, 0,
+            /* M */         1,  9, 13, 0
             ],
         edges: [
             /* from (root) */  0,  1,  4, 0, 2,  8, 0, 3, 12, 0, 4, 16,
@@ -460,9 +458,9 @@ InspectorTest.HeapNode.prototype = {
         // and even ids based on a hash for native DOMObject groups.
         rawSnapshot.nodes.push(this._id || this._ordinal * 2 + 1);
         rawSnapshot.nodes.push(this._selfSize);
-        rawSnapshot.nodes.push(0);                        // retained_size
-        rawSnapshot.nodes.push(0);                        // dominator
-        rawSnapshot.nodes.push(rawSnapshot.edges.length); // edges_index
+        rawSnapshot.nodes.push(0);                               // retained_size
+        rawSnapshot.nodes.push(0);                               // dominator
+        rawSnapshot.nodes.push(Object.keys(this._edges).length); // edge_count
 
         for (var i in this._edges)
             this._edges[i]._serialize(rawSnapshot);
@@ -528,7 +526,7 @@ InspectorTest.HeapSnapshotBuilder.prototype = {
         var rawSnapshot = {
             "snapshot": {
                 "meta": {
-                    "node_fields": ["type","name","id","self_size","retained_size","dominator","edges_index"],
+                    "node_fields": ["type","name","id","self_size","retained_size","dominator","edge_count"],
                     "node_types": [
                         this._nodeTypesArray,
                         "string",
index d835c8f..cb682ef 100644 (file)
@@ -95,6 +95,17 @@ function test()
             next();
         },
 
+        function heapSnapshotContainmentEdgeIndexesTest(next)
+        {
+            var snapshot = new WebInspector.HeapSnapshot(InspectorTest.createHeapSnapshotMock());
+            var actual = snapshot._firstEdgeIndexes;
+            var expected = [0, 6, 12, 18, 21, 21, 21];
+            InspectorTest.assertEquals(expected.length, actual.length, "Edge indexes size");
+            for (var i = 0; i < expected.length; ++i)
+                InspectorTest.assertEquals(expected[i], actual[i], "Edge indexes");
+            next();
+        },
+
         function heapSnapshotPostOrderIndexTest(next)
         {
             var snapshot = new WebInspector.HeapSnapshot(InspectorTest.createHeapSnapshotMock());
index cf7adc7..e3a8298 100644 (file)
@@ -1,3 +1,20 @@
+2012-06-05  Alexei Filippov  <alexeif@chromium.org>
+
+        Web Inspector: serialize edge counts instead of indexes in heap snapshot
+        https://bugs.webkit.org/show_bug.cgi?id=88324
+
+        The serialized node structure currently holds an index
+        of its first containment edge in the edges array.
+        The index can be quite big (up to 7 digits for large snapshots).
+        The patch changes the serialization format to pass
+        node containment edge count instead. For most nodes the count
+        is just a single digit number.
+        This reduces serialized snapshot size and therefore its transfer time.
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/heap-snapshot-performance-test.js:
+
 2012-06-04  Alexei Filippov  <alexeif@chromium.org>
 
         Web Inspector: speed up _calculateRetainedSizes function
index bfbcc97..516dbb4 100644 (file)
@@ -1,5 +1,6 @@
 function test()
 {
+    InspectorTest.measureFunction(WebInspector.HeapSnapshot.prototype, "_buildEdgeIndexes");
     InspectorTest.measureFunction(WebInspector.HeapSnapshot.prototype, "_buildRetainers");
     InspectorTest.measureFunction(WebInspector.HeapSnapshot.prototype, "_buildDominatedNodes");
     InspectorTest.measureFunction(WebInspector.HeapSnapshot.prototype, "_calculateFlags");
index 14fc7e5..5b5bf0e 100644 (file)
@@ -1,3 +1,33 @@
+2012-06-05  Alexei Filippov  <alexeif@chromium.org>
+
+        Web Inspector: serialize edge counts instead of indexes in heap snapshot
+        https://bugs.webkit.org/show_bug.cgi?id=88324
+
+        The serialized node structure currently holds an index
+        of its first containment edge in the edges array.
+        The index can be quite big (up to 7 digits for large snapshots).
+        The patch changes the serialization format to pass
+        node containment edge count instead. For most nodes the count
+        is just a single digit number.
+        This reduces serialized snapshot size and therefore its transfer time.
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/front-end/HeapSnapshot.js:
+        (WebInspector.HeapSnapshotNode.prototype._edgeIndexesStart):
+        (WebInspector.HeapSnapshotNode.prototype._edgeIndexesEnd):
+        (WebInspector.HeapSnapshotNode.prototype._ordinal):
+        (WebInspector.HeapSnapshotNodeIterator):
+        (WebInspector.HeapSnapshot.prototype._init):
+        (WebInspector.HeapSnapshot.prototype._buildEdgeIndexes):
+        (WebInspector.HeapSnapshot.prototype._buildRetainers):
+        (WebInspector.HeapSnapshot.prototype._bfs):
+        (WebInspector.HeapSnapshot.prototype._buildAggregates):
+        (WebInspector.HeapSnapshot.prototype._buildPostOrderIndex):
+        (WebInspector.HeapSnapshot.prototype._buildDominatorTree):
+        (WebInspector.HeapSnapshot.prototype._markPageOwnedNodes):
+        (WebInspector.HeapSnapshot.prototype._markQueriableHeapObjects):
+
 2012-06-05  Arpita Bahuguna  <arpitabahuguna@gmail.com>
 
         CSS 2.1 failure: border-conflict-element-021a
index f270282..57b2155 100644 (file)
@@ -535,14 +535,17 @@ WebInspector.HeapSnapshotNode.prototype = {
 
     _edgeIndexesStart: function()
     {
-        var snapshot = this._snapshot;
-        return snapshot._nodes[this.nodeIndex + snapshot._firstEdgeIndexOffset];
+        return this._snapshot._firstEdgeIndexes[this._ordinal()];
     },
 
     _edgeIndexesEnd: function()
     {
-        var snapshot = this._snapshot;
-        return snapshot._nodes[this._nextNodeIndex() + snapshot._firstEdgeIndexOffset]
+        return this._snapshot._firstEdgeIndexes[this._ordinal() + 1];
+    },
+
+    _ordinal: function()
+    {
+        return this.nodeIndex / this._snapshot._nodeFieldCount;
     },
 
     _nextNodeIndex: function()
@@ -563,7 +566,7 @@ WebInspector.HeapSnapshotNode.prototype = {
 WebInspector.HeapSnapshotNodeIterator = function(node)
 {
     this.node = node;
-    this._nodesLength = node._snapshot._realNodesLength;
+    this._nodesLength = node._snapshot._nodes.length;
 }
 
 WebInspector.HeapSnapshotNodeIterator.prototype = {
@@ -655,7 +658,7 @@ WebInspector.HeapSnapshot.prototype = {
         this._nodeNameOffset = meta.node_fields.indexOf("name");
         this._nodeIdOffset = meta.node_fields.indexOf("id");
         this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size");
-        this._firstEdgeIndexOffset = meta.node_fields.indexOf("edges_index");
+        this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count");
         this._nodeFieldCount = meta.node_fields.length;
 
         this._nodeTypes = meta.node_types[this._nodeTypeOffset];
@@ -688,16 +691,10 @@ WebInspector.HeapSnapshot.prototype = {
             visitedMarker:     0x10000  // bits: 1,0000,0000,0000,0000
         };
 
-        this._realNodesLength = this._nodes.length;
-        this.nodeCount = this._realNodesLength / this._nodeFieldCount;
+        this.nodeCount = this._nodes.length / this._nodeFieldCount;
         this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount;
 
-        // Add an extra node and make its first edge field point to the end of edges array.
-        var nodes = this._nodes;
-        this._nodes = new Uint32Array(this._realNodesLength + this._nodeFieldCount);
-        this._nodes.set(nodes);
-        this._nodes[this._realNodesLength + this._firstEdgeIndexOffset] = this._containmentEdges.length;
-
+        this._buildEdgeIndexes();
         this._markInvisibleEdges();
         this._buildRetainers();
         this._calculateFlags();
@@ -709,6 +706,35 @@ WebInspector.HeapSnapshot.prototype = {
         this._buildDominatedNodes();
     },
 
+    _buildEdgeIndexes: function()
+    {
+        // Support for old serialization.
+        if (this._nodeEdgeCountOffset === -1) {
+            var nodes = this._nodes;
+            var nodeCount = this.nodeCount;
+            var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
+            var nodeFieldCount = this._nodeFieldCount;
+            var nodeEdgesIndexOffset = this._metaNode.node_fields.indexOf("edges_index");
+            firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
+            for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
+                firstEdgeIndexes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeEdgesIndexOffset];
+            }
+            return;
+        }
+
+        var nodes = this._nodes;
+        var nodeCount = this.nodeCount;
+        var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
+        var nodeFieldCount = this._nodeFieldCount;
+        var edgeFieldsCount = this._edgeFieldsCount;
+        var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
+        firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
+        for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
+            firstEdgeIndexes[nodeOrdinal] = edgeIndex;
+            edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
+        }
+    },
+
     _buildRetainers: function()
     {
         var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount);
@@ -722,7 +748,8 @@ WebInspector.HeapSnapshot.prototype = {
         var nodeFieldCount = this._nodeFieldCount;
         var edgeToNodeOffset = this._edgeToNodeOffset;
         var nodes = this._nodes;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
+        var nodeCount = this.nodeCount;
 
         for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
             var toNodeIndex = containmentEdges[toNodeFieldIndex];
@@ -730,21 +757,19 @@ WebInspector.HeapSnapshot.prototype = {
                 throw new Error("Invalid toNodeIndex " + toNodeIndex);
             ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
         }
-        for (var i = 0, firstUnusedRetainerSlot = 0, l = this.nodeCount; i < l; i++) {
+        for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
             var retainersCount = firstRetainerIndex[i];
             firstRetainerIndex[i] = firstUnusedRetainerSlot;
             retainingNodes[firstUnusedRetainerSlot] = retainersCount;
             firstUnusedRetainerSlot += retainersCount;
         }
-        firstRetainerIndex[this.nodeCount] = retainingNodes.length;
+        firstRetainerIndex[nodeCount] = retainingNodes.length;
 
-        var srcNodeIndex = 0;
-        var nextNodeFirstEdgeIndex = nodes[firstEdgeIndexOffset];
-        var nodesLength = this._realNodesLength;
-        while (srcNodeIndex < nodesLength) {
+        var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
+        for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
             var firstEdgeIndex = nextNodeFirstEdgeIndex;
-            var nextNodeIndex = srcNodeIndex + nodeFieldCount;
-            nextNodeFirstEdgeIndex = nodes[nextNodeIndex + firstEdgeIndexOffset];
+            nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
+            var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
             for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
                 var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
                 if (toNodeIndex % nodeFieldCount)
@@ -754,7 +779,6 @@ WebInspector.HeapSnapshot.prototype = {
                 retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
                 retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
             }
-            srcNodeIndex = nextNodeIndex;
         }
     },
 
@@ -911,9 +935,9 @@ WebInspector.HeapSnapshot.prototype = {
     {
         // Peload fields into local variables for better performance.
         var edgeFieldsCount = this._edgeFieldsCount;
-        var containmentEdges = this._containmentEdges;
         var nodeFieldCount = this._nodeFieldCount;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var containmentEdges = this._containmentEdges;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
         var edgeToNodeOffset = this._edgeToNodeOffset;
         var nodes = this._nodes;
         var nodeCount = this.nodeCount;
@@ -924,8 +948,8 @@ WebInspector.HeapSnapshot.prototype = {
             var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
             var nodeOrdinal = nodeIndex / nodeFieldCount;
             var distance = distances[nodeOrdinal] + 1;
-            var firstEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset];
-            var edgesEnd = nodes[nodeIndex + firstEdgeIndexOffset + nodeFieldCount];
+            var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
             for (var edgeToNodeIndex = firstEdgeIndex + edgeToNodeOffset; edgeToNodeIndex < edgesEnd; edgeToNodeIndex += edgeFieldsCount) {
                 var childNodeIndex = containmentEdges[edgeToNodeIndex];
                 var childNodeOrdinal = childNodeIndex / nodeFieldCount;
@@ -946,7 +970,7 @@ WebInspector.HeapSnapshot.prototype = {
         var classIndexes = [];
         var nodes = this._nodes;
         var flags = this._flags;
-        var nodesLength = this._realNodesLength;
+        var nodesLength = nodes.length;
         var nodeNativeType = this._nodeNativeType;
         var nodeFieldCount = this._nodeFieldCount;
         var selfSizeOffset = this._nodeSelfSizeOffset;
@@ -1066,13 +1090,13 @@ WebInspector.HeapSnapshot.prototype = {
         var nodeFieldCount = this._nodeFieldCount;
         var nodes = this._nodes;
         var nodeCount = this.nodeCount;
-        var rootNodeIndex = this._rootNodeIndex;
+        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
 
         var edgeFieldsCount = this._edgeFieldsCount;
-        var edgeToNodeOffset = this._edgeToNodeOffset;
         var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
         var edgeShortcutType = this._edgeShortcutType;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
         var containmentEdges = this._containmentEdges;
         var containmentEdgesLength = this._containmentEdges.length;
 
@@ -1088,30 +1112,29 @@ WebInspector.HeapSnapshot.prototype = {
         var grey = 1;
         var black = 2;
 
-        nodesToVisit[nodesToVisitLength++] = this._rootNodeIndex;
-        painted[this._rootNodeIndex / nodeFieldCount] = grey;
+        nodesToVisit[nodesToVisitLength++] = rootNodeOrdinal;
+        painted[rootNodeOrdinal] = grey;
 
         while (nodesToVisitLength) {
-            var nodeIndex = nodesToVisit[nodesToVisitLength - 1];
-            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            var nodeOrdinal = nodesToVisit[nodesToVisitLength - 1];
             if (painted[nodeOrdinal] === grey) {
                 painted[nodeOrdinal] = black;
                 var nodeFlag = flags[nodeOrdinal] & flag;
-                var beginEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset];
-                var endEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset + nodeFieldCount];
+                var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+                var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
                 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
-                    if (nodeIndex !== rootNodeIndex && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
+                    if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
                         continue;
                     var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
                     var childNodeOrdinal = childNodeIndex / nodeFieldCount;
                     var childNodeFlag = flags[childNodeOrdinal] & flag;
                     // We are skipping the edges from non-page-owned nodes to page-owned nodes.
                     // Otherwise the dominators for the objects that also were retained by debugger would be affected.
-                    if (nodeIndex !== rootNodeIndex && childNodeFlag && !nodeFlag)
+                    if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
                         continue;
                     if (!painted[childNodeOrdinal]) {
                         painted[childNodeOrdinal] = grey;
-                        nodesToVisit[nodesToVisitLength++] = childNodeIndex;
+                        nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
                     }
                 }
             } else {
@@ -1142,10 +1165,10 @@ WebInspector.HeapSnapshot.prototype = {
         var retainingNodes = this._retainingNodes;
         var retainingEdges = this._retainingEdges;
         var edgeFieldsCount = this._edgeFieldsCount;
-        var edgeToNodeOffset = this._edgeToNodeOffset;
         var edgeTypeOffset = this._edgeTypeOffset;
+        var edgeToNodeOffset = this._edgeToNodeOffset;
         var edgeShortcutType = this._edgeShortcutType;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
         var containmentEdges = this._containmentEdges;
         var containmentEdgesLength = this._containmentEdges.length;
         var rootNodeIndex = this._rootNodeIndex;
@@ -1166,9 +1189,9 @@ WebInspector.HeapSnapshot.prototype = {
         var affected = new Uint8Array(nodesCount);
 
         { // Mark the root direct children as affected.
-            var nodeIndex = this._rootNodeIndex;
-            var beginEdgeToNodeFieldIndex = nodes[nodeIndex + firstEdgeIndexOffset] + edgeToNodeOffset;
-            var endEdgeToNodeFieldIndex = nodes[nodeIndex + nodeFieldCount + firstEdgeIndexOffset];
+            var nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+            var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+            var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
             for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
                  toNodeFieldIndex < endEdgeToNodeFieldIndex;
                  toNodeFieldIndex += edgeFieldsCount) {
@@ -1227,9 +1250,8 @@ WebInspector.HeapSnapshot.prototype = {
                     dominators[postOrderIndex] = newDominatorIndex;
                     changed = true;
                     nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
-                    nodeIndex = nodeOrdinal * nodeFieldCount;
-                    beginEdgeToNodeFieldIndex = nodes[nodeIndex + firstEdgeIndexOffset] + edgeToNodeOffset;
-                    endEdgeToNodeFieldIndex = nodes[nodeIndex + firstEdgeIndexOffset + nodeFieldCount];
+                    beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
+                    endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
                     for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
                          toNodeFieldIndex < endEdgeToNodeFieldIndex;
                          toNodeFieldIndex += edgeFieldsCount) {
@@ -1367,7 +1389,7 @@ WebInspector.HeapSnapshot.prototype = {
         var edgeTypeOffset = this._edgeTypeOffset;
         var edgeFieldsCount = this._edgeFieldsCount;
         var edgeWeakType = this._edgeWeakType;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
         var containmentEdges = this._containmentEdges;
         var containmentEdgesLength = containmentEdges.length;
         var nodes = this._nodes;
@@ -1383,25 +1405,23 @@ WebInspector.HeapSnapshot.prototype = {
         var nodesToVisit = new Uint32Array(nodesCount);
         var nodesToVisitLength = 0;
 
-        for (var edgeIndex = nodes[this._rootNodeIndex + firstEdgeIndexOffset], endEdgeIndex = nodes[this._rootNodeIndex + nodeFieldCount + firstEdgeIndexOffset];
+        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
+        for (var edgeIndex = firstEdgeIndexes[rootNodeOrdinal], endEdgeIndex = firstEdgeIndexes[rootNodeOrdinal + 1];
              edgeIndex < endEdgeIndex;
              edgeIndex += edgeFieldsCount) {
             if (containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType) {
-                var nodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
-                nodesToVisit[nodesToVisitLength++] = nodeIndex;
-                flags[nodeIndex / nodeFieldCount] |= visitedMarker;
+                var nodeOrdinal = containmentEdges[edgeIndex + edgeToNodeOffset] / nodeFieldCount;
+                nodesToVisit[nodesToVisitLength++] = nodeOrdinal;
+                flags[nodeOrdinal] |= visitedMarker;
             }
         }
 
         while (nodesToVisitLength) {
-            var nodeIndex = nodesToVisit[--nodesToVisitLength];
-            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            var nodeOrdinal = nodesToVisit[--nodesToVisitLength];
             flags[nodeOrdinal] |= flag;
             flags[nodeOrdinal] &= visitedMarkerMask;
-            var beginEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset];
-            var endEdgeIndex = nodeOrdinal < nodesCount - 1
-                ? nodes[nodeIndex + firstEdgeIndexOffset + nodeFieldCount]
-                : containmentEdgesLength;
+            var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
             for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
                 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
                 var childNodeOrdinal = childNodeIndex / nodeFieldCount;
@@ -1410,7 +1430,7 @@ WebInspector.HeapSnapshot.prototype = {
                 var type = containmentEdges[edgeIndex + edgeTypeOffset];
                 if (type === edgeWeakType)
                     continue;
-                nodesToVisit[nodesToVisitLength++] = childNodeIndex;
+                nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
                 flags[childNodeOrdinal] |= visitedMarker;
             }
         }
@@ -1432,32 +1452,32 @@ WebInspector.HeapSnapshot.prototype = {
         var nodes = this._nodes;
         var nodeCount = this.nodeCount;
         var nodeFieldCount = this._nodeFieldCount;
-        var firstEdgeIndexOffset = this._firstEdgeIndexOffset;
+        var firstEdgeIndexes = this._firstEdgeIndexes;
 
         var flags = this._flags;
         var list = [];
 
         for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
             if (iter.edge.node().isWindow())
-                list.push(iter.edge.node().nodeIndex);
+                list.push(iter.edge.node().nodeIndex / nodeFieldCount);
         }
 
         while (list.length) {
-            var nodeIndex = list.pop();
-            var nodeOrdinal = nodeIndex / nodeFieldCount;
+            var nodeOrdinal = list.pop();
             if (flags[nodeOrdinal] & flag)
                 continue;
             flags[nodeOrdinal] |= flag;
-            var beginEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset];
-            var endEdgeIndex = nodes[nodeIndex + firstEdgeIndexOffset + nodeFieldCount];
+            var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
+            var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
             for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
                 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
-                if (flags[childNodeIndex / nodeFieldCount] & flag)
+                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
+                if (flags[childNodeOrdinal] & flag)
                     continue;
                 var type = containmentEdges[edgeIndex + edgeTypeOffset];
                 if (type === hiddenEdgeType || type === invisibleEdgeType || type === internalEdgeType)
                     continue;
-                list.push(childNodeIndex);
+                list.push(childNodeOrdinal);
             }
         }
     },