-
Notifications
You must be signed in to change notification settings - Fork 2.5k
treap, ffldb: add treapNodePool #2425
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
Open
kcalvinalvin
wants to merge
5
commits into
btcsuite:master
Choose a base branch
from
kcalvinalvin:2025-09-20-add-treapnode-pool
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+284
−96
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
26ef499
treap: refactor the Put() code into exported and unexported versions
kcalvinalvin e3d237d
treap: add treapNodePool and fetch from it for cloneTreapNode and
kcalvinalvin 65d3968
treap: return newly created treapNodes in put()
kcalvinalvin 0e95ae3
treap: add recycle() method on treapNode
kcalvinalvin e009124
treap, ffldb: change Put() to take in multiple key-value pairs
kcalvinalvin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,13 +11,14 @@ import ( | |
|
|
||
| // cloneTreapNode returns a shallow copy of the passed node. | ||
| func cloneTreapNode(node *treapNode) *treapNode { | ||
| return &treapNode{ | ||
| key: node.key, | ||
| value: node.value, | ||
| priority: node.priority, | ||
| left: node.left, | ||
| right: node.right, | ||
| } | ||
| n := treapNodePool.Get().(*treapNode) | ||
| n.key = node.key | ||
| n.value = node.value | ||
| n.priority = node.priority | ||
| n.left = node.left | ||
| n.right = node.right | ||
|
|
||
| return n | ||
| } | ||
|
|
||
| // Immutable represents a treap data structure which is used to hold ordered | ||
|
|
@@ -104,19 +105,79 @@ func (t *Immutable) Get(key []byte) []byte { | |
| return nil | ||
| } | ||
|
|
||
| // Put inserts the passed key/value pair. | ||
| func (t *Immutable) Put(key, value []byte) *Immutable { | ||
| // KVPair is just a helper struct for a key-value pair that's going to be | ||
| // inserted into the treap. | ||
| type KVPair struct { | ||
| Key []byte | ||
| Value []byte | ||
| } | ||
|
|
||
| // Put puts the passed in key/value pairs into the treap. For operations | ||
| // requiring many insertions at once, Put is memory efficient as the | ||
| // intermediary treap nodes created between each put operation is recycled | ||
| // through an internal sync.Pool, reducing overall memory allocation. | ||
| func (t *Immutable) Put(kvPairs ...KVPair) *Immutable { | ||
| treap := t | ||
| var prevTreapNodes [staticDepth]*treapNode | ||
|
|
||
| for _, kvPair := range kvPairs { | ||
| newTreap, newTreapNodes := treap.put(kvPair.Key, kvPair.Value) | ||
|
|
||
| // Loop through the prevTreapNodes and check for treapNodes that | ||
| // are no longer being utilized. These will be garbaged collected | ||
| // and they're better off being recycled in the treapNodePool. | ||
| for _, node := range prevTreapNodes { | ||
| if node == nil { | ||
| break | ||
| } | ||
|
|
||
| // Make sure that the node we're going to recycle isn't | ||
| // being used by the latest immutable treap by checking | ||
| // if the pointer value of the node is the same. | ||
| got := newTreap.get(node.key) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| if got == node { | ||
| continue | ||
| } | ||
|
|
||
| // This node is only being used by the previous immutable | ||
| // copy and can safely be put into the treapNodePool to be | ||
| // recycled. | ||
| node.recycle() | ||
| } | ||
|
|
||
| // Replace with the latest treap and treap nodes. | ||
| treap = newTreap | ||
| prevTreapNodes = newTreapNodes | ||
| } | ||
|
|
||
| return treap | ||
| } | ||
|
|
||
| // put inserts the passed key/value pair and returns all the newly created | ||
| // treapNodes that were created during this put operation. The returned | ||
| // treapNodes can then be put into data structures like sync.Pool to reduce the | ||
| // memory overhead of allocating new treapNodes during multiple put calls. | ||
| func (t *Immutable) put(key, value []byte) (*Immutable, [staticDepth]*treapNode) { | ||
Roasbeef marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Use an empty byte slice for the value when none was provided. This | ||
| // ultimately allows key existence to be determined from the value since | ||
| // an empty byte slice is distinguishable from nil. | ||
| if value == nil { | ||
| value = emptySlice | ||
| } | ||
|
|
||
| // recycle is the treapNodes that are created during this put operation. | ||
| // We keep track of the nodes as the caller may be choose to recycle | ||
| // them to keep memory allocation low. | ||
| var ( | ||
| recycle [staticDepth]*treapNode | ||
| currentRecycleIndex int | ||
| ) | ||
|
|
||
| // The node is the root of the tree if there isn't already one. | ||
| if t.root == nil { | ||
| root := newTreapNode(key, value, rand.Int()) | ||
| return newImmutable(root, 1, nodeSize(root)) | ||
| recycle[currentRecycleIndex] = root | ||
| return newImmutable(root, 1, nodeSize(root)), recycle | ||
| } | ||
|
|
||
| // Find the binary tree insertion point and construct a replaced list of | ||
|
|
@@ -132,6 +193,16 @@ func (t *Immutable) Put(key, value []byte) *Immutable { | |
| for node := t.root; node != nil; { | ||
| // Clone the node and link its parent to it if needed. | ||
| nodeCopy := cloneTreapNode(node) | ||
|
|
||
| // Check if we still have space in the recycle for this node. | ||
| // It's ok if we don't put every single new node to be recycled | ||
| // as there's no guarantee in the sync.Pool that every recycled | ||
| // treapNode will be re-utilized. | ||
| if currentRecycleIndex < staticDepth { | ||
| recycle[currentRecycleIndex] = nodeCopy | ||
| currentRecycleIndex++ | ||
| } | ||
|
|
||
| if oldParent := parents.At(0); oldParent != nil { | ||
| if oldParent.left == node { | ||
| oldParent.left = nodeCopy | ||
|
|
@@ -161,11 +232,20 @@ func (t *Immutable) Put(key, value []byte) *Immutable { | |
| newRoot := parents.At(parents.Len() - 1) | ||
| newTotalSize := t.totalSize - uint64(len(node.value)) + | ||
| uint64(len(value)) | ||
| return newImmutable(newRoot, t.count, newTotalSize) | ||
| return newImmutable(newRoot, t.count, newTotalSize), recycle | ||
| } | ||
|
|
||
| // Link the new node into the binary tree in the correct position. | ||
| // Check if we still have space in the recycle for this node. | ||
| // It's ok if we don't put every single new node to be recycled | ||
| // as there's no guarantee in the sync.Pool that every recycled | ||
| // treapNode will be re-utilized. | ||
| node := newTreapNode(key, value, rand.Int()) | ||
| if currentRecycleIndex < staticDepth { | ||
| recycle[currentRecycleIndex] = node | ||
| currentRecycleIndex++ | ||
| } | ||
|
|
||
| // Link the new node into the binary tree in the correct position. | ||
| parent := parents.At(0) | ||
| if compareResult < 0 { | ||
| parent.left = node | ||
|
|
@@ -205,7 +285,7 @@ func (t *Immutable) Put(key, value []byte) *Immutable { | |
| } | ||
| } | ||
|
|
||
| return newImmutable(newRoot, t.count+1, t.totalSize+nodeSize(node)) | ||
| return newImmutable(newRoot, t.count+1, t.totalSize+nodeSize(node)), recycle | ||
| } | ||
|
|
||
| // Delete removes the passed key from the treap and returns the resulting treap | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.