Skip to content

Commit 65aa2da

Browse files
committed
runtime: assist before allocating
Currently, when the mutator allocates, the runtime first allocates the memory and then, if that G has done "enough" allocation, the runtime checks whether the G has assist debt to pay off and, if so, pays it off. This approach leads to under-assisting, where a G can allocate a large region (or many small regions) before paying for it, or can even exit with outstanding debt. This commit flips this around so that a G always acquires enough credit for an allocation before it can perform that allocation. We continue to amortize the cost of assists by requiring that they over-assist when triggered to build up credit for many allocations. Fixes #11967. Change-Id: Idac9f11133b328535667674d837be72c23ebd899 Reviewed-on: https://go-review.googlesource.com/15409 Reviewed-by: Rick Hudson <[email protected]> Run-TryBot: Austin Clements <[email protected]>
1 parent 89c341c commit 65aa2da

File tree

3 files changed

+46
-29
lines changed

3 files changed

+46
-29
lines changed

src/runtime/malloc.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,27 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
510510
return persistentalloc(size, align, &memstats.other_sys)
511511
}
512512

513+
// assistG is the G to charge for this allocation, or nil if
514+
// GC is not currently active.
515+
var assistG *g
516+
if gcBlackenEnabled != 0 {
517+
// Charge the current user G for this allocation.
518+
assistG = getg()
519+
if assistG.m.curg != nil {
520+
assistG = assistG.m.curg
521+
}
522+
// Charge the allocation against the G. We'll account
523+
// for internal fragmentation at the end of mallocgc.
524+
assistG.gcAssistBytes -= int64(size)
525+
526+
if assistG.gcAssistBytes < 0 {
527+
// This G is in debt. Assist the GC to correct
528+
// this before allocating. This must happen
529+
// before disabling preemption.
530+
gcAssistAlloc(assistG)
531+
}
532+
}
533+
513534
// Set mp.mallocing to keep from being preempted by GC.
514535
mp := acquirem()
515536
if mp.mallocing != 0 {
@@ -704,15 +725,15 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
704725
}
705726
}
706727

728+
if assistG != nil {
729+
// Account for internal fragmentation in the assist
730+
// debt now that we know it.
731+
assistG.gcAssistBytes -= int64(size - dataSize)
732+
}
733+
707734
if shouldhelpgc && shouldtriggergc() {
708735
startGC(gcBackgroundMode, false)
709-
} else if gcBlackenEnabled != 0 {
710-
// Assist garbage collector. We delay this until the
711-
// epilogue so that it doesn't interfere with the
712-
// inner working of malloc such as mcache refills that
713-
// might happen while doing the gcAssistAlloc.
714-
gcAssistAlloc(size, shouldhelpgc)
715-
} else if shouldhelpgc && bggc.working != 0 {
736+
} else if shouldhelpgc && bggc.working != 0 && gcBlackenEnabled == 0 {
716737
// The GC is starting up or shutting down, so we can't
717738
// assist, but we also can't allocate unabated. Slow
718739
// down this G's allocation and help the GC stay

src/runtime/mgc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,12 @@ const gcCreditSlack = 2000
723723
// can accumulate on a P before updating gcController.assistTime.
724724
const gcAssistTimeSlack = 5000
725725

726+
// gcOverAssistBytes determines how many extra allocation bytes of
727+
// assist credit a GC assist builds up when an assist happens. This
728+
// amortizes the cost of an assist by pre-paying for this many bytes
729+
// of future allocations.
730+
const gcOverAssistBytes = 1 << 20
731+
726732
// Determine whether to initiate a GC.
727733
// If the GC is already working no need to trigger another one.
728734
// This should establish a feedback loop where if the GC does not

src/runtime/mgcmark.go

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -198,28 +198,12 @@ func markrootSpans(gcw *gcWork, shard int) {
198198
}
199199
}
200200

201-
// gcAssistAlloc records and allocation of size bytes and, if
202-
// allowAssist is true, may assist GC scanning in proportion to the
203-
// allocations performed by this mutator since the last assist.
201+
// gcAssistAlloc performs GC work to make gp's assist debt positive.
202+
// gp must be the calling user gorountine.
204203
//
205-
// It should only be called if gcBlackenEnabled != 0.
206-
//
207-
// This must be called with preemption disabled.
204+
// This must be called with preemption enabled.
208205
//go:nowritebarrier
209-
func gcAssistAlloc(size uintptr, allowAssist bool) {
210-
// Find the G responsible for this assist.
211-
gp := getg()
212-
if gp.m.curg != nil {
213-
gp = gp.m.curg
214-
}
215-
216-
// Record allocation.
217-
gp.gcAssistBytes -= int64(size)
218-
219-
if !allowAssist || gp.gcAssistBytes >= 0 {
220-
return
221-
}
222-
206+
func gcAssistAlloc(gp *g) {
223207
// Don't assist in non-preemptible contexts. These are
224208
// generally fragile and won't allow the assist to block.
225209
if getg() == gp.m.g0 {
@@ -230,8 +214,9 @@ func gcAssistAlloc(size uintptr, allowAssist bool) {
230214
}
231215

232216
// Compute the amount of scan work we need to do to make the
233-
// balance positive.
234-
debtBytes := -gp.gcAssistBytes
217+
// balance positive. We over-assist to build up credit for
218+
// future allocations and amortize the cost of assisting.
219+
debtBytes := -gp.gcAssistBytes + gcOverAssistBytes
235220
scanWork := int64(gcController.assistWorkPerByte * float64(debtBytes))
236221

237222
retry:
@@ -358,7 +343,12 @@ retry:
358343
// more, so go around again after performing an
359344
// interruptible sleep for 100 us (the same as the
360345
// getfull barrier) to let other mutators run.
346+
347+
// timeSleep may allocate, so avoid recursive assist.
348+
gcAssistBytes := gp.gcAssistBytes
349+
gp.gcAssistBytes = int64(^uint64(0) >> 1)
361350
timeSleep(100 * 1000)
351+
gp.gcAssistBytes = gcAssistBytes
362352
goto retry
363353
}
364354
}

0 commit comments

Comments
 (0)