Skip to content

Commit 3053788

Browse files
committed
cmd/trace: add minimum mutator utilization (MMU) plot
This adds an endpoint to the trace tool that plots the minimum mutator utilization curve using information on mark assists and GC pauses from the trace. This commit implements a fairly straightforward O(nm) algorithm for computing the MMU (and tests against an even more direct but slower algorithm). Future commits will extend and optimize this algorithm. This should be useful for debugging and understanding mutator utilization issues like #14951, #14812, #18155. #18534, #21107, particularly once follow-up CLs add trace cross-referencing. Change-Id: Ic2866869e7da1e6c56ba3e809abbcb2eb9c4923a Reviewed-on: https://go-review.googlesource.com/c/60790 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
1 parent f999576 commit 3053788

File tree

4 files changed

+567
-0
lines changed

4 files changed

+567
-0
lines changed

src/cmd/trace/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ var templMain = template.Must(template.New("").Parse(`
202202
<a href="/sched">Scheduler latency profile</a> (<a href="/sche?raw=1" download="sched.profile">⬇</a>)<br>
203203
<a href="/usertasks">User-defined tasks</a><br>
204204
<a href="/userregions">User-defined regions</a><br>
205+
<a href="/mmu">Minimum mutator utilization</a><br>
205206
</body>
206207
</html>
207208
`))

src/cmd/trace/mmu.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Minimum mutator utilization (MMU) graphing.
6+
7+
package main
8+
9+
import (
10+
"encoding/json"
11+
"fmt"
12+
trace "internal/traceparser"
13+
"log"
14+
"math"
15+
"net/http"
16+
"strings"
17+
"sync"
18+
"time"
19+
)
20+
21+
func init() {
22+
http.HandleFunc("/mmu", httpMMU)
23+
http.HandleFunc("/mmuPlot", httpMMUPlot)
24+
}
25+
26+
var mmuCache struct {
27+
init sync.Once
28+
util []trace.MutatorUtil
29+
mmuCurve *trace.MMUCurve
30+
err error
31+
}
32+
33+
func getMMUCurve() ([]trace.MutatorUtil, *trace.MMUCurve, error) {
34+
mmuCache.init.Do(func() {
35+
tr, err := parseTrace()
36+
if err != nil {
37+
mmuCache.err = err
38+
} else {
39+
mmuCache.util = tr.MutatorUtilization()
40+
mmuCache.mmuCurve = trace.NewMMUCurve(mmuCache.util)
41+
}
42+
})
43+
return mmuCache.util, mmuCache.mmuCurve, mmuCache.err
44+
}
45+
46+
// httpMMU serves the MMU plot page.
47+
func httpMMU(w http.ResponseWriter, r *http.Request) {
48+
http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU))
49+
}
50+
51+
// httpMMUPlot serves the JSON data for the MMU plot.
52+
func httpMMUPlot(w http.ResponseWriter, r *http.Request) {
53+
mu, mmuCurve, err := getMMUCurve()
54+
if err != nil {
55+
http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError)
56+
return
57+
}
58+
59+
// Find a nice starting point for the plot.
60+
xMin := time.Second
61+
for xMin > 1 {
62+
if mmu := mmuCurve.MMU(xMin); mmu < 0.0001 {
63+
break
64+
}
65+
xMin /= 1000
66+
}
67+
// Cover six orders of magnitude.
68+
xMax := xMin * 1e6
69+
// But no more than the length of the trace.
70+
if maxMax := time.Duration(mu[len(mu)-1].Time - mu[0].Time); xMax > maxMax {
71+
xMax = maxMax
72+
}
73+
// Compute MMU curve.
74+
logMin, logMax := math.Log(float64(xMin)), math.Log(float64(xMax))
75+
const samples = 100
76+
plot := make([][2]float64, samples)
77+
for i := 0; i < samples; i++ {
78+
window := time.Duration(math.Exp(float64(i)/(samples-1)*(logMax-logMin) + logMin))
79+
y := mmuCurve.MMU(window)
80+
plot[i] = [2]float64{float64(window), y}
81+
}
82+
83+
// Create JSON response.
84+
err = json.NewEncoder(w).Encode(map[string]interface{}{"xMin": int64(xMin), "xMax": int64(xMax), "curve": plot})
85+
if err != nil {
86+
log.Printf("failed to serialize response: %v", err)
87+
return
88+
}
89+
}
90+
91+
var templMMU = `<!doctype html>
92+
<html>
93+
<head>
94+
<meta charset="utf-8">
95+
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
96+
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
97+
<script type="text/javascript">
98+
google.charts.load('current', {'packages':['corechart']});
99+
google.charts.setOnLoadCallback(refreshChart);
100+
101+
function niceDuration(ns) {
102+
if (ns < 1e3) { return ns + 'ns'; }
103+
else if (ns < 1e6) { return ns / 1e3 + 'µs'; }
104+
else if (ns < 1e9) { return ns / 1e6 + 'ms'; }
105+
else { return ns / 1e9 + 's'; }
106+
}
107+
108+
function refreshChart() {
109+
$.getJSON('/mmuPlot')
110+
.fail(function(xhr, status, error) {
111+
alert('failed to load plot: ' + status);
112+
})
113+
.done(drawChart);
114+
}
115+
116+
function drawChart(plotData) {
117+
var curve = plotData.curve;
118+
var data = new google.visualization.DataTable();
119+
data.addColumn('number', 'Window duration');
120+
data.addColumn('number', 'Minimum mutator utilization');
121+
data.addRows(curve);
122+
for (var i = 0; i < curve.length; i++) {
123+
data.setFormattedValue(i, 0, niceDuration(curve[i][0]));
124+
}
125+
126+
var options = {
127+
chart: {
128+
title: 'Minimum mutator utilization',
129+
},
130+
hAxis: {
131+
title: 'Window duration',
132+
scaleType: 'log',
133+
ticks: [],
134+
},
135+
vAxis: {
136+
title: 'Minimum mutator utilization',
137+
minValue: 0.0,
138+
maxValue: 1.0,
139+
},
140+
legend: { position: 'none' },
141+
width: 900,
142+
height: 500,
143+
chartArea: { width: '80%', height: '80%' },
144+
};
145+
for (var v = plotData.xMin; v <= plotData.xMax; v *= 10) {
146+
options.hAxis.ticks.push({v:v, f:niceDuration(v)});
147+
}
148+
149+
var container = $('#mmu_chart');
150+
container.empty();
151+
var chart = new google.visualization.LineChart(container[0]);
152+
chart.draw(data, options);
153+
}
154+
</script>
155+
</head>
156+
<body>
157+
<div id="mmu_chart" style="width: 900px; height: 500px">Loading plot...</div>
158+
</body>
159+
</html>
160+
`

0 commit comments

Comments
 (0)