]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: implement addrRanges.findSucc with a binary search
authorMichael Anthony Knyszek <mknyszek@google.com>
Tue, 14 Jul 2020 21:45:16 +0000 (21:45 +0000)
committerMichael Knyszek <mknyszek@google.com>
Mon, 26 Oct 2020 22:00:15 +0000 (22:00 +0000)
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>
src/runtime/mranges.go

index 16acadcff175c9030ed48f2f4b6d0500a11d4545..84a2c06dbb2e66fcb1810e91640403e8281196e9 100644 (file)
@@ -172,20 +172,46 @@ func (a *addrRanges) init(sysStat *sysMemStat) {
        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.
 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}
-       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) {
                        return i
                }
        }
-       return len(a.ranges)
+       return top
 }
 
 // findAddrGreaterEqual returns the smallest address represented by a