runtime: implement addrRanges.findSucc with a binary search

This change modifies addrRanges.findSucc to more efficiently find the
successor range in an addrRanges by using a binary search to narrow down
large addrRanges and iterate over no more than 8 addrRanges.

This change makes the runtime more robust against systems that may
aggressively randomize the address space mappings it gives the runtime
(e.g. Fuchsia).

For #40191.

Change-Id: If529df2abd2edb1b1496d8690ddd284ecd7138c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/242679
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-07-14 21:45:16 +00:00 committed by Michael Knyszek
parent 0eb52ac250
commit 76bce1dd52
1 changed files with 33 additions and 7 deletions

View File

@ -172,20 +172,46 @@ func (a *addrRanges) init(sysStat *sysMemStat) {
a.totalBytes = 0 a.totalBytes = 0
} }
// findSucc returns the first index in a such that base is // findSucc returns the first index in a such that addr is
// less than the base of the addrRange at that index. // less than the base of the addrRange at that index.
func (a *addrRanges) findSucc(addr uintptr) int { func (a *addrRanges) findSucc(addr uintptr) int {
// TODO(mknyszek): Consider a binary search for large arrays.
// While iterating over these ranges is potentially expensive,
// the expected number of ranges is small, ideally just 1,
// since Go heaps are usually mostly contiguous.
base := offAddr{addr} base := offAddr{addr}
for i := range a.ranges {
// Narrow down the search space via a binary search
// for large addrRanges until we have at most iterMax
// candidates left.
const iterMax = 8
bot, top := 0, len(a.ranges)
for top-bot > iterMax {
i := ((top - bot) / 2) + bot
if a.ranges[i].contains(base.addr()) {
// a.ranges[i] contains base, so
// its successor is the next index.
return i + 1
}
if base.lessThan(a.ranges[i].base) {
// In this case i might actually be
// the successor, but we can't be sure
// until we check the ones before it.
top = i
} else {
// In this case we know base is
// greater than or equal to a.ranges[i].limit-1,
// so i is definitely not the successor.
// We already checked i, so pick the next
// one.
bot = i + 1
}
}
// There are top-bot candidates left, so
// iterate over them and find the first that
// base is strictly less than.
for i := bot; i < top; i++ {
if base.lessThan(a.ranges[i].base) { if base.lessThan(a.ranges[i].base) {
return i return i
} }
} }
return len(a.ranges) return top
} }
// findAddrGreaterEqual returns the smallest address represented by a // findAddrGreaterEqual returns the smallest address represented by a