-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Introduce an interval tree implementation backed by an array. #73855
Conversation
using Xunit; | ||
|
||
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Collections; | ||
|
||
public sealed class IntervalTreeTests | ||
public abstract class IntervalTreeTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this allows me to run the same tests on both impls of interval trees. note: this test code is not pretty. I didn't want to spend a lot of time figuring out how to make it pretty.
[Fact] | ||
public void TestProperBalancing() | ||
{ | ||
for (var i = 0; i < 3000; i++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hammers a ton of configurations of interval trees, ensuring that our creation of the complete binary tree is correct.
return (new TextSpanIntervalTree(interpolationInteriorSpans), new TextSpanIntervalTree(restrictedSpans)); | ||
return ( | ||
FlatArrayIntervalTree<TextSpan>.CreateFromUnsorted(new TextSpanIntervalIntrospector(), interpolationInteriorSpans), | ||
FlatArrayIntervalTree<TextSpan>.CreateFromUnsorted(new TextSpanIntervalIntrospector(), restrictedSpans)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note; i'm usnig CreateFromUnsorted here as i don't want to have to prove yet if the above algorithm produces a sorted list of elements.
/// </list> | ||
/// </remarks> | ||
public static FlatArrayIntervalTree<T> CreateFromSorted<TIntrospector>(in TIntrospector introspector, SegmentedList<T> values) | ||
where TIntrospector : struct, IIntervalIntrospector<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was the code from the binary tree version, moved over her.e
Note: it was changed extensively and should be reviewed. Specifically, we must generated a complete binary tree so that all nodes can find their children through the 2*i + 1 (or) 2
algorithm. If there are gaps anywhere (say multiple nodes on the second to last level that have a left child, but no right child) then this will break.
...Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/FlatArrayIntervalTree`1.cs
Show resolved
Hide resolved
|
||
public sealed class BinaryIntervalTreeTests : IntervalTreeTests | ||
{ | ||
private protected override IEnumerable<IIntervalTree<Tuple<int, int, string>>> CreateTrees(IEnumerable<Tuple<int, int, string>> values) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't ask :'(
...Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/FlatArrayIntervalTree`1.cs
Show resolved
Hide resolved
where TIntrospector : struct, IIntervalIntrospector<T> | ||
=> introspector.GetSpan(value).End; | ||
|
||
bool IIntervalTree<T>.Any<TIntrospector>(int start, int length, TestInterval<T, TIntrospector> testInterval, in TIntrospector introspector) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the next pr, this code will be shared with the binary tree impl.
int start, int length, TestInterval<T, TIntrospector> testInterval, | ||
ref TemporaryArray<T> builder, in TIntrospector introspector, | ||
bool stopAfterFirst) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the next pr, this code will be shared with the binary tree impl.
// The nature of a complete tree is that the last level always only contains the odd remaining numbers. | ||
// For example, given the initial values a-n: | ||
// | ||
// a, b, c, d, e, f, g, h, i, j, k, l, m, n. The final tree will look like: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do in followup pr.
ref TemporaryArray<T> builder, in TIntrospector introspector, | ||
bool stopAfterFirst) | ||
{ | ||
var array = _array; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
candidates.Push((nodeIndex: 0, firstTime: true)); | ||
|
||
while (candidates.TryPop(out var currentTuple)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup. can do in followup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drops memory allocations for our interval tree by 2/3rds. From:
to:
The savings here come from the problem that the binary-tree approach needs an allocation for each node itself (so all the overhead there for all N nodes we create), and then each node contains 3 node pointers (to the left node, right node, and max end node), and an 'int' to represent height.
The new system has no Node allocation itself (the 'node' is now just a struct with the original client value, and an integer for hte max end node index). Instead, there is just one array alloc (so only the overhead of one object header for the array itself). There is no need for a left/right pointer as they can be inferred from the index of the node we are currently at.