Skip to content

Commit 8c05d67

Browse files
committed
runtime: make mTreap.find actually find the best fit
This change modifies the implementation of mTreap.find to find the best-fit span with the lowest possible base address. Fixes #31616. Change-Id: Ib4bda0f85d7d0590326f939a243a6e4665f37d3f Reviewed-on: https://go-review.googlesource.com/c/go/+/173479 Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 93fceba commit 8c05d67

File tree

2 files changed

+19
-18
lines changed

2 files changed

+19
-18
lines changed

src/runtime/mgclarge.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -301,27 +301,30 @@ func (root *mTreap) removeNode(t *treapNode) {
301301
// find searches for, finds, and returns the treap iterator representing
302302
// the position of the smallest span that can hold npages. If no span has
303303
// at least npages it returns an invalid iterator.
304-
// This is slightly more complicated than a simple binary tree search
305-
// since if an exact match is not found the next larger node is
306-
// returned.
307-
// TODO(mknyszek): It turns out this routine does not actually find the
308-
// best-fit span, so either fix that or move to something else first, and
309-
// evaluate the performance implications of doing so.
304+
// This is a simple binary tree search that tracks the best-fit node found
305+
// so far. The best-fit node is guaranteed to be on the path to a
306+
// (maybe non-existent) lowest-base exact match.
310307
func (root *mTreap) find(npages uintptr) treapIter {
308+
var best *treapNode
311309
t := root.treap
312310
for t != nil {
313311
if t.spanKey == nil {
314312
throw("treap node with nil spanKey found")
315313
}
316-
if t.npagesKey < npages {
317-
t = t.right
318-
} else if t.left != nil && t.left.npagesKey >= npages {
314+
// If we found an exact match, try to go left anyway. There could be
315+
// a span there with a lower base address.
316+
//
317+
// Don't bother checking nil-ness of left and right here; even if t
318+
// becomes nil, we already know the other path had nothing better for
319+
// us anyway.
320+
if t.npagesKey >= npages {
321+
best = t
319322
t = t.left
320323
} else {
321-
return treapIter{t}
324+
t = t.right
322325
}
323326
}
324-
return treapIter{}
327+
return treapIter{best}
325328
}
326329

327330
// removeSpan searches for, finds, deletes span along with

src/runtime/treap_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,7 @@ func TestTreap(t *testing.T) {
5858
}
5959
tr.RemoveSpan(spans[0])
6060
})
61-
t.Run("Find", func(t *testing.T) {
62-
// Note that Find doesn't actually find the best-fit
63-
// element, so just make sure it always returns an element
64-
// that is at least large enough to satisfy the request.
65-
//
61+
t.Run("FindBestFit", func(t *testing.T) {
6662
// Run this 10 times, recreating the treap each time.
6763
// Because of the non-deterministic structure of a treap,
6864
// we'll be able to test different structures this way.
@@ -72,8 +68,10 @@ func TestTreap(t *testing.T) {
7268
tr.Insert(s)
7369
}
7470
i := tr.Find(5)
75-
if i.Span().Pages() < 5 {
76-
t.Fatalf("expected span of size at least 5, got size %d", i.Span().Pages())
71+
if i.Span().Pages() != 5 {
72+
t.Fatalf("expected span of size 5, got span of size %d", i.Span().Pages())
73+
} else if i.Span().Base() != 0xc0040000 {
74+
t.Fatalf("expected span to have the lowest base address, instead got base %x", i.Span().Base())
7775
}
7876
for _, s := range spans {
7977
tr.RemoveSpan(s)

0 commit comments

Comments
 (0)