Skip to content

Commit 4565283

Browse files
committed
runtime: make assists preemptible
Currently, assists are non-preemptible, which means a heavily assisting G can block other Gs from running. At the beginning of a GC cycle, it can also delay scang, which will spin until the assist is done. Since scanning is currently done sequentially, this can seriously extend the length of the scan phase. Fix this by making assists preemptible. Since the assist holds work buffers and runs on the system stack, this must be done cooperatively: we make gcDrainN return on preemption, and make the assist return from the system stack and voluntarily Gosched. This is prerequisite to enlarging the work buffers. Without this change, the delays and spinning in scang increase significantly. This has no effect on the go1 benchmarks. name old time/op new time/op delta XBenchGarbage-12 5.72ms ± 4% 5.37ms ± 5% -6.11% (p=0.000 n=20+20) Change-Id: I829e732a0f23b126da633516a1a9ec1a508fdbf1 Reviewed-on: https://go-review.googlesource.com/15894 Reviewed-by: Rick Hudson <[email protected]> Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 15aa6bb commit 4565283

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

src/runtime/mgcmark.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,13 @@ retry:
440440
// do one of these before letting the mutator allocate
441441
// more to prevent over-allocation.
442442
//
443+
// If this is because we were preempted, reschedule
444+
// and try some more.
445+
if gp.preempt {
446+
Gosched()
447+
goto retry
448+
}
449+
443450
// Add this G to an assist queue and park. When the GC
444451
// has more background credit, it will satisfy queued
445452
// assists before flushing to the global credit pool.
@@ -845,11 +852,11 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
845852
}
846853

847854
// gcDrainN blackens grey objects until it has performed roughly
848-
// scanWork units of scan work. This is best-effort, so it may perform
849-
// less work if it fails to get a work buffer. Otherwise, it will
850-
// perform at least n units of work, but may perform more because
851-
// scanning is always done in whole object increments. It returns the
852-
// amount of scan work performed.
855+
// scanWork units of scan work or the G is preempted. This is
856+
// best-effort, so it may perform less work if it fails to get a work
857+
// buffer. Otherwise, it will perform at least n units of work, but
858+
// may perform more because scanning is always done in whole object
859+
// increments. It returns the amount of scan work performed.
853860
//go:nowritebarrier
854861
func gcDrainN(gcw *gcWork, scanWork int64) int64 {
855862
if !writeBarrierEnabled {
@@ -860,7 +867,8 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 {
860867
// want to claim was done by this call.
861868
workFlushed := -gcw.scanWork
862869

863-
for workFlushed+gcw.scanWork < scanWork {
870+
gp := getg().m.curg
871+
for !gp.preempt && workFlushed+gcw.scanWork < scanWork {
864872
// This might be a good place to add prefetch code...
865873
// if(wbuf.nobj > 4) {
866874
// PREFETCH(wbuf->obj[wbuf.nobj - 3];

0 commit comments

Comments
 (0)