mirror of
https://github.com/golang/go
synced 2024-11-13 17:10:21 -07:00
runtime: remove stack barriers during concurrent mark
Currently we remove stack barriers during STW mark termination, which has a non-trivial per-goroutine cost and means that we have to touch even clean stacks during mark termination. However, there's no problem with leaving them in during the sweep phase. They just have to be out by the time we install new stack barriers immediately prior to scanning the stack such as during the mark phase of the next GC cycle or during mark termination in a STW GC. Hence, move the gcRemoveStackBarriers from STW mark termination to just before we install new stack barriers during concurrent mark. This removes the cost from STW. Furthermore, this combined with concurrent stack shrinking means that the mark termination scan of a clean stack is a complete no-op, which will make it possible to skip clean stacks entirely during mark termination. This has the downside that it will mess up anything outside of Go that tries to walk Go stacks all the time instead of just some of the time. This includes tools like GDB, perf, and VTune. We'll improve the situation shortly. Change-Id: Ia40baad8f8c16aeefac05425e00b0cf478137097 Reviewed-on: https://go-review.googlesource.com/20667 Reviewed-by: Rick Hudson <rlh@golang.org> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
efb0c55407
commit
269c969c81
@ -602,9 +602,6 @@ func gcFlushBgCredit(scanWork int64) {
|
|||||||
//go:nowritebarrier
|
//go:nowritebarrier
|
||||||
func scanstack(gp *g) {
|
func scanstack(gp *g) {
|
||||||
if gp.gcscanvalid {
|
if gp.gcscanvalid {
|
||||||
if gcphase == _GCmarktermination {
|
|
||||||
gcRemoveStackBarriers(gp)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,6 +644,7 @@ func scanstack(gp *g) {
|
|||||||
} else {
|
} else {
|
||||||
sp = gp.sched.sp
|
sp = gp.sched.sp
|
||||||
}
|
}
|
||||||
|
gcLockStackBarriers(gp) // Not necessary during mark term, but harmless.
|
||||||
switch gcphase {
|
switch gcphase {
|
||||||
case _GCmark:
|
case _GCmark:
|
||||||
// Install stack barriers during stack scan.
|
// Install stack barriers during stack scan.
|
||||||
@ -657,16 +655,18 @@ func scanstack(gp *g) {
|
|||||||
nextBarrier = ^uintptr(0)
|
nextBarrier = ^uintptr(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gp.stkbarPos != 0 || len(gp.stkbar) != 0 {
|
// Remove any existing stack barriers before we
|
||||||
// If this happens, it's probably because we
|
// install new ones.
|
||||||
// scanned a stack twice in the same phase.
|
gcRemoveStackBarriers(gp)
|
||||||
print("stkbarPos=", gp.stkbarPos, " len(stkbar)=", len(gp.stkbar), " goid=", gp.goid, " gcphase=", gcphase, "\n")
|
|
||||||
throw("g already has stack barriers")
|
|
||||||
}
|
|
||||||
|
|
||||||
gcLockStackBarriers(gp)
|
|
||||||
|
|
||||||
case _GCmarktermination:
|
case _GCmarktermination:
|
||||||
|
if !work.markrootDone {
|
||||||
|
// This is a STW GC. There may be stale stack
|
||||||
|
// barriers from an earlier cycle since we
|
||||||
|
// never passed through mark phase.
|
||||||
|
gcRemoveStackBarriers(gp)
|
||||||
|
}
|
||||||
|
|
||||||
if int(gp.stkbarPos) == len(gp.stkbar) {
|
if int(gp.stkbarPos) == len(gp.stkbar) {
|
||||||
// gp hit all of the stack barriers (or there
|
// gp hit all of the stack barriers (or there
|
||||||
// were none). Re-scan the whole stack.
|
// were none). Re-scan the whole stack.
|
||||||
@ -683,8 +683,6 @@ func scanstack(gp *g) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gcRemoveStackBarriers(gp)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw("scanstack in wrong phase")
|
throw("scanstack in wrong phase")
|
||||||
}
|
}
|
||||||
@ -722,9 +720,7 @@ func scanstack(gp *g) {
|
|||||||
if gcphase == _GCmarktermination {
|
if gcphase == _GCmarktermination {
|
||||||
gcw.dispose()
|
gcw.dispose()
|
||||||
}
|
}
|
||||||
if gcphase == _GCmark {
|
gcUnlockStackBarriers(gp)
|
||||||
gcUnlockStackBarriers(gp)
|
|
||||||
}
|
|
||||||
gp.gcscanvalid = true
|
gp.gcscanvalid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,14 +214,15 @@ func gcInstallStackBarrier(gp *g, frame *stkframe) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// gcRemoveStackBarriers removes all stack barriers installed in gp's stack.
|
// gcRemoveStackBarriers removes all stack barriers installed in gp's stack.
|
||||||
|
//
|
||||||
|
// gp's stack barriers must be locked.
|
||||||
|
//
|
||||||
//go:nowritebarrier
|
//go:nowritebarrier
|
||||||
func gcRemoveStackBarriers(gp *g) {
|
func gcRemoveStackBarriers(gp *g) {
|
||||||
if debugStackBarrier && gp.stkbarPos != 0 {
|
if debugStackBarrier && gp.stkbarPos != 0 {
|
||||||
print("hit ", gp.stkbarPos, " stack barriers, goid=", gp.goid, "\n")
|
print("hit ", gp.stkbarPos, " stack barriers, goid=", gp.goid, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
gcLockStackBarriers(gp)
|
|
||||||
|
|
||||||
// Remove stack barriers that we didn't hit.
|
// Remove stack barriers that we didn't hit.
|
||||||
for _, stkbar := range gp.stkbar[gp.stkbarPos:] {
|
for _, stkbar := range gp.stkbar[gp.stkbarPos:] {
|
||||||
gcRemoveStackBarrier(gp, stkbar)
|
gcRemoveStackBarrier(gp, stkbar)
|
||||||
@ -231,8 +232,6 @@ func gcRemoveStackBarriers(gp *g) {
|
|||||||
// adjust them.
|
// adjust them.
|
||||||
gp.stkbarPos = 0
|
gp.stkbarPos = 0
|
||||||
gp.stkbar = gp.stkbar[:0]
|
gp.stkbar = gp.stkbar[:0]
|
||||||
|
|
||||||
gcUnlockStackBarriers(gp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gcRemoveStackBarrier removes a single stack barrier. It is the
|
// gcRemoveStackBarrier removes a single stack barrier. It is the
|
||||||
|
Loading…
Reference in New Issue
Block a user