Commit 40036a99 authored by Michael Anthony Knyszek's avatar Michael Anthony Knyszek Committed by Michael Knyszek

runtime: change the span allocation policy to first-fit

This change modifies the treap implementation to be address-ordered
instead of size-ordered, and further augments it so it may be used for
allocation. It then modifies the find method to implement a first-fit
allocation policy.

This change to the treap implementation consequently makes it so that
spans are scavenged in highest-address-first order without any
additional changes to the scavenging code. Because the treap itself is
now address ordered, and the scavenging code iterates over it in
reverse, the highest address is now chosen instead of the largest span.

This change also renames the now wrongly-named "scavengeLargest" method
on mheap to just "scavengeLocked" and also fixes up logic in that method
which made assumptions about size.

For #30333.

Change-Id: I94b6f3209211cc1bfdc8cdaea04152a232cfbbb4
Reviewed-on: https://go-review.googlesource.com/c/go/+/164101
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAustin Clements <austin@google.com>
parent e30aa166
...@@ -627,4 +627,5 @@ func (t *Treap) Size() int { ...@@ -627,4 +627,5 @@ func (t *Treap) Size() int {
func (t *Treap) CheckInvariants() { func (t *Treap) CheckInvariants() {
t.mTreap.treap.walkTreap(checkTreapNode) t.mTreap.treap.walkTreap(checkTreapNode)
t.mTreap.treap.validateMaxPages()
} }
...@@ -6,24 +6,28 @@ ...@@ -6,24 +6,28 @@
// //
// See malloc.go for the general overview. // See malloc.go for the general overview.
// //
// Large spans are the subject of this file. Spans consisting of less than // Allocation policy is the subject of this file. All free spans live in
// _MaxMHeapLists are held in lists of like sized spans. Larger spans // a treap for most of their time being free. See
// are held in a treap. See https://en.wikipedia.org/wiki/Treap or // https://en.wikipedia.org/wiki/Treap or
// https://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview. // https://faculty.washington.edu/aragon/pubs/rst89.pdf for an overview.
// sema.go also holds an implementation of a treap. // sema.go also holds an implementation of a treap.
// //
// Each treapNode holds a single span. The treap is sorted by page size // Each treapNode holds a single span. The treap is sorted by base address
// and for spans of the same size a secondary sort based on start address // and each span necessarily has a unique base address.
// is done. // Spans are returned based on a first-fit algorithm, acquiring the span
// Spans are returned based on a best fit algorithm and for spans of the same // with the lowest base address which still satisfies the request.
// size the one at the lowest address is selected. //
// The first-fit algorithm is possible due to an augmentation of each
// treapNode to maintain the size of the largest span in the subtree rooted
// at that treapNode. Below we refer to this invariant as the maxPages
// invariant.
// //
// The primary routines are // The primary routines are
// insert: adds a span to the treap // insert: adds a span to the treap
// remove: removes the span from that treap that best fits the required size // remove: removes the span from that treap that best fits the required size
// removeSpan: which removes a specific span from the treap // removeSpan: which removes a specific span from the treap
// //
// _mheap.lock must be held when manipulating this data structure. // mheap_.lock must be held when manipulating this data structure.
package runtime package runtime
...@@ -41,11 +45,26 @@ type treapNode struct { ...@@ -41,11 +45,26 @@ type treapNode struct {
right *treapNode // all treapNodes > this treap node right *treapNode // all treapNodes > this treap node
left *treapNode // all treapNodes < this treap node left *treapNode // all treapNodes < this treap node
parent *treapNode // direct parent of this node, nil if root parent *treapNode // direct parent of this node, nil if root
npagesKey uintptr // number of pages in spanKey, used as primary sort key key uintptr // base address of the span, used as primary sort key
spanKey *mspan // span of size npagesKey, used as secondary sort key span *mspan // span at base address key
maxPages uintptr // the maximum size of any span in this subtree, including the root
priority uint32 // random number used by treap algorithm to keep tree probabilistically balanced priority uint32 // random number used by treap algorithm to keep tree probabilistically balanced
} }
// recomputeMaxPages is a helper method which has a node
// recompute its own maxPages value by looking at its own
// span's length as well as the maxPages value of its
// direct children.
func (t *treapNode) recomputeMaxPages() {
t.maxPages = t.span.npages
if t.left != nil && t.maxPages < t.left.maxPages {
t.maxPages = t.left.maxPages
}
if t.right != nil && t.maxPages < t.right.maxPages {
t.maxPages = t.right.maxPages
}
}
func (t *treapNode) pred() *treapNode { func (t *treapNode) pred() *treapNode {
if t.left != nil { if t.left != nil {
// If it has a left child, its predecessor will be // If it has a left child, its predecessor will be
...@@ -70,7 +89,7 @@ func (t *treapNode) pred() *treapNode { ...@@ -70,7 +89,7 @@ func (t *treapNode) pred() *treapNode {
// has no predecessor. // has no predecessor.
for t.parent != nil && t.parent.right != t { for t.parent != nil && t.parent.right != t {
if t.parent.left != t { if t.parent.left != t {
println("runtime: predecessor t=", t, "t.spanKey=", t.spanKey) println("runtime: predecessor t=", t, "t.span=", t.span)
throw("node is not its parent's child") throw("node is not its parent's child")
} }
t = t.parent t = t.parent
...@@ -91,7 +110,7 @@ func (t *treapNode) succ() *treapNode { ...@@ -91,7 +110,7 @@ func (t *treapNode) succ() *treapNode {
// See pred. // See pred.
for t.parent != nil && t.parent.left != t { for t.parent != nil && t.parent.left != t {
if t.parent.right != t { if t.parent.right != t {
println("runtime: predecessor t=", t, "t.spanKey=", t.spanKey) println("runtime: predecessor t=", t, "t.span=", t.span)
throw("node is not its parent's child") throw("node is not its parent's child")
} }
t = t.parent t = t.parent
...@@ -105,10 +124,10 @@ func (t *treapNode) isSpanInTreap(s *mspan) bool { ...@@ -105,10 +124,10 @@ func (t *treapNode) isSpanInTreap(s *mspan) bool {
if t == nil { if t == nil {
return false return false
} }
return t.spanKey == s || t.left.isSpanInTreap(s) || t.right.isSpanInTreap(s) return t.span == s || t.left.isSpanInTreap(s) || t.right.isSpanInTreap(s)
} }
// walkTreap is handy for debugging. // walkTreap is handy for debugging and testing.
// Starting at some treapnode t, for example the root, do a depth first preorder walk of // Starting at some treapnode t, for example the root, do a depth first preorder walk of
// the tree executing fn at each treap node. One should hold the heap lock, usually // the tree executing fn at each treap node. One should hold the heap lock, usually
// mheap_.lock(). // mheap_.lock().
...@@ -124,36 +143,46 @@ func (t *treapNode) walkTreap(fn func(tn *treapNode)) { ...@@ -124,36 +143,46 @@ func (t *treapNode) walkTreap(fn func(tn *treapNode)) {
// checkTreapNode when used in conjunction with walkTreap can usually detect a // checkTreapNode when used in conjunction with walkTreap can usually detect a
// poorly formed treap. // poorly formed treap.
func checkTreapNode(t *treapNode) { func checkTreapNode(t *treapNode) {
// lessThan is used to order the treap.
// npagesKey and npages are the primary keys.
// spanKey and span are the secondary keys.
// span == nil (0) will always be lessThan all
// spans of the same size.
lessThan := func(npages uintptr, s *mspan) bool {
if t.npagesKey != npages {
return t.npagesKey < npages
}
// t.npagesKey == npages
return t.spanKey.base() < s.base()
}
if t == nil { if t == nil {
return return
} }
if t.spanKey.next != nil || t.spanKey.prev != nil || t.spanKey.list != nil { if t.span.next != nil || t.span.prev != nil || t.span.list != nil {
throw("span may be on an mSpanList while simultaneously in the treap") throw("span may be on an mSpanList while simultaneously in the treap")
} }
if t.spanKey.npages != t.npagesKey { if t.span.base() != t.key {
println("runtime: checkTreapNode treapNode t=", t, " t.npagesKey=", t.npagesKey, println("runtime: checkTreapNode treapNode t=", t, " t.key=", t.key,
"t.spanKey.npages=", t.spanKey.npages) "t.span.base()=", t.span.base())
throw("span.npages and treap.npagesKey do not match") throw("why does span.base() and treap.key do not match?")
}
if t.left != nil && t.key < t.left.key {
throw("found out-of-order spans in treap (left child has greater base address)")
}
if t.right != nil && t.key > t.right.key {
throw("found out-of-order spans in treap (right child has lesser base address)")
} }
if t.left != nil && lessThan(t.left.npagesKey, t.left.spanKey) { }
throw("t.lessThan(t.left.npagesKey, t.left.spanKey) is not false")
// validateMaxPages is handy for debugging and testing.
// It ensures that the maxPages field is appropriately maintained throughout
// the treap by walking the treap in a post-order manner.
func (t *treapNode) validateMaxPages() uintptr {
if t == nil {
return 0
}
leftMax := t.left.validateMaxPages()
rightMax := t.right.validateMaxPages()
max := t.span.npages
if leftMax > max {
max = leftMax
}
if rightMax > max {
max = rightMax
} }
if t.right != nil && !lessThan(t.right.npagesKey, t.right.spanKey) { if max != t.maxPages {
throw("!t.lessThan(t.left.npagesKey, t.left.spanKey) is not false") println("runtime: t.maxPages=", t.maxPages, "want=", max)
throw("maxPages invariant violated in treap")
} }
return max
} }
// treapIter is a bidirectional iterator type which may be used to iterate over a // treapIter is a bidirectional iterator type which may be used to iterate over a
...@@ -169,7 +198,7 @@ type treapIter struct { ...@@ -169,7 +198,7 @@ type treapIter struct {
// span returns the span at the current position in the treap. // span returns the span at the current position in the treap.
// If the treap is not valid, span will panic. // If the treap is not valid, span will panic.
func (i *treapIter) span() *mspan { func (i *treapIter) span() *mspan {
return i.t.spanKey return i.t.span
} }
// valid returns whether the iterator represents a valid position // valid returns whether the iterator represents a valid position
...@@ -220,19 +249,14 @@ func (root *mTreap) end() treapIter { ...@@ -220,19 +249,14 @@ func (root *mTreap) end() treapIter {
// 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 base := span.base()
var last *treapNode var last *treapNode
pt := &root.treap pt := &root.treap
for t := *pt; t != nil; t = *pt { for t := *pt; t != nil; t = *pt {
last = t last = t
if t.npagesKey < npages { if t.key < base {
pt = &t.right pt = &t.right
} else if t.npagesKey > npages { } else if t.key > base {
pt = &t.left
} else if t.spanKey.base() < span.base() {
// t.npagesKey == npages, so sort on span addresses.
pt = &t.right
} else if t.spanKey.base() > span.base() {
pt = &t.left pt = &t.left
} else { } else {
throw("inserting span already in treap") throw("inserting span already in treap")
...@@ -241,25 +265,38 @@ func (root *mTreap) insert(span *mspan) { ...@@ -241,25 +265,38 @@ func (root *mTreap) insert(span *mspan) {
// Add t as new leaf in tree of span size and unique addrs. // Add t as new leaf in tree of span size and unique addrs.
// The balanced tree is a treap using priority as the random heap priority. // The balanced tree is a treap using priority as the random heap priority.
// That is, it is a binary tree ordered according to the npagesKey, // That is, it is a binary tree ordered according to the key,
// but then among the space of possible binary trees respecting those // but then among the space of possible binary trees respecting those
// npagesKeys, it is kept balanced on average by maintaining a heap ordering // keys, it is kept balanced on average by maintaining a heap ordering
// on the priority: s.priority <= both s.right.priority and s.right.priority. // on the priority: s.priority <= both s.right.priority and s.right.priority.
// https://en.wikipedia.org/wiki/Treap // https://en.wikipedia.org/wiki/Treap
// https://faculty.washington.edu/aragon/pubs/rst89.pdf // https://faculty.washington.edu/aragon/pubs/rst89.pdf
t := (*treapNode)(mheap_.treapalloc.alloc()) t := (*treapNode)(mheap_.treapalloc.alloc())
t.npagesKey = span.npages t.key = span.base()
t.priority = fastrand() t.priority = fastrand()
t.spanKey = span t.span = span
t.maxPages = span.npages
t.parent = last t.parent = last
*pt = t // t now at a leaf. *pt = t // t now at a leaf.
// Update the tree to maintain the maxPages invariant.
i := t
for i.parent != nil {
if i.parent.maxPages < i.maxPages {
i.parent.maxPages = i.maxPages
} else {
break
}
i = i.parent
}
// Rotate up into tree according to priority. // Rotate up into tree according to priority.
for t.parent != nil && t.parent.priority > t.priority { for t.parent != nil && t.parent.priority > t.priority {
if t != nil && t.spanKey.npages != t.npagesKey { if t != nil && t.span.base() != t.key {
println("runtime: insert t=", t, "t.npagesKey=", t.npagesKey) println("runtime: insert t=", t, "t.key=", t.key)
println("runtime: t.spanKey=", t.spanKey, "t.spanKey.npages=", t.spanKey.npages) println("runtime: t.span=", t.span, "t.span.base()=", t.span.base())
throw("span and treap sizes do not match?") throw("span and treap node base addresses do not match")
} }
if t.parent.left == t { if t.parent.left == t {
root.rotateRight(t.parent) root.rotateRight(t.parent)
...@@ -273,8 +310,8 @@ func (root *mTreap) insert(span *mspan) { ...@@ -273,8 +310,8 @@ func (root *mTreap) insert(span *mspan) {
} }
func (root *mTreap) removeNode(t *treapNode) { func (root *mTreap) removeNode(t *treapNode) {
if t.spanKey.npages != t.npagesKey { if t.span.base() != t.key {
throw("span and treap node npages do not match") throw("span and treap node base addresses do not match")
} }
// Rotate t down to be leaf of tree for removal, respecting priorities. // Rotate t down to be leaf of tree for removal, respecting priorities.
for t.right != nil || t.left != nil { for t.right != nil || t.left != nil {
...@@ -286,10 +323,23 @@ func (root *mTreap) removeNode(t *treapNode) { ...@@ -286,10 +323,23 @@ func (root *mTreap) removeNode(t *treapNode) {
} }
// Remove t, now a leaf. // Remove t, now a leaf.
if t.parent != nil { if t.parent != nil {
if t.parent.left == t { p := t.parent
t.parent.left = nil if p.left == t {
p.left = nil
} else { } else {
t.parent.right = nil p.right = nil
}
// Walk up the tree updating maxPages values until
// it no longer changes, since the just-removed node
// could have contained the biggest span in any subtree
// up to the root.
for p != nil {
m := p.maxPages
p.recomputeMaxPages()
if p.maxPages == m {
break
}
p = p.parent
} }
} else { } else {
root.treap = nil root.treap = nil
...@@ -298,50 +348,63 @@ func (root *mTreap) removeNode(t *treapNode) { ...@@ -298,50 +348,63 @@ func (root *mTreap) removeNode(t *treapNode) {
mheap_.treapalloc.free(unsafe.Pointer(t)) mheap_.treapalloc.free(unsafe.Pointer(t))
} }
// find searches for, finds, and returns the treap iterator representing // find searches for, finds, and returns the treap iterator representing the
// the position of the smallest span that can hold npages. If no span has // position of the span with the smallest base address which is at least npages
// at least npages it returns an invalid iterator. // in size. If no span has at least npages it returns an invalid iterator.
// This is a simple binary tree search that tracks the best-fit node found //
// so far. The best-fit node is guaranteed to be on the path to a // This algorithm is as follows:
// (maybe non-existent) lowest-base exact match. // * If there's a left child and its subtree can satisfy this allocation,
// continue down that subtree.
// * If there's no such left child, check if the root of this subtree can
// satisfy the allocation. If so, we're done.
// * If the root cannot satisfy the allocation either, continue down the
// right subtree if able.
// * Else, break and report that we cannot satisfy the allocation.
//
// The preference for left, then current, then right, results in us getting
// the left-most node which will contain the span with the lowest base
// address.
//
// Note that if a request cannot be satisfied the fourth case will be
// reached immediately at the root, since neither the left subtree nor
// the right subtree will have a sufficient maxPages, whilst the root
// node is also unable to satisfy it.
func (root *mTreap) find(npages uintptr) treapIter { func (root *mTreap) find(npages uintptr) treapIter {
var best *treapNode
t := root.treap t := root.treap
for t != nil { for t != nil {
if t.spanKey == nil { if t.span == nil {
throw("treap node with nil spanKey found") throw("treap node with nil span found")
} }
// If we found an exact match, try to go left anyway. There could be // Iterate over the treap trying to go as far left
// a span there with a lower base address. // as possible while simultaneously ensuring that the
// // subtrees we choose always have a span which can
// Don't bother checking nil-ness of left and right here; even if t // satisfy the allocation.
// becomes nil, we already know the other path had nothing better for if t.left != nil && t.left.maxPages >= npages {
// us anyway.
if t.npagesKey >= npages {
best = t
t = t.left t = t.left
} else { } else if t.span.npages >= npages {
// Before going right, if this span can satisfy the
// request, stop here.
break
} else if t.right != nil && t.right.maxPages >= npages {
t = t.right t = t.right
} else {
t = nil
} }
} }
return treapIter{best} return treapIter{t}
} }
// removeSpan searches for, finds, deletes span along with // removeSpan searches for, finds, deletes span along with
// the associated treap node. If the span is not in the treap // the associated treap node. If the span is not in the treap
// then t will eventually be set to nil and the t.spanKey // then t will eventually be set to nil and the t.span
// will throw. // will throw.
func (root *mTreap) removeSpan(span *mspan) { func (root *mTreap) removeSpan(span *mspan) {
npages := span.npages base := span.base()
t := root.treap t := root.treap
for t.spanKey != span { for t.span != span {
if t.npagesKey < npages { if t.key < base {
t = t.right t = t.right
} else if t.npagesKey > npages { } else if t.key > base {
t = t.left
} else if t.spanKey.base() < span.base() {
t = t.right
} else if t.spanKey.base() > span.base() {
t = t.left t = t.left
} }
} }
...@@ -390,6 +453,11 @@ func (root *mTreap) rotateLeft(x *treapNode) { ...@@ -390,6 +453,11 @@ func (root *mTreap) rotateLeft(x *treapNode) {
} }
p.right = y p.right = y
} }
// Recomputing maxPages for x and y is sufficient
// for maintaining the maxPages invariant.
x.recomputeMaxPages()
y.recomputeMaxPages()
} }
// rotateRight rotates the tree rooted at node y. // rotateRight rotates the tree rooted at node y.
...@@ -426,4 +494,9 @@ func (root *mTreap) rotateRight(y *treapNode) { ...@@ -426,4 +494,9 @@ func (root *mTreap) rotateRight(y *treapNode) {
} }
p.right = x p.right = x
} }
// Recomputing maxPages for x and y is sufficient
// for maintaining the maxPages invariant.
y.recomputeMaxPages()
x.recomputeMaxPages()
} }
...@@ -1121,10 +1121,10 @@ func (h *mheap) pickFreeSpan(npage uintptr) *mspan { ...@@ -1121,10 +1121,10 @@ func (h *mheap) pickFreeSpan(npage uintptr) *mspan {
// Note that we want the _smaller_ free span, i.e. the free span // Note that we want the _smaller_ free span, i.e. the free span
// closer in size to the amount we requested (npage). // closer in size to the amount we requested (npage).
var s *mspan var s *mspan
if tf.valid() && (!ts.valid() || tf.span().npages <= ts.span().npages) { if tf.valid() && (!ts.valid() || tf.span().base() <= ts.span().base()) {
s = tf.span() s = tf.span()
h.free.erase(tf) h.free.erase(tf)
} else if ts.valid() && (!tf.valid() || tf.span().npages > ts.span().npages) { } else if ts.valid() && (!tf.valid() || tf.span().base() > ts.span().base()) {
s = ts.span() s = ts.span()
h.scav.erase(ts) h.scav.erase(ts)
} }
...@@ -1198,10 +1198,10 @@ HaveSpan: ...@@ -1198,10 +1198,10 @@ HaveSpan:
// grew the RSS. Mitigate this by scavenging enough free // grew the RSS. Mitigate this by scavenging enough free
// space to make up for it. // space to make up for it.
// //
// Also, scavengeLargest may cause coalescing, so prevent // Also, scavenge may cause coalescing, so prevent
// coalescing with s by temporarily changing its state. // coalescing with s by temporarily changing its state.
s.state = mSpanManual s.state = mSpanManual
h.scavengeLargest(s.npages * pageSize) h.scavengeLocked(s.npages * pageSize)
s.state = mSpanFree s.state = mSpanFree
} }
s.unusedsince = 0 s.unusedsince = 0
...@@ -1236,7 +1236,7 @@ func (h *mheap) grow(npage uintptr) bool { ...@@ -1236,7 +1236,7 @@ func (h *mheap) grow(npage uintptr) bool {
// is proportional to the number of sysUnused() calls rather than // is proportional to the number of sysUnused() calls rather than
// the number of pages released, so we make fewer of those calls // the number of pages released, so we make fewer of those calls
// with larger spans. // with larger spans.
h.scavengeLargest(size) h.scavengeLocked(size)
// Create a fake "in use" span and free it, so that the // Create a fake "in use" span and free it, so that the
// right coalescing happens. // right coalescing happens.
...@@ -1344,10 +1344,10 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i ...@@ -1344,10 +1344,10 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
h.treapForSpan(s).insert(s) h.treapForSpan(s).insert(s)
} }
// scavengeLargest scavenges nbytes worth of spans in unscav // scavengeLocked scavenges nbytes worth of spans in the free treap by
// starting from the largest span and working down. It then takes those spans // starting from the span with the highest base address and working down.
// and places them in scav. h must be locked. // It then takes those spans and places them in scav. h must be locked.
func (h *mheap) scavengeLargest(nbytes uintptr) { func (h *mheap) scavengeLocked(nbytes uintptr) {
// Use up scavenge credit if there's any available. // Use up scavenge credit if there's any available.
if nbytes > h.scavengeCredit { if nbytes > h.scavengeCredit {
nbytes -= h.scavengeCredit nbytes -= h.scavengeCredit
...@@ -1356,23 +1356,16 @@ func (h *mheap) scavengeLargest(nbytes uintptr) { ...@@ -1356,23 +1356,16 @@ func (h *mheap) scavengeLargest(nbytes uintptr) {
h.scavengeCredit -= nbytes h.scavengeCredit -= nbytes
return return
} }
// Iterate over the treap backwards (from largest to smallest) scavenging spans // Iterate over the treap backwards (from highest address to lowest address)
// until we've reached our quota of nbytes. // scavenging spans until we've reached our quota of nbytes.
released := uintptr(0) released := uintptr(0)
for t := h.free.end(); released < nbytes && t.valid(); { for t := h.free.end(); released < nbytes && t.valid(); {
s := t.span() 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 // This span doesn't cover at least one physical page, so skip it.
// means all other spans are no bigger than s. There's a high t = t.prev()
// chance that the other spans don't even cover a full page, continue
// (though they could) but iterating further just for a handful
// of pages probably isn't worth it, so just stop here.
//
// This check also preserves the invariant that spans that have
// `scavenged` set are only ever in the `scav` treap, and
// those which have it unset are only in the `free` treap.
break
} }
n := t.prev() n := t.prev()
h.free.erase(t) h.free.erase(t)
...@@ -1393,7 +1386,7 @@ func (h *mheap) scavengeLargest(nbytes uintptr) { ...@@ -1393,7 +1386,7 @@ func (h *mheap) scavengeLargest(nbytes uintptr) {
// scavengeAll visits each node in the unscav treap and scavenges the // scavengeAll visits each node in the unscav treap and scavenges the
// 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) scavengeAllLocked(now, limit uint64) uintptr {
// Iterate over the treap scavenging spans if unused for at least limit time. // Iterate over the treap scavenging spans if unused for at least limit time.
released := uintptr(0) released := uintptr(0)
for t := h.free.start(); t.valid(); { for t := h.free.start(); t.valid(); {
...@@ -1416,14 +1409,14 @@ func (h *mheap) scavengeAll(now, limit uint64) uintptr { ...@@ -1416,14 +1409,14 @@ func (h *mheap) scavengeAll(now, limit uint64) uintptr {
return released return released
} }
func (h *mheap) scavenge(k int32, now, limit uint64) { func (h *mheap) scavengeAll(k int32, now, limit uint64) {
// Disallow malloc or panic while holding the heap lock. We do // Disallow malloc or panic while holding the heap lock. We do
// this here because this is an non-mallocgc entry-point to // this here because this is an non-mallocgc entry-point to
// the mheap API. // the mheap API.
gp := getg() gp := getg()
gp.m.mallocing++ gp.m.mallocing++
lock(&h.lock) lock(&h.lock)
released := h.scavengeAll(now, limit) released := h.scavengeAllLocked(now, limit)
unlock(&h.lock) unlock(&h.lock)
gp.m.mallocing-- gp.m.mallocing--
...@@ -1438,7 +1431,7 @@ func (h *mheap) scavenge(k int32, now, limit uint64) { ...@@ -1438,7 +1431,7 @@ func (h *mheap) scavenge(k int32, now, limit uint64) {
//go:linkname runtime_debug_freeOSMemory runtime/debug.freeOSMemory //go:linkname runtime_debug_freeOSMemory runtime/debug.freeOSMemory
func runtime_debug_freeOSMemory() { func runtime_debug_freeOSMemory() {
GC() GC()
systemstack(func() { mheap_.scavenge(-1, ^uint64(0), 0) }) systemstack(func() { mheap_.scavengeAll(-1, ^uint64(0), 0) })
} }
// Initialize a new span with the given start and npages. // Initialize a new span with the given start and npages.
......
...@@ -4369,7 +4369,7 @@ func sysmon() { ...@@ -4369,7 +4369,7 @@ func sysmon() {
} }
// scavenge heap once in a while // scavenge heap once in a while
if lastscavenge+scavengelimit/2 < now { if lastscavenge+scavengelimit/2 < now {
mheap_.scavenge(int32(nscavenge), uint64(now), uint64(scavengelimit)) mheap_.scavengeAll(int32(nscavenge), uint64(now), uint64(scavengelimit))
lastscavenge = now lastscavenge = now
nscavenge++ nscavenge++
} }
......
...@@ -58,20 +58,18 @@ func TestTreap(t *testing.T) { ...@@ -58,20 +58,18 @@ func TestTreap(t *testing.T) {
} }
tr.RemoveSpan(spans[0]) tr.RemoveSpan(spans[0])
}) })
t.Run("FindBestFit", func(t *testing.T) { t.Run("FindFirstFit", func(t *testing.T) {
// Run this 10 times, recreating the treap each time. // Run this 10 times, recreating the treap each time.
// Because of the non-deterministic structure of a treap, // Because of the non-deterministic structure of a treap,
// we'll be able to test different structures this way. // we'll be able to test different structures this way.
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
tr := treap{} tr := runtime.Treap{}
for _, s := range spans { for _, s := range spans {
tr.Insert(s) tr.Insert(s)
} }
i := tr.Find(5) i := tr.Find(5)
if i.Span().Pages() != 5 { if i.Span().Base() != 0xc0010000 {
t.Fatalf("expected span of size 5, got span of size %d", i.Span().Pages()) t.Fatalf("expected span at lowest address which could fit 5 pages, instead found span at %x", i.Span().Base())
} else if i.Span().Base() != 0xc0040000 {
t.Fatalf("expected span to have the lowest base address, instead got base %x", i.Span().Base())
} }
for _, s := range spans { for _, s := range spans {
tr.RemoveSpan(s) tr.RemoveSpan(s)
...@@ -88,13 +86,13 @@ func TestTreap(t *testing.T) { ...@@ -88,13 +86,13 @@ func TestTreap(t *testing.T) {
tr.Insert(s) tr.Insert(s)
} }
nspans := 0 nspans := 0
lastSize := uintptr(0) lastBase := uintptr(0)
for i := tr.Start(); i.Valid(); i = i.Next() { for i := tr.Start(); i.Valid(); i = i.Next() {
nspans++ nspans++
if lastSize > i.Span().Pages() { if lastBase > i.Span().Base() {
t.Fatalf("not iterating in correct order: encountered size %d before %d", lastSize, i.Span().Pages()) t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base())
} }
lastSize = i.Span().Pages() lastBase = i.Span().Base()
} }
if nspans != len(spans) { if nspans != len(spans) {
t.Fatal("failed to iterate forwards over full treap") t.Fatal("failed to iterate forwards over full treap")
...@@ -112,13 +110,13 @@ func TestTreap(t *testing.T) { ...@@ -112,13 +110,13 @@ func TestTreap(t *testing.T) {
tr.Insert(s) tr.Insert(s)
} }
nspans := 0 nspans := 0
lastSize := ^uintptr(0) lastBase := ^uintptr(0)
for i := tr.End(); i.Valid(); i = i.Prev() { for i := tr.End(); i.Valid(); i = i.Prev() {
nspans++ nspans++
if lastSize < i.Span().Pages() { if lastBase < i.Span().Base() {
t.Fatalf("not iterating in correct order: encountered size %d before %d", lastSize, i.Span().Pages()) t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base())
} }
lastSize = i.Span().Pages() lastBase = i.Span().Base()
} }
if nspans != len(spans) { if nspans != len(spans) {
t.Fatal("failed to iterate backwards over full treap") t.Fatal("failed to iterate backwards over full treap")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment