Bug 1219071 - Cache the results of the dfs when rendering the tree widget; r=jsantell
authorNick Fitzgerald <fitzgen@gmail.com>
Wed, 28 Oct 2015 10:20:32 -0700
changeset 305120 63f0f345ca872ce7fe12b0cfef3e3a166f1d27e5
parent 305119 692d0ed9f9eb102efb136fff4dd9df4fdb16a2ea
child 305121 acfa9848f2eeaa7dee9dd14a7a2863998bd2bcfe
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1219071
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1219071 - Cache the results of the dfs when rendering the tree widget; r=jsantell
devtools/client/memory/components/heap.js
devtools/client/memory/components/tree.js
--- a/devtools/client/memory/components/heap.js
+++ b/devtools/client/memory/components/heap.js
@@ -42,16 +42,19 @@ function createTreeProperties (census) {
 
   return {
     getParent: node => map(node.id),
     getChildren: node => node.children || [],
     renderItem: (item, depth, focused, arrow) => new TreeItem({ item, depth, focused, arrow }),
     getRoots: () => census.children,
     getKey: node => node.id,
     itemHeight: HEAP_TREE_ROW_HEIGHT,
+    // Because we never add or remove children when viewing the same census, we
+    // can always reuse a cached traversal if one is available.
+    reuseCachedTraversal: traversal => true,
   };
 }
 
 /**
  * Main view for the memory tool -- contains several panels for different states;
  * an initial state of only a button to take a snapshot, loading states, and the
  * heap view tree.
  */
--- a/devtools/client/memory/components/tree.js
+++ b/devtools/client/memory/components/tree.js
@@ -95,17 +95,17 @@ const TreeNode = createFactory(createCla
       // margin to completely hide it.
       MozMarginStart: "-1000px !important",
     }
   }
 }));
 
 /**
  * A generic tree component. See propTypes for the public API.
- * 
+ *
  * @see `devtools/client/memory/components/test/mochitest/head.js` for usage
  * @see `devtools/client/memory/components/heap.js` for usage
  */
 const Tree = module.exports = createClass({
   displayName: "Tree",
 
   propTypes: {
     // Required props
@@ -125,36 +125,42 @@ const Tree = module.exports = createClas
     // pixels.
     itemHeight: PropTypes.number.isRequired,
 
     // Optional props
 
     // A predicate function to filter out unwanted items from the tree.
     filter: PropTypes.func,
     // The depth to which we should automatically expand new items.
-    autoExpandDepth: PropTypes.number
+    autoExpandDepth: PropTypes.number,
+    // A predicate that returns true if the last DFS traversal that was cached
+    // can be reused, false otherwise. The predicate function is passed the
+    // cached traversal as an array of nodes.
+    reuseCachedTraversal: PropTypes.func,
   },
 
   getDefaultProps() {
     return {
       filter: item => true,
       expanded: new Set(),
       seen: new Set(),
       focused: undefined,
-      autoExpandDepth: AUTO_EXPAND_DEPTH
+      autoExpandDepth: AUTO_EXPAND_DEPTH,
+      reuseCachedTraversal: null,
     };
   },
 
   getInitialState() {
     return {
       scroll: 0,
       height: window.innerHeight,
       expanded: new Set(),
       seen: new Set(),
-      focused: undefined
+      focused: undefined,
+      cachedTraversal: undefined,
     };
   },
 
   componentDidMount() {
     window.addEventListener("resize", this._updateHeight);
     this._updateHeight();
   },
 
@@ -268,20 +274,33 @@ const Tree = module.exports = createClas
 
     return traversal;
   },
 
   /**
    * Perform a pre-order depth-first search over the whole forest.
    */
   _dfsFromRoots(maxDepth = Infinity) {
+    const cached = this.state.cachedTraversal;
+    if (cached
+        && maxDepth === Infinity
+        && this.props.reuseCachedTraversal
+        && this.props.reuseCachedTraversal(cached)) {
+      return cached;
+    }
+
     const traversal = [];
     for (let root of this.props.getRoots()) {
       this._dfs(root, maxDepth, traversal);
     }
+
+    if (this.props.reuseCachedTraversal) {
+      this.state.cachedTraversal = traversal;
+    }
+
     return traversal;
   },
 
   /**
    * Expands current row.
    *
    * @param {Object} item
    * @param {Boolean} expandAllChildren
@@ -291,29 +310,31 @@ const Tree = module.exports = createClas
 
     if (expandAllChildren) {
       for (let { item: child } of this._dfs(item)) {
         this.state.expanded.add(child);
       }
     }
 
     this.setState({
-      expanded: this.state.expanded
+      expanded: this.state.expanded,
+      cachedTraversal: null,
     });
   },
 
   /**
    * Collapses current row.
    *
    * @param {Object} item
    */
   _onCollapse(item) {
     this.state.expanded.delete(item);
     this.setState({
-      expanded: this.state.expanded
+      expanded: this.state.expanded,
+      cachedTraversal: null,
     });
   },
 
   /**
    * Sets the passed in item to be the focused item.
    *
    * @param {Object} item
    */