Skip to content

Commit

Permalink
refactor: rework node value assignment logic
Browse files Browse the repository at this point in the history
Previous implementation that used `sum()` was inefficient and contained
some minor errors (like using `getChildren()` was used with nodes).
Proposed implementation is 3x-4x faster.

Conflicts:
	dist/d3-flamegraph.min.js
  • Loading branch information
Andrey Pokrovskiy authored and spiermar committed Oct 23, 2018
1 parent 4f57269 commit 0ecd190
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 33 deletions.
99 changes: 83 additions & 16 deletions dist/d3-flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5195,21 +5195,9 @@ var flamegraph = function () {
var x = linear().range([0, w]);
var y = linear().range([0, c]);

reappraiseNode(root);
if (sort) root.sort(doSort);
root.sum(function (d) {
if (d.fade || d.hide) {
return 0
}
// The node's self value is its total value minus all children.
var v = getValue(d);
if (!selfValue && getChildren(d)) {
var c = getChildren(d);
for (var i = 0; i < c.length; i++) {
v -= getValue(c[i]);
}
}
return v
});

p(root);

var kx = w / (root.x1 - root.x0);
Expand Down Expand Up @@ -5318,7 +5306,7 @@ var flamegraph = function () {

function forEachNode (node, f) {
f(node);
let children = getChildren(node);
let children = node.children;
if (children) {
const stack = [children];
let count, child, grandChildren;
Expand All @@ -5328,7 +5316,7 @@ var flamegraph = function () {
while (count--) {
child = children[count];
f(child);
grandChildren = getChildren(child);
grandChildren = child.children;
if (grandChildren) {
stack.push(grandChildren);
}
Expand All @@ -5353,6 +5341,85 @@ var flamegraph = function () {
});
}

function reappraiseNode (root) {
let node, children, grandChildren, childrenValue, i, j, child, childValue;
const stack = [];
const included = [];
const excluded = [];
const compoundValue = !selfValue;
let item = root.data;
if (item.hide) {
root.value = 0;
children = root.children;
if (children) {
excluded.push(children);
}
} else {
root.value = item.fade ? 0 : getValue(item);
stack.push(root);
}
// First DFS pass:
// 1. Update node.value with node's self value
// 2. Populate excluded list with children under hidden nodes
// 3. Populate included list with children under visible nodes
while ((node = stack.pop())) {
children = node.children;
if (children && (i = children.length)) {
childrenValue = 0;
while (i--) {
child = children[i];
item = child.data;
if (item.hide) {
child.value = 0;
grandChildren = child.children;
if (grandChildren) {
excluded.push(grandChildren);
}
continue
}
if (item.fade) {
child.value = 0;
} else {
childValue = getValue(item);
child.value = childValue;
childrenValue += childValue;
}
stack.push(child);
}
// Here second part of `&&` is actually checking for `node.data.fade`. However,
// checking for node.value is faster and presents more oportunities for JS optimizer.
if (compoundValue && node.value) {
node.value -= childrenValue;
}
included.push(children);
}
}
// Postorder traversal to compute compound value of each visible node.
i = included.length;
while (i--) {
children = included[i];
childrenValue = 0;
j = children.length;
while (j--) {
childrenValue += children[j].value;
}
children[0].parent.value += childrenValue;
}
// Continue DFS to set value of all hidden nodes to 0.
while (excluded.length) {
children = excluded.pop();
j = children.length;
while (j--) {
child = children[j];
child.value = 0;
grandChildren = child.children;
if (grandChildren) {
excluded.push(grandChildren);
}
}
}
}

function chart (s) {
var root = hierarchy(s.datum(), getChildren);
adoptNode(root);
Expand Down
2 changes: 1 addition & 1 deletion dist/d3-flamegraph.min.js

Large diffs are not rendered by default.

99 changes: 83 additions & 16 deletions src/flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,21 +330,9 @@ export default function () {
var x = scaleLinear().range([0, w])
var y = scaleLinear().range([0, c])

reappraiseNode(root)
if (sort) root.sort(doSort)
root.sum(function (d) {
if (d.fade || d.hide) {
return 0
}
// The node's self value is its total value minus all children.
var v = getValue(d)
if (!selfValue && getChildren(d)) {
var c = getChildren(d)
for (var i = 0; i < c.length; i++) {
v -= getValue(c[i])
}
}
return v
})

p(root)

var kx = w / (root.x1 - root.x0)
Expand Down Expand Up @@ -453,7 +441,7 @@ export default function () {

function forEachNode (node, f) {
f(node)
let children = getChildren(node)
let children = node.children
if (children) {
const stack = [children]
let count, child, grandChildren
Expand All @@ -463,7 +451,7 @@ export default function () {
while (count--) {
child = children[count]
f(child)
grandChildren = getChildren(child)
grandChildren = child.children
if (grandChildren) {
stack.push(grandChildren)
}
Expand All @@ -488,6 +476,85 @@ export default function () {
})
}

function reappraiseNode (root) {
let node, children, grandChildren, childrenValue, i, j, child, childValue
const stack = []
const included = []
const excluded = []
const compoundValue = !selfValue
let item = root.data
if (item.hide) {
root.value = 0
children = root.children
if (children) {
excluded.push(children)
}
} else {
root.value = item.fade ? 0 : getValue(item)
stack.push(root)
}
// First DFS pass:
// 1. Update node.value with node's self value
// 2. Populate excluded list with children under hidden nodes
// 3. Populate included list with children under visible nodes
while ((node = stack.pop())) {
children = node.children
if (children && (i = children.length)) {
childrenValue = 0
while (i--) {
child = children[i]
item = child.data
if (item.hide) {
child.value = 0
grandChildren = child.children
if (grandChildren) {
excluded.push(grandChildren)
}
continue
}
if (item.fade) {
child.value = 0
} else {
childValue = getValue(item)
child.value = childValue
childrenValue += childValue
}
stack.push(child)
}
// Here second part of `&&` is actually checking for `node.data.fade`. However,
// checking for node.value is faster and presents more oportunities for JS optimizer.
if (compoundValue && node.value) {
node.value -= childrenValue
}
included.push(children)
}
}
// Postorder traversal to compute compound value of each visible node.
i = included.length
while (i--) {
children = included[i]
childrenValue = 0
j = children.length
while (j--) {
childrenValue += children[j].value
}
children[0].parent.value += childrenValue
}
// Continue DFS to set value of all hidden nodes to 0.
while (excluded.length) {
children = excluded.pop()
j = children.length
while (j--) {
child = children[j]
child.value = 0
grandChildren = child.children
if (grandChildren) {
excluded.push(grandChildren)
}
}
}
}

function chart (s) {
var root = hierarchy(s.datum(), getChildren)
adoptNode(root)
Expand Down

0 comments on commit 0ecd190

Please sign in to comment.