Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve asymptotic performance of binary treemap #44

Merged
merged 1 commit into from
Jun 7, 2016

Conversation

rohanpadhye
Copy link
Contributor

The current algorithm for computing partitions in a binary treemap performs a lot of redundant prefix sums and therefore has sub-optimal asymptotic performance.

In the worst-case, where each call to partition results in the right split having exactly one child node (this happens when children have values in increasing powers of two), the runtime is O(n2). On the other hand, the best-case performance is O(n), when the values are in decreasing powers of two.

The asymptotic performance can be improved by pre-computing the prefix sums of all the children and performing a binary search at each call to partition to locate the split point. With this, the best-case performance is O(n) (for even splits when all children have the same value) and the worst-case performance is O(n log n) (for unbalanced splits).

Here are some results of a benchmark that measures execution times for 1,000 iterations of three trees with 1,000 children on my machine (Macbook Pro with Node v5.3.0).

Before:

Even splits     : 46.061ms
Unbalanced-left : 1466.794ms
Unbalanced-right: 43.297ms

After:

Even splits     : 53.034ms
Unbalanced-left : 74.412ms
Unbalanced-right: 60.188ms

There is of course some overhead for creating the prefix sum array, but the performance is consistent across different value distributions. In particular, the worst-case has improved by almost 20X, while the best-case has degraded by less than 25%.

The fix currently uses an inline implementation of binary search, but Lines 22-30 can be replaced with the following if d3-array is included as a dependency:

var k = bisectLeft(sums, goal, i+1, j-1);

@mbostock mbostock self-assigned this May 27, 2016
@mbostock
Copy link
Member

Exciting! This looks promising. Will try to review soon. Thank you for the contribution.

@mbostock mbostock merged commit 0a986f8 into d3:master Jun 7, 2016
@mbostock
Copy link
Member

mbostock commented Jun 7, 2016

I polished it a tiny bit in f5ba7f1. It’s now merged. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants