mirror of
https://github.com/golang/go
synced 2024-11-06 22:26:11 -07:00
f579fb3656
Currently we still see some flakes on perf dashboard (e.g. sys-stack is quite frequent). I am planning to run real perf builder with 5 different values of GOMAXPROCS, so we can require 3 builders to agree on a change instead of 2. This will provide 20x improvement in flake detection. At the same time lower noise bar from 1.2 to 1.1, as I see some real perf changes gets ignored as noise. All these magic numbers affect only representation of data, but not the data stored in DB. So we can continue freely tuning them later. There are no significant risks here. LGTM=adg R=adg CC=golang-codereviews https://golang.org/cl/127520044
187 lines
4.0 KiB
Go
187 lines
4.0 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"
|
|
|
|
"appengine"
|
|
"appengine/datastore"
|
|
)
|
|
|
|
func init() {
|
|
http.HandleFunc("/perflearn", perfLearnHandler)
|
|
}
|
|
|
|
const (
|
|
learnPercentile = 0.95
|
|
learnSignalMultiplier = 1.1
|
|
learnMinSignal = 0.5
|
|
)
|
|
|
|
func perfLearnHandler(w http.ResponseWriter, r *http.Request) {
|
|
d := dashboardForRequest(r)
|
|
c := d.Context(appengine.NewContext(r))
|
|
|
|
pc, err := GetPerfConfig(c, r)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
p, err := GetPackage(c, "")
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
update := r.FormValue("update") != ""
|
|
noise := make(map[string]string)
|
|
|
|
data := &perfLearnData{}
|
|
|
|
commits, err := GetCommits(c, 0, p.NextNum)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
for _, builder := range pc.BuildersForBenchmark("") {
|
|
for _, benchmark := range pc.BenchmarksForBuilder(builder) {
|
|
for _, metric := range pc.MetricsForBenchmark(benchmark) {
|
|
for _, procs := range pc.ProcList(builder) {
|
|
values, err := GetPerfMetricsForCommits(c, builder, fmt.Sprintf("%v-%v", benchmark, procs), metric, 0, p.NextNum)
|
|
if err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
var dd []float64
|
|
last := uint64(0)
|
|
for i, v := range values {
|
|
if v == 0 {
|
|
if commits[i].NeedsBenchmarking {
|
|
last = 0
|
|
}
|
|
continue
|
|
}
|
|
if last != 0 {
|
|
v1 := v
|
|
if v1 < last {
|
|
v1, last = last, v1
|
|
}
|
|
diff := float64(v1)/float64(last)*100 - 100
|
|
dd = append(dd, diff)
|
|
}
|
|
last = v
|
|
}
|
|
if len(dd) == 0 {
|
|
continue
|
|
}
|
|
sort.Float64s(dd)
|
|
|
|
baseIdx := int(float64(len(dd)) * learnPercentile)
|
|
baseVal := dd[baseIdx]
|
|
signalVal := baseVal * learnSignalMultiplier
|
|
if signalVal < learnMinSignal {
|
|
signalVal = learnMinSignal
|
|
}
|
|
signalIdx := -1
|
|
noiseNum := 0
|
|
signalNum := 0
|
|
|
|
var diffs []*perfLearnDiff
|
|
for i, d := range dd {
|
|
if d > 3*signalVal {
|
|
d = 3 * signalVal
|
|
}
|
|
diffs = append(diffs, &perfLearnDiff{Num: i, Val: d})
|
|
if signalIdx == -1 && d >= signalVal {
|
|
signalIdx = i
|
|
}
|
|
if d < signalVal {
|
|
noiseNum++
|
|
} else {
|
|
signalNum++
|
|
}
|
|
}
|
|
diffs[baseIdx].Hint = "95%"
|
|
if signalIdx != -1 {
|
|
diffs[signalIdx].Hint = "signal"
|
|
}
|
|
diffs = diffs[len(diffs)*4/5:]
|
|
name := fmt.Sprintf("%v/%v-%v/%v", builder, benchmark, procs, metric)
|
|
data.Entries = append(data.Entries, &perfLearnEntry{len(data.Entries), name, baseVal, noiseNum, signalVal, signalNum, diffs})
|
|
|
|
if len(dd) >= 100 || r.FormValue("force") != "" {
|
|
nname := fmt.Sprintf("%v|%v-%v", builder, benchmark, procs)
|
|
n := noise[nname] + fmt.Sprintf("|%v=%.2f", metric, signalVal)
|
|
noise[nname] = n
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if update {
|
|
var noiseLevels []string
|
|
for k, v := range noise {
|
|
noiseLevels = append(noiseLevels, k+v)
|
|
}
|
|
tx := func(c appengine.Context) error {
|
|
pc, err := GetPerfConfig(c, r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pc.NoiseLevels = noiseLevels
|
|
if _, err := datastore.Put(c, PerfConfigKey(c), pc); err != nil {
|
|
return fmt.Errorf("putting PerfConfig: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
if err := datastore.RunInTransaction(c, tx, nil); err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := perfLearnTemplate.Execute(&buf, data); err != nil {
|
|
logErr(w, r, err)
|
|
return
|
|
}
|
|
|
|
buf.WriteTo(w)
|
|
}
|
|
|
|
var perfLearnTemplate = template.Must(
|
|
template.New("perf_learn.html").Funcs(tmplFuncs).ParseFiles("build/perf_learn.html"),
|
|
)
|
|
|
|
type perfLearnData struct {
|
|
Entries []*perfLearnEntry
|
|
}
|
|
|
|
type perfLearnEntry struct {
|
|
Num int
|
|
Name string
|
|
BaseVal float64
|
|
NoiseNum int
|
|
SignalVal float64
|
|
SignalNum int
|
|
Diffs []*perfLearnDiff
|
|
}
|
|
|
|
type perfLearnDiff struct {
|
|
Num int
|
|
Val float64
|
|
Hint string
|
|
}
|