RFR: 8345314: Add a red–black tree as a utility data structure [v7]

Johan Sjölen jsjolen at openjdk.org
Tue Jan 7 16:01:59 UTC 2025


On Wed, 18 Dec 2024 10:46:53 GMT, Casper Norrbin <cnorrbin at openjdk.org> wrote:

>> Hi everyone,
>> 
>> This effort began as an exploration of replacing the current NMT treap with a red-black tree. Along the way, I discovered that others were also interested in having a general-purpose tree structure available within HotSpot.
>> 
>> The red-black tree is designed to serve as a drop-in replacement for the existing NMT treap, keeping a nearly identical interface. However, I’ve also added a few additional requested features, such as an iterator.
>> 
>> Testing builds off the treap tests, adding a few extra that inserts/removes and checks that the tree is correct. Testing uses the function `verify_self`, which iterates over the tree and checks that all red-black tree properties hold. Additionally, the tree has been tested in vmatree instead of the treap without any errors.
>> 
>> For those who may want to revisit the fundamentals of red-black trees, [Wikipedia](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) offers a great summary with tables covering the various balancing cases. Alternatively, your favorite data structure book could provide even more insight.
>
> Casper Norrbin has updated the pull request incrementally with one additional commit since the last revision:
> 
>   renamed coloring functions

src/hotspot/share/utilities/rbTree.hpp line 114:

> 112:     if (node_place == nullptr) {
> 113:       return nullptr;
> 114:     }

Remove this and instead `assert` that it can't be `nullptr` with a message saying that allocators must exit on failure.

src/hotspot/share/utilities/rbTree.hpp line 134:

> 132:   RBNode* find_node(RBNode* curr, const K& k);
> 133: 
> 134:   RBNode* insert_node(const K& k, const V& v);

Comment that it replaces `v` (as `upsert` does) if found.

src/hotspot/share/utilities/rbTree.hpp line 266:

> 264:     const RBTree* const _tree;
> 265:     GrowableArrayCHeap<RBNode*, mtInternal> _to_visit;
> 266:     RBNode* _next;

This isn't used?

src/hotspot/share/utilities/rbTree.hpp line 274:

> 272:     NONCOPYABLE(IteratorImpl);
> 273: 
> 274:     IteratorImpl(const RBTree* tree) : _tree(tree) {

Initialize `_to_visit` explicitly with a `2log(n)` where n is number of nodes, unnecessary resizing happens otherwise.

src/hotspot/share/utilities/rbTree.inline.hpp line 100:

> 98:     _right->visit_in_order_inner(f);
> 99:   }
> 100: }

This is basically fine, as stack depth will be at most `2log n`, but is there a reason that you can't use the iterative solution that's present in the Treap? Btw, having this method belonging to RBTree and take a `RBNode*` instead of RBNode allows you to just check if the current root node is null, letting you skip a check.

Here's `visit_in_order` as an iterative function:


  template<typename F>
  void visit_in_order(F f) const {
    GrowableArrayCHeap<TreapNode*, mtNMT> to_visit;
    TreapNode* head = _root;
    while (!to_visit.is_empty() || head != nullptr) {
      while (head != nullptr) {
        to_visit.push(head);
        head = head->left();
      }
      head = to_visit.pop();
      f(head);
      head = head->right();
    }
  }

src/hotspot/share/utilities/rbTree.inline.hpp line 172:

> 170:       curr = curr->_left;
> 171:     } else {                    // k > key
> 172:       curr = curr->_right;

Nit: Unnecessary comments, should be clear what they do.

src/hotspot/share/utilities/rbTree.inline.hpp line 203:

> 201:       curr = curr->_right;
> 202:     }
> 203:   }

Replace with `find_node` and write `parent` by reading `_parent`?

src/hotspot/share/utilities/rbTree.inline.hpp line 237:

> 235: 
> 236:     RBNode* uncle = parent->is_left_child() ? grandparent->_right : grandparent->_left;
> 237:     if (is_black(uncle)) { // Parent is red, uncle is black

And uncle has to exist (can't be null) as per some invariant? Add assert.

test/hotspot/gtest/utilities/test_rbtree.cpp line 300:

> 298:       EXPECT_EQ(reverse_iterator.next()->val(), n);
> 299:     }
> 300:   }

At least also test:

1. Iterator on empty tree works as expected
2. `has_next` is false after the loops

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905163526
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905433593
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905676241
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905675907
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905175777
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905431887
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905434902
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905437920
PR Review Comment: https://git.openjdk.org/jdk/pull/22360#discussion_r1905676906


More information about the hotspot-dev mailing list