1
0
mirror of https://github.com/golang/go synced 2024-11-13 16:50:23 -07:00

runtime: add iterator abstraction for mTreap

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>
This commit is contained in:
Michael Anthony Knyszek 2018-11-26 23:56:35 +00:00 committed by Michael Knyszek
parent 9a3c1a1bc8
commit 3651476075
3 changed files with 88 additions and 40 deletions

View File

@ -337,9 +337,9 @@ func ReadMemStatsSlow() (base, slow MemStats) {
slow.BySize[i].Frees = bySize[i].Frees slow.BySize[i].Frees = bySize[i].Frees
} }
mheap_.scav.treap.walkTreap(func(tn *treapNode) { for i := mheap_.scav.iter(); i.valid(); i = i.next() {
slow.HeapReleased += uint64(tn.spanKey.released()) slow.HeapReleased += uint64(i.span().released())
}) }
getg().m.mallocing-- getg().m.mallocing--
}) })

View 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. // insert adds span to the large span treap.
func (root *mTreap) insert(span *mspan) { func (root *mTreap) insert(span *mspan) {
npages := span.npages npages := span.npages
@ -280,6 +344,16 @@ func (root *mTreap) removeSpan(span *mspan) {
root.removeNode(t) 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. // rotateLeft rotates the tree rooted at node x.
// turning (x a (y b c)) into (y (x a b) c). // turning (x a (y b c)) into (y (x a b) c).
func (root *mTreap) rotateLeft(x *treapNode) { func (root *mTreap) rotateLeft(x *treapNode) {

View 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 // starting from the largest span and working down. It then takes those spans
// and places them in scav. h must be locked. // and places them in scav. h must be locked.
func (h *mheap) scavengeLargest(nbytes uintptr) { func (h *mheap) scavengeLargest(nbytes uintptr) {
// Find the largest child. // Iterate over the treap backwards (from largest to smallest) scavenging spans
t := h.free.treap // until we've reached our quota of nbytes.
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).
released := uintptr(0) released := uintptr(0)
for t != nil && released < nbytes { for t := h.free.rev(); released < nbytes && t.valid(); {
s := t.spanKey s := t.span()
r := s.scavenge() r := s.scavenge()
if r == 0 { if r == 0 {
// Since we're going in order of largest-to-smallest span, this // 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. // those which have it unset are only in the `free` treap.
return return
} }
prev := t.pred() t = h.free.erase(t)
h.free.removeNode(t)
t = prev
h.scav.insert(s) h.scav.insert(s)
released += r released += r
} }
@ -1313,34 +1301,20 @@ func (h *mheap) scavengeLargest(nbytes uintptr) {
// treapNode's span. It then removes the scavenged span from // treapNode's span. It then removes the scavenged span from
// unscav and adds it into scav before continuing. h must be locked. // unscav and adds it into scav before continuing. h must be locked.
func (h *mheap) scavengeAll(now, limit uint64) uintptr { func (h *mheap) scavengeAll(now, limit uint64) uintptr {
// Compute the left-most child in unscav to start iteration from. // Iterate over the treap scavenging spans if unused for at least limit time.
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.
released := uintptr(0) released := uintptr(0)
for t != nil { for t := h.free.iter(); t.valid(); {
s := t.spanKey s := t.span()
next := t.succ()
if (now - uint64(s.unusedsince)) > limit { if (now - uint64(s.unusedsince)) > limit {
r := s.scavenge() r := s.scavenge()
if r != 0 { if r != 0 {
// If we ended up scavenging s, then remove it from unscav t = h.free.erase(t)
// and add it to scav. This is safe to do since we've already
// moved to t's successor.
h.free.removeNode(t)
h.scav.insert(s) h.scav.insert(s)
released += r released += r
continue
} }
} }
// Move t forward to its successor to iterate over the whole t = t.next()
// treap.
t = next
} }
return released return released
} }