|
@@ -2,6 +2,7 @@ package rbtree
|
|
|
|
|
|
import (
|
|
|
"math"
|
|
|
+ "sync"
|
|
|
)
|
|
|
|
|
|
//
|
|
@@ -16,8 +17,12 @@ type Item struct {
|
|
|
|
|
|
// Allocator is the allocator for nodes in a RBTree.
|
|
|
type Allocator struct {
|
|
|
+ HibernationThreshold int
|
|
|
+
|
|
|
storage []node
|
|
|
gaps map[uint32]bool
|
|
|
+ hibernatedStorage [6][]byte
|
|
|
+ hibernatedLen int
|
|
|
}
|
|
|
|
|
|
// NewAllocator creates a new allocator for RBTree's nodes.
|
|
@@ -51,6 +56,71 @@ func (allocator *Allocator) Clone() *Allocator {
|
|
|
return newAllocator
|
|
|
}
|
|
|
|
|
|
+// Hibernate compresses the allocated memory.
|
|
|
+func (allocator *Allocator) Hibernate() {
|
|
|
+ if allocator.HibernationThreshold == 0 || len(allocator.storage) < allocator.HibernationThreshold {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ allocator.hibernatedLen = len(allocator.storage)
|
|
|
+ buffers := [6][]uint32{}
|
|
|
+ for i := 0; i < len(buffers); i++ {
|
|
|
+ buffers[i] = make([]uint32, len(allocator.storage))
|
|
|
+ }
|
|
|
+ // we deinterleave to achieve a better compression ratio
|
|
|
+ for i, n := range allocator.storage {
|
|
|
+ buffers[0][i] = n.item.Key
|
|
|
+ buffers[1][i] = n.item.Value
|
|
|
+ buffers[2][i] = n.left
|
|
|
+ buffers[3][i] = n.parent
|
|
|
+ buffers[4][i] = n.right
|
|
|
+ if n.color {
|
|
|
+ buffers[5][i] = 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ allocator.storage = nil
|
|
|
+ wg := &sync.WaitGroup{}
|
|
|
+ wg.Add(len(buffers))
|
|
|
+ for i, buffer := range buffers {
|
|
|
+ go func(i int, buffer []uint32) {
|
|
|
+ allocator.hibernatedStorage[i] = CompressUInt32Slice(buffer)
|
|
|
+ buffers[i] = nil
|
|
|
+ wg.Done()
|
|
|
+ }(i, buffer)
|
|
|
+ }
|
|
|
+ wg.Wait()
|
|
|
+}
|
|
|
+
|
|
|
+// Boot performs the opposite of Hibernate() - decompresses and restores the allocated memory.
|
|
|
+func (allocator *Allocator) Boot() {
|
|
|
+ if allocator.hibernatedLen == 0 {
|
|
|
+ // not hibernated
|
|
|
+ return
|
|
|
+ }
|
|
|
+ buffers := [6][]uint32{}
|
|
|
+ wg := &sync.WaitGroup{}
|
|
|
+ wg.Add(len(buffers))
|
|
|
+ for i := 0; i < len(buffers); i++ {
|
|
|
+ go func(i int) {
|
|
|
+ buffers[i] = make([]uint32, allocator.hibernatedLen)
|
|
|
+ DecompressUInt32Slice(allocator.hibernatedStorage[i], buffers[i])
|
|
|
+ allocator.hibernatedStorage[i] = nil
|
|
|
+ wg.Done()
|
|
|
+ }(i)
|
|
|
+ }
|
|
|
+ wg.Wait()
|
|
|
+ allocator.storage = make([]node, allocator.hibernatedLen, (allocator.hibernatedLen * 3) / 2)
|
|
|
+ for i := range allocator.storage {
|
|
|
+ n := &allocator.storage[i]
|
|
|
+ n.item.Key = buffers[0][i]
|
|
|
+ n.item.Value = buffers[1][i]
|
|
|
+ n.left = buffers[2][i]
|
|
|
+ n.parent = buffers[3][i]
|
|
|
+ n.right = buffers[4][i]
|
|
|
+ n.color = buffers[5][i] > 0
|
|
|
+ }
|
|
|
+ allocator.hibernatedLen = 0
|
|
|
+}
|
|
|
+
|
|
|
func (allocator *Allocator) malloc() uint32 {
|
|
|
if len(allocator.gaps) > 0 {
|
|
|
var key uint32
|