From 100da60979087cac50c186b4928ee695ace2db62 Mon Sep 17 00:00:00 2001
From: Austin Clements <austin@google.com>
Date: Tue, 17 Mar 2015 12:17:47 -0400
Subject: [PATCH] runtime: track time spent in mutator assists

This time is tracked per P and periodically flushed to the global
controller state. This will be used to compute mutator assist
utilization in order to schedule background GC work.

Change-Id: Ib94f90903d426a02cf488bf0e2ef67a068eb3eec
Reviewed-on: https://go-review.googlesource.com/8837
Reviewed-by: Rick Hudson <rlh@golang.org>
---
 src/runtime/mgc.go      | 23 ++++++++++++++++++++++-
 src/runtime/mgcmark.go  | 13 +++++++++++++
 src/runtime/runtime2.go |  3 +++
 3 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index fba57db9ac..7efd8d6d06 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -203,6 +203,12 @@ type gcControllerState struct {
 	// it is both written and read throughout the cycle.
 	bgScanCredit int64
 
+	// assistTime is the nanoseconds spent in mutator assists
+	// during this cycle. This is updated atomically. Updates
+	// occur in bounded batches, since it is both written and read
+	// throughout the cycle.
+	assistTime int64
+
 	// workRatioAvg is a moving average of the scan work ratio
 	// (scan work per byte marked).
 	workRatioAvg float64
@@ -214,10 +220,11 @@ type gcControllerState struct {
 }
 
 // startCycle resets the GC controller's state and computes estimates
-// for a new GC cycle.
+// for a new GC cycle. The caller must hold worldsema.
 func (c *gcControllerState) startCycle() {
 	c.scanWork = 0
 	c.bgScanCredit = 0
+	c.assistTime = 0
 
 	// If this is the first GC cycle or we're operating on a very
 	// small heap, fake heap_marked so it looks like next_gc is
@@ -247,6 +254,16 @@ func (c *gcControllerState) startCycle() {
 		heapDistance = 1024 * 1024
 	}
 	c.assistRatio = float64(scanWorkExpected) / float64(heapDistance)
+
+	// Clear per-P state
+	for _, p := range &allp {
+		if p == nil {
+			break
+		}
+		p.gcAssistTime = 0
+	}
+
+	return
 }
 
 // endCycle updates the GC controller state at the end of the
@@ -269,6 +286,10 @@ func (c *gcControllerState) endCycle() {
 // memory contention.
 const gcBgCreditSlack = 2000
 
+// gcAssistTimeSlack is the nanoseconds of mutator assist time that
+// can accumulate on a P before updating gcController.assistTime.
+const gcAssistTimeSlack = 5000
+
 // Determine whether to initiate a GC.
 // If the GC is already working no need to trigger another one.
 // This should establish a feedback loop where if the GC does not
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index c28388f3ed..966cc28c8c 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -221,6 +221,11 @@ func gcAssistAlloc(size uintptr, allowAssist bool) {
 
 	// Perform assist work
 	systemstack(func() {
+		// Track time spent in this assist. Since we're on the
+		// system stack, this is non-preemptible, so we can
+		// just measure start and end time.
+		startTime := nanotime()
+
 		// drain own current wbuf first in the hopes that it
 		// will be more cache friendly.
 		var gcw gcWork
@@ -234,6 +239,14 @@ func gcAssistAlloc(size uintptr, allowAssist bool) {
 		// per-P gcWork cache (probably combined with the
 		// write barrier wbuf cache).
 		gcw.dispose()
+
+		duration := nanotime() - startTime
+		_p_ := gp.m.p.ptr()
+		_p_.gcAssistTime += duration
+		if _p_.gcAssistTime > gcAssistTimeSlack {
+			xaddint64(&gcController.assistTime, _p_.gcAssistTime)
+			_p_.gcAssistTime = 0
+		}
 	})
 }
 
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 18722bc6d3..fe3d0326c2 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -366,6 +366,9 @@ type p struct {
 
 	palloc persistentAlloc // per-P to avoid mutex
 
+	// Per-P GC state
+	gcAssistTime int64 // Nanoseconds in assistAlloc
+
 	pad [64]byte
 }
 
-- 
2.30.9