]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: add iterator abstraction for mTreap
authorMichael Anthony Knyszek <mknyszek@google.com>
Mon, 26 Nov 2018 23:56:35 +0000 (23:56 +0000)
committerMichael Knyszek <mknyszek@google.com>
Mon, 17 Dec 2018 23:28:18 +0000 (23:28 +0000)
This change adds the treapIter type which provides an iterator
abstraction for walking over an mTreap. In particular, the mTreap type
now has iter() and rev() for iterating both forwards (smallest to
largest) and backwards (largest to smallest). It also has an erase()
method for erasing elements at the iterator's current position.

For #28479.

While the expectation is that this change will slow down Go programs,
the impact on Go1 and Garbage is negligible.

Go1:     https://perf.golang.org/search?q=upload:20181214.6
Garbage: https://perf.golang.org/search?q=upload:20181214.11

Change-Id: I60dbebbbe73cbbe7b78d45d2093cec12cc0bc649
Reviewed-on: https://go-review.googlesource.com/c/151537
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Rick Hudson <rlh@golang.org>
src/runtime/export_test.go
src/runtime/mgclarge.go
src/runtime/mheap.go

index ecb21935b9efe6348b03920d77df32e1986221a9..de66b07c68ebb48a1ecae4d6c54774ccee930496 100644 (file)
@@ -337,9 +337,9 @@ func ReadMemStatsSlow() (base, slow MemStats) {
                        slow.BySize[i].Frees = bySize[i].Frees
                }
 
-               mheap_.scav.treap.walkTreap(func(tn *treapNode) {
-                       slow.HeapReleased += uint64(tn.spanKey.released())
-               })
+               for i := mheap_.scav.iter(); i.valid(); i = i.next() {
+                       slow.HeapReleased += uint64(i.span().released())
+               }
 
                getg().m.mallocing--
        })
index 66259d4cdfd81c101aed1d039340cd0f743bf28f..663a37e3edcf9e76aa461167a1bf5cc14dbc5be6 100644 (file)
@@ -153,6 +153,70 @@ func checkTreapNode(t *treapNode) {
        }
 }
 
+// treapIter is a unidirectional iterator type which may be used to iterate over a
+// an mTreap in-order forwards (increasing order) or backwards (decreasing order).
+// Its purpose is to hide details about the treap from users when trying to iterate
+// over it.
+//
+// To create iterators over the treap, call iter or rev on an mTreap.
+type treapIter struct {
+       t   *treapNode
+       inc bool // if true, iterate in increasing order, otherwise decreasing order.
+}
+
+// span returns the span at the current position in the treap.
+// If the treap is not valid, span will panic.
+func (i *treapIter) span() *mspan {
+       return i.t.spanKey
+}
+
+// valid returns whether the iterator represents a valid position
+// in the mTreap.
+func (i *treapIter) valid() bool {
+       return i.t != nil
+}
+
+// next moves the iterator forward by one. Once the iterator
+// ceases to be valid, calling next will panic.
+func (i treapIter) next() treapIter {
+       if i.inc {
+               i.t = i.t.succ()
+       } else {
+               i.t = i.t.pred()
+       }
+       return i
+}
+
+// iter returns an iterator which may be used to iterate over the treap
+// in increasing order of span size ("forwards").
+func (root *mTreap) iter() treapIter {
+       i := treapIter{inc: true}
+       t := root.treap
+       if t == nil {
+               return i
+       }
+       for t.left != nil {
+               t = t.left
+       }
+       i.t = t
+       return i
+}
+
+// rev returns an iterator which may be used to iterate over the treap
+// in decreasing order of span size ("reverse").
+func (root *mTreap) rev() treapIter {
+       i := treapIter{inc: false}
+       t := root.treap
+       if t == nil {
+               return i
+       }
+       for t.right != nil {
+               t = t.right
+       }
+       i.t = t
+       return i
+}
+
 // insert adds span to the large span treap.
 func (root *mTreap) insert(span *mspan) {
        npages := span.npages
@@ -280,6 +344,16 @@ func (root *mTreap) removeSpan(span *mspan) {
        root.removeNode(t)
 }
 
+// erase removes the element referred to by the current position of the
+// iterator and returns i.next(). This operation consumes the given
+// iterator, so it should no longer be used and iteration should continue
+// from the returned iterator.
+func (root *mTreap) erase(i treapIter) treapIter {
+       n := i.next()
+       root.removeNode(i.t)
+       return n
+}
+
 // rotateLeft rotates the tree rooted at node x.
 // turning (x a (y b c)) into (y (x a b) c).
 func (root *mTreap) rotateLeft(x *treapNode) {
index 99994593c3934994f3316912579fbd4fc620899b..b5e0b0f306625cf3c17756296ebdbc286dfa7862 100644 (file)
@@ -1273,21 +1273,11 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
 // starting from the largest span and working down. It then takes those spans
 // and places them in scav. h must be locked.
 func (h *mheap) scavengeLargest(nbytes uintptr) {
-       // Find the largest child.
-       t := h.free.treap
-       if t == nil {
-               return
-       }
-       for t.right != nil {
-               t = t.right
-       }
-       // Iterate over the treap from the largest child to the smallest by
-       // starting from the largest and finding its predecessor until we've
-       // recovered nbytes worth of physical memory, or it no longer has a
-       // predecessor (meaning the treap is now empty).
+       // Iterate over the treap backwards (from largest to smallest) scavenging spans
+       // until we've reached our quota of nbytes.
        released := uintptr(0)
-       for t != nil && released < nbytes {
-               s := t.spanKey
+       for t := h.free.rev(); released < nbytes && t.valid(); {
+               s := t.span()
                r := s.scavenge()
                if r == 0 {
                        // Since we're going in order of largest-to-smallest span, this
@@ -1301,9 +1291,7 @@ func (h *mheap) scavengeLargest(nbytes uintptr) {
                        // those which have it unset are only in the `free` treap.
                        return
                }
-               prev := t.pred()
-               h.free.removeNode(t)
-               t = prev
+               t = h.free.erase(t)
                h.scav.insert(s)
                released += r
        }
@@ -1313,34 +1301,20 @@ func (h *mheap) scavengeLargest(nbytes uintptr) {
 // treapNode's span. It then removes the scavenged span from
 // unscav and adds it into scav before continuing. h must be locked.
 func (h *mheap) scavengeAll(now, limit uint64) uintptr {
-       // Compute the left-most child in unscav to start iteration from.
-       t := h.free.treap
-       if t == nil {
-               return 0
-       }
-       for t.left != nil {
-               t = t.left
-       }
-       // Iterate over the treap be computing t's successor before
-       // potentially scavenging it.
+       // Iterate over the treap scavenging spans if unused for at least limit time.
        released := uintptr(0)
-       for t != nil {
-               s := t.spanKey
-               next := t.succ()
+       for t := h.free.iter(); t.valid(); {
+               s := t.span()
                if (now - uint64(s.unusedsince)) > limit {
                        r := s.scavenge()
                        if r != 0 {
-                               // If we ended up scavenging s, then remove it from unscav
-                               // and add it to scav. This is safe to do since we've already
-                               // moved to t's successor.
-                               h.free.removeNode(t)
+                               t = h.free.erase(t)
                                h.scav.insert(s)
                                released += r
+                               continue
                        }
                }
-               // Move t forward to its successor to iterate over the whole
-               // treap.
-               t = next
+               t = t.next()
        }
        return released
 }