Commit 9439fa10 authored by Rick Hudson's avatar Rick Hudson

runtime: eagerly share GC work buffers

Currently, due to an oversight, we only balance work buffers
in background and idle workers and not in assists. As a
result, in assist-heavy workloads, assists are likely to tie
up large work buffers in per-P caches increasing the
likelihood that the global list will be empty. This increases
the likelihood that other GC workers will exit and assists
will block, slowing down the system as a whole. Fix this by
eagerly balancing work buffers as soon as the assists notice
that the global buffers are empty. This makes it much more
likely that work will be immediately available to other
workers and assists.

This change reduces the garbage benchmark time by 39% and
fixes the regresssion seen at CL 15893 golang.org/cl/15893.

Garbage benchmark times before and after this CL.
Before GOPERF-METRIC:time=4427020
After  GOPERF-METRIC:time=2721645

Fixes #13827

Change-Id: I9cb531fb873bab4b69ce9c1617e30df6c49cdcfe
Reviewed-on: https://go-review.googlesource.com/18341Reviewed-by: default avatarAustin Clements <austin@google.com>
parent 109d54a3
...@@ -796,7 +796,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { ...@@ -796,7 +796,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
} }
gp := getg() gp := getg()
preemtible := flags&gcDrainUntilPreempt != 0 preemptible := flags&gcDrainUntilPreempt != 0
blocking := flags&(gcDrainUntilPreempt|gcDrainNoBlock) == 0 blocking := flags&(gcDrainUntilPreempt|gcDrainNoBlock) == 0
flushBgCredit := flags&gcDrainFlushBgCredit != 0 flushBgCredit := flags&gcDrainFlushBgCredit != 0
...@@ -815,9 +815,13 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { ...@@ -815,9 +815,13 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
initScanWork := gcw.scanWork initScanWork := gcw.scanWork
// Drain heap marking jobs. // Drain heap marking jobs.
for !(preemtible && gp.preempt) { for !(preemptible && gp.preempt) {
// If another proc wants a pointer, give it some. // Try to keep work available on the global queue. We used to
if work.nwait > 0 && work.full == 0 { // check if there were waiting workers, but it's better to
// just keep work available than to make workers wait. In the
// worst case, we'll do O(log(_WorkbufSize)) unnecessary
// balances.
if work.full == 0 {
gcw.balance() gcw.balance()
} }
...@@ -884,10 +888,16 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 { ...@@ -884,10 +888,16 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 {
gp := getg().m.curg gp := getg().m.curg
for !gp.preempt && workFlushed+gcw.scanWork < scanWork { for !gp.preempt && workFlushed+gcw.scanWork < scanWork {
// See gcDrain comment.
if work.full == 0 {
gcw.balance()
}
// This might be a good place to add prefetch code... // This might be a good place to add prefetch code...
// if(wbuf.nobj > 4) { // if(wbuf.nobj > 4) {
// PREFETCH(wbuf->obj[wbuf.nobj - 3]; // PREFETCH(wbuf->obj[wbuf.nobj - 3];
// } // }
//
b := gcw.tryGet() b := gcw.tryGet()
if b == 0 { if b == 0 {
break break
......
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