runtime: make getMCache inlineable

This change moves the responsibility of throwing if an mcache is not
available to the caller, because the inlining cost of throw is set very
high in the compiler. Even if it was reduced down to the cost of a usual
function call, it would still be too expensive, so just move it out.

This choice also makes sense in the context of #42339 since we're going
to have to handle the case where we don't have an mcache to update stats
in a few contexts anyhow.

Also, add getMCache to the list of functions that should be inlined to
prevent future regressions.

getMCache is called on the allocation fast path and because its not
inlined actually causes a significant regression (~10%) in some
microbenchmarks.

Fixes #42305.

Change-Id: I64ac5e4f26b730bd4435ea1069a4a50f55411ced
Reviewed-on: https://go-review.googlesource.com/c/go/+/267157
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Michael Anthony Knyszek 2020-11-02 16:58:38 +00:00 committed by Michael Knyszek
parent 4fcb5068f6
commit ac766e3718
5 changed files with 26 additions and 6 deletions

View file

@ -51,6 +51,7 @@ func TestIntendedInlining(t *testing.T) {
"funcPC", "funcPC",
"getArgInfoFast", "getArgInfoFast",
"getm", "getm",
"getMCache",
"isDirectIface", "isDirectIface",
"itabHashFunc", "itabHashFunc",
"noescape", "noescape",

View file

@ -975,6 +975,9 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
shouldhelpgc := false shouldhelpgc := false
dataSize := size dataSize := size
c := getMCache() c := getMCache()
if c == nil {
throw("mallocgc called without a P or outside bootstrapping")
}
var span *mspan var span *mspan
var x unsafe.Pointer var x unsafe.Pointer
noscan := typ == nil || typ.ptrdata == 0 noscan := typ == nil || typ.ptrdata == 0
@ -1202,7 +1205,11 @@ func reflect_unsafe_NewArray(typ *_type, n int) unsafe.Pointer {
} }
func profilealloc(mp *m, x unsafe.Pointer, size uintptr) { func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
getMCache().nextSample = nextSample() c := getMCache()
if c == nil {
throw("profilealloc called without a P or outside bootstrapping")
}
c.nextSample = nextSample()
mProf_Malloc(x, size) mProf_Malloc(x, size)
} }

View file

@ -124,8 +124,8 @@ func freemcache(c *mcache) {
// getMCache is a convenience function which tries to obtain an mcache. // getMCache is a convenience function which tries to obtain an mcache.
// //
// Must be running with a P when called (so the caller must be in a // Returns nil if we're not bootstrapping or we don't have a P. The caller's
// non-preemptible state) or must be called during bootstrapping. // P must not change, so we must be in a non-preemptible state.
func getMCache() *mcache { func getMCache() *mcache {
// Grab the mcache, since that's where stats live. // Grab the mcache, since that's where stats live.
pp := getg().m.p.ptr() pp := getg().m.p.ptr()
@ -136,9 +136,6 @@ func getMCache() *mcache {
// mcache0 is cleared when bootstrapping is complete, // mcache0 is cleared when bootstrapping is complete,
// by procresize. // by procresize.
c = mcache0 c = mcache0
if c == nil {
throw("getMCache called with no P or outside bootstrapping")
}
} else { } else {
c = pp.mcache c = pp.mcache
} }

View file

@ -734,6 +734,9 @@ func (p *pageAlloc) scavengeRangeLocked(ci chunkIdx, base, npages uint) uintptr
// Update consistent accounting too. // Update consistent accounting too.
c := getMCache() c := getMCache()
if c == nil {
throw("scavengeRangeLocked called without a P or outside bootstrapping")
}
stats := memstats.heapStats.acquire(c) stats := memstats.heapStats.acquire(c)
atomic.Xaddint64(&stats.committed, -nbytes) atomic.Xaddint64(&stats.committed, -nbytes)
atomic.Xaddint64(&stats.released, nbytes) atomic.Xaddint64(&stats.released, nbytes)

View file

@ -1247,6 +1247,10 @@ HaveSpan:
} }
// Update consistent stats. // Update consistent stats.
c := getMCache() c := getMCache()
if c == nil {
// TODO(mknyszek): Remove this and handle this case to fix #42339.
throw("allocSpan called without P or outside bootstrapping")
}
stats := memstats.heapStats.acquire(c) stats := memstats.heapStats.acquire(c)
atomic.Xaddint64(&stats.committed, int64(scav)) atomic.Xaddint64(&stats.committed, int64(scav))
atomic.Xaddint64(&stats.released, -int64(scav)) atomic.Xaddint64(&stats.released, -int64(scav))
@ -1341,6 +1345,10 @@ func (h *mheap) grow(npage uintptr) bool {
// just add directly to heap_released. // just add directly to heap_released.
atomic.Xadd64(&memstats.heap_released, int64(asize)) atomic.Xadd64(&memstats.heap_released, int64(asize))
c := getMCache() c := getMCache()
if c == nil {
// TODO(mknyszek): Remove this and handle this case to fix #42339.
throw("grow called without P or outside bootstrapping")
}
stats := memstats.heapStats.acquire(c) stats := memstats.heapStats.acquire(c)
atomic.Xaddint64(&stats.released, int64(asize)) atomic.Xaddint64(&stats.released, int64(asize))
memstats.heapStats.release(c) memstats.heapStats.release(c)
@ -1440,6 +1448,10 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) {
} }
// Update consistent stats. // Update consistent stats.
c := getMCache() c := getMCache()
if c == nil {
// TODO(mknyszek): Remove this and handle this case to fix #42339.
throw("freeSpanLocked called without P or outside bootstrapping")
}
stats := memstats.heapStats.acquire(c) stats := memstats.heapStats.acquire(c)
switch typ { switch typ {
case spanAllocHeap: case spanAllocHeap: