mirror of
https://github.com/golang/go
synced 2024-11-06 22:46:14 -07:00
5d476c5293
Currently for every benchmark/metric we show all changes for all builders x procs. With 4 builders and 5 procs, that's 20 changes (20 red/green boxes in a single cell). Instead show only maximum change for every benchmark/metric. This significantly reduces clutter in UI. When you click on the red/green box, you can see the rest of the changes. LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/126430043
281 lines
6.5 KiB
Go
281 lines
6.5 KiB
Go
// Copyright 2013 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build appengine
|
|
|
|
package build
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"appengine"
|
|
"appengine/datastore"
|
|
)
|
|
|
|
func init() {
|
|
http.HandleFunc("/perf", perfChangesHandler)
|
|
}
|
|
|
|
// perfSummaryHandler draws the main benchmarking page.
|
|
func perfChangesHandler(w http.ResponseWriter, r *http.Request) {
|
|
d := dashboardForRequest(r)
|
|
c := d.Context(appengine.NewContext(r))
|
|
|
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
|
if page < 0 {
|
|
page = 0
|
|
}
|
|
|
|
pc, err := GetPerfConfig(c, r)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
commits, err := dashPerfCommits(c, page)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
// Fetch PerfResult's for the commits.
|
|
var uiCommits []*perfChangesCommit
|
|
rc := MakePerfResultCache(c, commits[0], false)
|
|
|
|
// But first compare tip with the last release.
|
|
if page == 0 {
|
|
res0 := &PerfResult{CommitHash: knownTags[lastRelease]}
|
|
if err := datastore.Get(c, res0.Key(c), res0); err != nil && err != datastore.ErrNoSuchEntity {
|
|
logErr(w, r, fmt.Errorf("getting PerfResult: %v", err))
|
|
return
|
|
}
|
|
if err != datastore.ErrNoSuchEntity {
|
|
uiCom, err := handleOneCommit(pc, commits[0], rc, res0)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
uiCom.IsSummary = true
|
|
uiCom.ParentHash = lastRelease
|
|
uiCommits = append(uiCommits, uiCom)
|
|
}
|
|
}
|
|
|
|
for _, com := range commits {
|
|
uiCom, err := handleOneCommit(pc, com, rc, nil)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
uiCommits = append(uiCommits, uiCom)
|
|
}
|
|
|
|
p := &Pagination{}
|
|
if len(commits) == commitsPerPage {
|
|
p.Next = page + 1
|
|
}
|
|
if page > 0 {
|
|
p.Prev = page - 1
|
|
p.HasPrev = true
|
|
}
|
|
|
|
data := &perfChangesData{d, p, uiCommits}
|
|
|
|
var buf bytes.Buffer
|
|
if err := perfChangesTemplate.Execute(&buf, data); err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
buf.WriteTo(w)
|
|
}
|
|
|
|
func handleOneCommit(pc *PerfConfig, com *Commit, rc *PerfResultCache, baseRes *PerfResult) (*perfChangesCommit, error) {
|
|
uiCom := new(perfChangesCommit)
|
|
uiCom.Commit = com
|
|
res1 := rc.Get(com.Num)
|
|
for builder, benchmarks1 := range res1.ParseData() {
|
|
for benchmark, data1 := range benchmarks1 {
|
|
if benchmark != "meta-done" || !data1.OK {
|
|
uiCom.NumResults++
|
|
}
|
|
if !data1.OK {
|
|
v := new(perfChangesChange)
|
|
v.diff = 10000
|
|
v.Style = "fail"
|
|
v.Builder = builder
|
|
v.Link = fmt.Sprintf("log/%v", data1.Artifacts["log"])
|
|
v.Val = builder
|
|
v.Hint = builder
|
|
if benchmark != "meta-done" {
|
|
v.Hint += "/" + benchmark
|
|
}
|
|
m := findMetric(uiCom, "failure")
|
|
m.BadChanges = append(m.BadChanges, v)
|
|
}
|
|
}
|
|
res0 := baseRes
|
|
if res0 == nil {
|
|
var err error
|
|
res0, err = rc.NextForComparison(com.Num, builder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if res0 == nil {
|
|
continue
|
|
}
|
|
}
|
|
changes := significantPerfChanges(pc, builder, res0, res1)
|
|
changes = dedupPerfChanges(changes)
|
|
for _, ch := range changes {
|
|
v := new(perfChangesChange)
|
|
v.Builder = builder
|
|
v.Benchmark, v.Procs = splitBench(ch.Bench)
|
|
v.diff = ch.Diff
|
|
v.Val = fmt.Sprintf("%+.2f%%", ch.Diff)
|
|
v.Hint = fmt.Sprintf("%v/%v", builder, ch.Bench)
|
|
v.Link = fmt.Sprintf("perfdetail?commit=%v&commit0=%v&builder=%v&benchmark=%v", com.Hash, res0.CommitHash, builder, v.Benchmark)
|
|
m := findMetric(uiCom, ch.Metric)
|
|
if v.diff > 0 {
|
|
v.Style = "bad"
|
|
m.BadChanges = append(m.BadChanges, v)
|
|
} else {
|
|
v.Style = "good"
|
|
m.GoodChanges = append(m.GoodChanges, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort metrics and changes.
|
|
for _, m := range uiCom.Metrics {
|
|
sort.Sort(m.GoodChanges)
|
|
sort.Sort(m.BadChanges)
|
|
}
|
|
sort.Sort(uiCom.Metrics)
|
|
// Need at least one metric for UI.
|
|
if len(uiCom.Metrics) == 0 {
|
|
uiCom.Metrics = append(uiCom.Metrics, &perfChangesMetric{})
|
|
}
|
|
uiCom.Metrics[0].First = true
|
|
return uiCom, nil
|
|
}
|
|
|
|
// Find builder-procs with the maximum absolute diff for every benchmark-metric, drop the rest.
|
|
func dedupPerfChanges(changes []*PerfChange) (deduped []*PerfChange) {
|
|
maxDiff := make(map[string]float64)
|
|
maxBench := make(map[string]string)
|
|
// First, find the maximum.
|
|
for _, ch := range changes {
|
|
bench, _ := splitBench(ch.Bench)
|
|
k := bench + "|" + ch.Metric
|
|
v := ch.Diff
|
|
if v < 0 {
|
|
v = -v
|
|
}
|
|
if maxDiff[k] < v {
|
|
maxDiff[k] = v
|
|
maxBench[k] = ch.Builder + "|" + ch.Bench
|
|
}
|
|
}
|
|
// Then, remove the rest.
|
|
for _, ch := range changes {
|
|
bench, _ := splitBench(ch.Bench)
|
|
k := bench + "|" + ch.Metric
|
|
if maxBench[k] == ch.Builder+"|"+ch.Bench {
|
|
deduped = append(deduped, ch)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func findMetric(c *perfChangesCommit, metric string) *perfChangesMetric {
|
|
for _, m := range c.Metrics {
|
|
if m.Name == metric {
|
|
return m
|
|
}
|
|
}
|
|
m := new(perfChangesMetric)
|
|
m.Name = metric
|
|
c.Metrics = append(c.Metrics, m)
|
|
return m
|
|
}
|
|
|
|
type uiPerfConfig struct {
|
|
Builders []uiPerfConfigElem
|
|
Benchmarks []uiPerfConfigElem
|
|
Metrics []uiPerfConfigElem
|
|
Procs []uiPerfConfigElem
|
|
}
|
|
|
|
type uiPerfConfigElem struct {
|
|
Name string
|
|
Selected bool
|
|
}
|
|
|
|
var perfChangesTemplate = template.Must(
|
|
template.New("perf_changes.html").Funcs(tmplFuncs).ParseFiles("build/perf_changes.html"),
|
|
)
|
|
|
|
type perfChangesData struct {
|
|
Dashboard *Dashboard
|
|
Pagination *Pagination
|
|
Commits []*perfChangesCommit
|
|
}
|
|
|
|
type perfChangesCommit struct {
|
|
*Commit
|
|
IsSummary bool
|
|
NumResults int
|
|
Metrics perfChangesMetricSlice
|
|
}
|
|
|
|
type perfChangesMetric struct {
|
|
Name string
|
|
First bool
|
|
BadChanges perfChangesChangeSlice
|
|
GoodChanges perfChangesChangeSlice
|
|
}
|
|
|
|
type perfChangesChange struct {
|
|
Builder string
|
|
Benchmark string
|
|
Link string
|
|
Hint string
|
|
Style string
|
|
Val string
|
|
Procs int
|
|
diff float64
|
|
}
|
|
|
|
type perfChangesMetricSlice []*perfChangesMetric
|
|
|
|
func (l perfChangesMetricSlice) Len() int { return len(l) }
|
|
func (l perfChangesMetricSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
|
func (l perfChangesMetricSlice) Less(i, j int) bool {
|
|
if l[i].Name == "failure" || l[j].Name == "failure" {
|
|
return l[i].Name == "failure"
|
|
}
|
|
return l[i].Name < l[j].Name
|
|
}
|
|
|
|
type perfChangesChangeSlice []*perfChangesChange
|
|
|
|
func (l perfChangesChangeSlice) Len() int { return len(l) }
|
|
func (l perfChangesChangeSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
|
func (l perfChangesChangeSlice) Less(i, j int) bool {
|
|
vi, vj := l[i].diff, l[j].diff
|
|
if vi > 0 && vj > 0 {
|
|
return vi > vj
|
|
} else if vi < 0 && vj < 0 {
|
|
return vi < vj
|
|
} else {
|
|
panic("comparing positive and negative diff")
|
|
}
|
|
}
|