| // 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" |
| |
| "context" |
| "google.golang.org/appengine" |
| "google.golang.org/appengine/datastore" |
| ) |
| |
| func init() { |
| 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 com := commits[i]; com == nil || com.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 context.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("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 |
| } |