github.com/grafana/pyroscope@v1.18.0/pkg/util/loser/tree.go (about)

     1  // Loser tree, from https://en.wikipedia.org/wiki/K-way_merge_algorithm#Tournament_Tree
     2  
     3  package loser
     4  
     5  type Sequence interface {
     6  	Next() bool // Advances and returns true if there is a value at this new position.
     7  	Err() error // Returns any error encountered while advancing.
     8  }
     9  
    10  // New returns a new loser tree that merges the given sequences.
    11  // The sequences must be sorted according to the less function already.
    12  // The maxVal is used to initialize the tree.
    13  // The at function returns the current value of the sequence.
    14  // The less function compares two values.
    15  // The close function is called on each sequence when the tree is closed or when a sequence returns false from Next().
    16  // If any sequence returns an error from Err() after Next() = false, the tree will stop and return that error in Err().
    17  // Examples:
    18  //
    19  //	tree := loser.New(...)
    20  //	defer tree.Close()
    21  //	for tree.Next() {
    22  //		value := tree.Winner().At()
    23  //		...
    24  //	}
    25  //	if err := tree.Err(); err != nil {
    26  //		...
    27  //	}
    28  func New[E any, S Sequence](sequences []S, maxVal E, at func(S) E, less func(E, E) bool, close func(S)) *Tree[E, S] {
    29  	nSequences := len(sequences)
    30  	t := Tree[E, S]{
    31  		maxVal: maxVal,
    32  		at:     at,
    33  		less:   less,
    34  		close:  close,
    35  		nodes:  make([]node[E, S], nSequences*2),
    36  	}
    37  	for i, s := range sequences {
    38  		t.nodes[i+nSequences].items = s
    39  		if !t.moveNext(i + nSequences) { // Must call Next on each item so that At() has a value.
    40  			if t.err != nil {
    41  				// error during initialize, requires us to close sequences not touched yet and mark nodes as uninitialized
    42  				for j := i + 1; j < nSequences; j++ {
    43  					t.close(sequences[j])
    44  					t.nodes[j+nSequences].index = -1
    45  				}
    46  				break
    47  			}
    48  		}
    49  	}
    50  	if nSequences > 0 {
    51  		t.nodes[0].index = -1 // flag to be initialized on first call to Next().
    52  	}
    53  	return &t
    54  }
    55  
    56  // Call the close function on all sequences that are still open.
    57  func (t *Tree[E, S]) Close() {
    58  	for _, e := range t.nodes[len(t.nodes)/2 : len(t.nodes)] {
    59  		if e.index == -1 {
    60  			continue
    61  		}
    62  		t.close(e.items)
    63  	}
    64  }
    65  
    66  // A loser tree is a binary tree laid out such that nodes N and N+1 have parent N/2.
    67  // We store M leaf nodes in positions M...2M-1, and M-1 internal nodes in positions 1..M-1.
    68  // Node 0 is a special node, containing the winner of the contest.
    69  type Tree[E any, S Sequence] struct {
    70  	maxVal E
    71  	at     func(S) E
    72  	less   func(E, E) bool
    73  	close  func(S) // Called when Next() returns false.
    74  	nodes  []node[E, S]
    75  
    76  	err error
    77  }
    78  
    79  type node[E any, S Sequence] struct {
    80  	index int // This is the loser for all nodes except the 0th, where it is the winner.
    81  	value E   // Value copied from the loser node, or winner for node 0.
    82  	items S   // Only populated for leaf nodes.
    83  }
    84  
    85  func (t *Tree[E, S]) moveNext(index int) bool {
    86  	n := &t.nodes[index]
    87  	if n.items.Next() {
    88  		n.value = t.at(n.items)
    89  		return true
    90  	}
    91  	t.close(n.items) // Next() returned false; close it and mark as finished.
    92  	t.err = n.items.Err()
    93  	n.value = t.maxVal
    94  	n.index = -1
    95  	return false
    96  }
    97  
    98  func (t *Tree[E, S]) Winner() S {
    99  	return t.nodes[t.nodes[0].index].items
   100  }
   101  
   102  func (t *Tree[E, S]) Next() bool {
   103  	if len(t.nodes) == 0 || t.err != nil {
   104  		return false
   105  	}
   106  	if t.nodes[0].index == -1 { // If tree has not been initialized yet, do that.
   107  		t.initialize()
   108  		return t.nodes[t.nodes[0].index].index != -1
   109  	}
   110  	if t.nodes[t.nodes[0].index].index == -1 { // already exhausted
   111  		return false
   112  	}
   113  	if !t.moveNext(t.nodes[0].index) && t.err != nil {
   114  		return false
   115  	}
   116  	t.replayGames(t.nodes[0].index)
   117  	return t.nodes[t.nodes[0].index].index != -1
   118  }
   119  
   120  func (t *Tree[E, S]) Err() error {
   121  	return t.err
   122  }
   123  
   124  func (t *Tree[E, S]) initialize() {
   125  	winners := make([]int, len(t.nodes))
   126  	// Initialize leaf nodes as winners to start.
   127  	for i := len(t.nodes) / 2; i < len(t.nodes); i++ {
   128  		winners[i] = i
   129  	}
   130  	for i := len(t.nodes) - 2; i > 0; i -= 2 {
   131  		// At each stage the winners play each other, and we record the loser in the node.
   132  		loser, winner := t.playGame(winners[i], winners[i+1])
   133  		p := parent(i)
   134  		t.nodes[p].index = loser
   135  		t.nodes[p].value = t.nodes[loser].value
   136  		winners[p] = winner
   137  	}
   138  	t.nodes[0].index = winners[1]
   139  	t.nodes[0].value = t.nodes[winners[1]].value
   140  }
   141  
   142  // Starting at pos, re-consider all values up to the root.
   143  func (t *Tree[E, S]) replayGames(pos int) {
   144  	// At the start, pos is a leaf node, and is the winner at that level.
   145  	n := parent(pos)
   146  	for n != 0 {
   147  		if t.less(t.nodes[n].value, t.nodes[pos].value) {
   148  			loser := pos
   149  			// Record pos as the loser here, and the old loser is the new winner.
   150  			pos = t.nodes[n].index
   151  			t.nodes[n].index = loser
   152  			t.nodes[n].value = t.nodes[loser].value
   153  		}
   154  		n = parent(n)
   155  	}
   156  	// pos is now the winner; store it in node 0.
   157  	t.nodes[0].index = pos
   158  	t.nodes[0].value = t.nodes[pos].value
   159  }
   160  
   161  func (t *Tree[E, S]) playGame(a, b int) (loser, winner int) {
   162  	if t.less(t.nodes[a].value, t.nodes[b].value) {
   163  		return b, a
   164  	}
   165  	return a, b
   166  }
   167  
   168  func parent(i int) int { return i / 2 }
   169  
   170  // Add a new sequence to the merge set
   171  func (t *Tree[E, S]) Push(sequence S) error {
   172  	// First, see if we can replace one that was previously finished.
   173  	for newPos := len(t.nodes) / 2; newPos < len(t.nodes); newPos++ {
   174  		if t.nodes[newPos].index == -1 {
   175  			t.nodes[newPos].index = newPos
   176  			t.nodes[newPos].items = sequence
   177  			if !t.moveNext(newPos) {
   178  				if t.err != nil {
   179  					return t.err
   180  				}
   181  			}
   182  			t.nodes[0].index = -1 // flag for re-initialize on next call to Next()
   183  			return nil
   184  		}
   185  	}
   186  	// We need to expand the tree. Pick the next biggest power of 2 to amortise resizing cost.
   187  	size := 1
   188  	for ; size <= len(t.nodes)/2; size *= 2 {
   189  	}
   190  	newPos := size + len(t.nodes)/2
   191  	newNodes := make([]node[E, S], size*2)
   192  	// Copy data over and fix up the indexes.
   193  	for i, n := range t.nodes[len(t.nodes)/2:] {
   194  		newNodes[i+size] = n
   195  		newNodes[i+size].index = i + size
   196  	}
   197  	t.nodes = newNodes
   198  	t.nodes[newPos].index = newPos
   199  	t.nodes[newPos].items = sequence
   200  	// Mark all the empty nodes we have added as finished.
   201  	for i := newPos + 1; i < len(t.nodes); i++ {
   202  		t.nodes[i].index = -1
   203  		t.nodes[i].value = t.maxVal
   204  	}
   205  	if !t.moveNext(newPos) {
   206  		if t.err != nil {
   207  			return t.err
   208  		}
   209  	}
   210  	t.nodes[0].index = -1 // flag for re-initialize on next call to Next()
   211  	return nil
   212  }