Commit 269c969c authored by Austin Clements's avatar Austin Clements

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/20667Reviewed-by: default avatarRick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent efb0c554
...@@ -602,9 +602,6 @@ func gcFlushBgCredit(scanWork int64) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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) { ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment