Skip to content

Fix type instabilities in AVL, RB and splay tree #766

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

Merged
merged 7 commits into from
Dec 2, 2021

Conversation

goerch
Copy link
Contributor

@goerch goerch commented Oct 25, 2021

Improves benchmark results for
#762

v0.18.10

100000 elements
DataStructures.AVLTree 6.061 s (7102520 allocations: 114.48 MiB)
1000 elements
DataStructures.AVLTree 36.800 ms (28637 allocations: 509.97 KiB)
10 elements
DataStructures.AVLTree 134.361 μs (102 allocations: 2.23 KiB)

v0.19.0-DEV

100000 elements
DataStructures.AVLTree 6.113 s (7102520 allocations: 114.48 MiB)
1000 elements
DataStructures.AVLTree 36.298 ms (28637 allocations: 509.97 KiB)
10 elements
DataStructures.AVLTree 130.629 μs (102 allocations: 2.23 KiB)

Now:

100000 elements
DataStructures.AVLTree 213.450 ms (200002 allocations: 9.16 MiB)
1000 elements
DataStructures.AVLTree 1.408 ms (2002 allocations: 93.80 KiB)
10 elements
DataStructures.AVLTree 7.814 μs (22 allocations: 1008 bytes)

Improves benchmark results for
JuliaCollections#762

v0.18.10

100000 elements
  DataStructures.AVLTree                   6.061 s (7102520 allocations: 114.48 MiB)
1000 elements
  DataStructures.AVLTree                   36.800 ms (28637 allocations: 509.97 KiB)
10 elements
  DataStructures.AVLTree                   134.361 μs (102 allocations: 2.23 KiB)

v0.19.0-DEV

100000 elements
  DataStructures.AVLTree                   6.113 s (7102520 allocations: 114.48 MiB)
1000 elements
  DataStructures.AVLTree                   36.298 ms (28637 allocations: 509.97 KiB)
10 elements
  DataStructures.AVLTree                   130.629 μs (102 allocations: 2.23 KiB)

Now:

100000 elements
  DataStructures.AVLTree                   213.450 ms (200002 allocations: 9.16 MiB)
1000 elements
  DataStructures.AVLTree                   1.408 ms (2002 allocations: 93.80 KiB)
10 elements
  DataStructures.AVLTree                   7.814 μs (22 allocations: 1008 bytes)
Improves benchmark results for
JuliaCollections#762

Before:

100000 elements
  DataStructures.AVLTree                   201.994 ms (200002 allocations: 9.16 MiB)
  DataStructures.RBTree                    514.536 ms (200003 allocations: 12.21 MiB)
1000 elements
  DataStructures.AVLTree                   1.303 ms (2002 allocations: 93.80 KiB)
  DataStructures.RBTree                    4.395 ms (2003 allocations: 125.11 KiB)
10 elements
  DataStructures.AVLTree                   6.998 μs (22 allocations: 1008 bytes)
  DataStructures.RBTree                    37.789 μs (23 allocations: 1.36 KiB)

Now:

100000 elements
  DataStructures.AVLTree                   261.295 ms (204574 allocations: 9.23 MiB)
  DataStructures.RBTree                    394.023 ms (200003 allocations: 12.21 MiB)
1000 elements
  DataStructures.AVLTree                   1.771 ms (2014 allocations: 93.98 KiB)
  DataStructures.RBTree                    3.190 ms (2003 allocations: 125.11 KiB)
10 elements
  DataStructures.AVLTree                   9.797 μs (22 allocations: 1008 bytes)
  DataStructures.RBTree                    17.728 μs (23 allocations: 1.36 KiB)
@goerch goerch changed the title Fix type instabilities in AVL tree Fix type instabilities in AVL and RB tree Oct 26, 2021
Copy link
Member

@oxinabox oxinabox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.
Those are quite the performance improvements.

need to address the type-piracy though

We should also delete/change

https://github.com/JuliaCollections/DataStructures.jl/pull/766/files?diff=split&w=1#diff-bed9f1a3faabfaae84a4ed46f9cd059405077f120c7737ab8e782d84a54c7d32R15
and
https://github.com/JuliaCollections/DataStructures.jl/pull/766/files?diff=split&w=1#diff-11e9fe382dc297594ea9e1ef03a4076696e21896a49b4be935cd710bfa23fa37R18

which define:
RBTreeNode(d) = RBTreeNode{Any}(d) should really be: RBTreeNode(d::K) where K = RBTreeNode{K}(d)
and similar

@goerch
Copy link
Contributor Author

goerch commented Oct 27, 2021

After some more experimenting with type specific convert I don't see a way around specializing setproperty!. Without it I currently get (on a different system)

100000 elements
  DataStructures.AVLTree                   431.418 ms (200002 allocations: 9.16 MiB)
  DataStructures.RBTree                    545.514 ms (200003 allocations: 12.21 MiB)
  DataStructures.SplayTree                 366.566 ms (200002 allocations: 9.16 MiB)
1000 elements
  DataStructures.AVLTree                   5.499 ms (2002 allocations: 93.80 KiB)
  DataStructures.RBTree                    4.756 ms (2003 allocations: 125.11 KiB)
  DataStructures.SplayTree                 3.164 ms (2002 allocations: 93.80 KiB)
10 elements
  DataStructures.AVLTree                   48.985 μs (22 allocations: 1008 bytes)
  DataStructures.RBTree                    43.387 μs (23 allocations: 1.36 KiB)
  DataStructures.SplayTree                 28.924 μs (22 allocations: 1008 bytes)

Specializing like

Base.setproperty!(x::AVLTreeNode{T}, f::Symbol, v) where {T} =
    setfield!(x, f, v)

I see

100000 elements
  DataStructures.AVLTree                   194.724 ms (200002 allocations: 9.16 MiB)
  DataStructures.RBTree                    192.539 ms (200003 allocations: 12.21 MiB)
  DataStructures.SplayTree                 123.407 ms (200002 allocations: 9.16 MiB)
1000 elements
  DataStructures.AVLTree                   1.250 ms (2002 allocations: 93.80 KiB)
  DataStructures.RBTree                    1.253 ms (2003 allocations: 125.11 KiB)
  DataStructures.SplayTree                 982.515 μs (2002 allocations: 93.80 KiB)
10 elements
  DataStructures.AVLTree                   6.998 μs (22 allocations: 1008 bytes)
  DataStructures.RBTree                    5.598 μs (23 allocations: 1.36 KiB)
  DataStructures.SplayTree                 7.348 μs (22 allocations: 1008 bytes)

Do you still suspect type piracy with the above change?

Edit: Tried to discuss the problem on Discourse recently: https://discourse.julialang.org/t/dynamic-dispatch-with-union-nothing/70174/8

@goerch
Copy link
Contributor Author

goerch commented Oct 28, 2021

We should also delete/change

https://github.com/JuliaCollections/DataStructures.jl/pull/766/files?diff=split&w=1#diff-bed9f1a3faabfaae84a4ed46f9cd059405077f120c7737ab8e782d84a54c7d32R15 and https://github.com/JuliaCollections/DataStructures.jl/pull/766/files?diff=split&w=1#diff-11e9fe382dc297594ea9e1ef03a4076696e21896a49b4be935cd710bfa23fa37R18

which define: RBTreeNode(d) = RBTreeNode{Any}(d) should really be: RBTreeNode(d::K) where K = RBTreeNode{K}(d) and similar

Sorry I can't estimate the impact of these changes. You should be able to edit this patch at will?

@goerch goerch changed the title Fix type instabilities in AVL and RB tree Fix type instabilities in AVL, RB and splay tree Oct 28, 2021
oxinabox
oxinabox previously approved these changes Oct 29, 2021
@oxinabox
Copy link
Member

oxinabox commented Oct 29, 2021

Sorry I can't estimate the impact of these changes. You should be able to edit this patch at will?

A follow up can be made

@oxinabox
Copy link
Member

Do you still suspect type piracy with the above change?

There is no longer any type-piracy in the code, and it is much cleaner.
Good job

Comment on lines +17 to +18
Base.setproperty!(x::AVLTreeNode{T}, f::Symbol, v) where {T} =
setfield!(x, f, v)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised this is needed.
What happens if we delete this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@goerch goerch Oct 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically: Juno profiler shows dynamic dispatch in

setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v))

called by

    z.rightChild = α

in left_rotate and

    z.leftChild = α

in right_rotate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative fix seems to be

    if α === nothing
        z.rightChild = nothing
    else
        z.rightChild = α
    end

but I do not feel something like this should be necessary?

Comment on lines +14 to +15
Base.setproperty!(x::SplayTreeNode{K}, f::Symbol, v) where {K} =
setfield!(x, f, v)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar I am surprised this is needed, what happens if we delete this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the analysis with Juno profiler is not that clear cut. It surprisingly indicates alloctions in

setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v))

called from

    node_x.rightChild = node_y.leftChild

in left_rotate! and

    node_x.leftChild = node_y.rightChild

in right_rotate!.

@oxinabox oxinabox dismissed their stale review October 29, 2021 11:26

misclicked

@oxinabox oxinabox self-requested a review October 29, 2021 11:26
@goerch
Copy link
Contributor Author

goerch commented Nov 2, 2021

@oxinabox, @eulerkochy: any ideas how to proceed?

Copy link
Member

@oxinabox oxinabox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets merge this now, it can always be improved later.

@oxinabox oxinabox merged commit d4e6491 into JuliaCollections:master Dec 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants