1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:54:45 -07:00

runtime: consolidate gcDrain and gcDrainUntilPreempt

These functions were nearly identical. Consolidate them by adding a
flags argument. In addition to cleaning up this code, this makes
further changes that affect both functions easier.

Change-Id: I6ec5c947603bbbd3ff4040113b2fbc240e99745f
Reviewed-on: https://go-review.googlesource.com/15405
Reviewed-by: Rick Hudson <rlh@golang.org>
This commit is contained in:
Austin Clements 2015-10-04 22:42:43 -04:00
parent 39ed682206
commit 9b3cdaf0a3
2 changed files with 39 additions and 69 deletions

View File

@ -1334,7 +1334,7 @@ func gcBgMarkWorker(p *p) {
default:
throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
case gcMarkWorkerDedicatedMode:
gcDrain(&p.gcw, gcBgCreditSlack)
gcDrain(&p.gcw, gcBgCreditSlack, gcDrainBlock)
// gcDrain did the xadd(&work.nwait +1) to
// match the decrement above. It only returns
// at a mark completion point.
@ -1343,7 +1343,7 @@ func gcBgMarkWorker(p *p) {
throw("gcDrain returned with buffer")
}
case gcMarkWorkerFractionalMode, gcMarkWorkerIdleMode:
gcDrainUntilPreempt(&p.gcw, gcBgCreditSlack)
gcDrain(&p.gcw, gcBgCreditSlack, gcDrainUntilPreempt)
// If we are nearing the end of mark, dispose
// of the cache promptly. We must do this
@ -1454,7 +1454,7 @@ func gcMark(start_time int64) {
parfordo(work.markfor)
var gcw gcWork
gcDrain(&gcw, -1)
gcDrain(&gcw, -1, gcDrainBlock)
gcw.dispose()
if work.full != 0 {
@ -1717,7 +1717,7 @@ func gchelper() {
parfordo(work.markfor)
if gcphase != _GCscan {
var gcw gcWork
gcDrain(&gcw, -1) // blocks in getfull
gcDrain(&gcw, -1, gcDrainBlock) // blocks in getfull
gcw.dispose()
}

View File

@ -549,71 +549,34 @@ func scanframeworker(frame *stkframe, unused unsafe.Pointer, gcw *gcWork) {
}
}
// TODO(austin): Can we consolidate the gcDrain* functions?
type gcDrainFlags int
// gcDrain scans objects in work buffers, blackening grey
// objects until all work buffers have been drained.
const (
gcDrainUntilPreempt gcDrainFlags = 1 << iota
// gcDrainBlock is the opposite of gcDrainUntilPreempt. This
// is the default, but callers should use the constant for
// documentation purposes.
gcDrainBlock gcDrainFlags = 0
)
// gcDrain scans objects in work buffers, blackening grey objects
// until all work buffers have been drained.
//
// If flags&gcDrainUntilPreempt != 0, gcDrain also returns if
// g.preempt is set. Otherwise, this will block until all dedicated
// workers are blocked in gcDrain.
//
// If flushScanCredit != -1, gcDrain flushes accumulated scan work
// credit to gcController.bgScanCredit whenever gcw's local scan work
// credit exceeds flushScanCredit.
//go:nowritebarrier
func gcDrain(gcw *gcWork, flushScanCredit int64) {
func gcDrain(gcw *gcWork, flushScanCredit int64, flags gcDrainFlags) {
if !writeBarrierEnabled {
throw("gcDrain phase incorrect")
}
var lastScanFlush, nextScanFlush int64
if flushScanCredit != -1 {
lastScanFlush = gcw.scanWork
nextScanFlush = lastScanFlush + flushScanCredit
} else {
nextScanFlush = int64(^uint64(0) >> 1)
}
for {
// If another proc wants a pointer, give it some.
if work.nwait > 0 && work.full == 0 {
gcw.balance()
}
b := gcw.get()
if b == 0 {
// work barrier reached
break
}
// If the current wbuf is filled by the scan a new wbuf might be
// returned that could possibly hold only a single object. This
// could result in each iteration draining only a single object
// out of the wbuf passed in + a single object placed
// into an empty wbuf in scanobject so there could be
// a performance hit as we keep fetching fresh wbufs.
scanobject(b, gcw)
// Flush background scan work credit to the global
// account if we've accumulated enough locally so
// mutator assists can draw on it.
if gcw.scanWork >= nextScanFlush {
credit := gcw.scanWork - lastScanFlush
xaddint64(&gcController.bgScanCredit, credit)
lastScanFlush = gcw.scanWork
nextScanFlush = lastScanFlush + flushScanCredit
}
}
if flushScanCredit != -1 {
credit := gcw.scanWork - lastScanFlush
xaddint64(&gcController.bgScanCredit, credit)
}
}
// gcDrainUntilPreempt blackens grey objects until g.preempt is set.
// This is best-effort, so it will return as soon as it is unable to
// get work, even though there may be more work in the system.
//go:nowritebarrier
func gcDrainUntilPreempt(gcw *gcWork, flushScanCredit int64) {
if !writeBarrierEnabled {
println("gcphase =", gcphase)
throw("gcDrainUntilPreempt phase incorrect")
}
blocking := flags&gcDrainUntilPreempt == 0
var lastScanFlush, nextScanFlush int64
if flushScanCredit != -1 {
@ -624,21 +587,28 @@ func gcDrainUntilPreempt(gcw *gcWork, flushScanCredit int64) {
}
gp := getg()
for !gp.preempt {
// If the work queue is empty, balance. During
// concurrent mark we don't really know if anyone else
// can make use of this work, but even if we're the
// only worker, the total cost of this per cycle is
// only O(_WorkbufSize) pointer copies.
if work.full == 0 && work.partial == 0 {
for blocking || !gp.preempt {
// If another proc wants a pointer, give it some.
if work.nwait > 0 && work.full == 0 {
gcw.balance()
}
b := gcw.tryGet()
var b uintptr
if blocking {
b = gcw.get()
} else {
b = gcw.tryGet()
}
if b == 0 {
// No more work
// work barrier reached or tryGet failed.
break
}
// If the current wbuf is filled by the scan a new wbuf might be
// returned that could possibly hold only a single object. This
// could result in each iteration draining only a single object
// out of the wbuf passed in + a single object placed
// into an empty wbuf in scanobject so there could be
// a performance hit as we keep fetching fresh wbufs.
scanobject(b, gcw)
// Flush background scan work credit to the global