github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/dashboard/app/build/perf_changes.go (about) 1 // Copyright 2013 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 // +build appengine 6 7 package build 8 9 import ( 10 "bytes" 11 "fmt" 12 "html/template" 13 "net/http" 14 "sort" 15 "strconv" 16 17 "appengine" 18 "appengine/datastore" 19 ) 20 21 func init() { 22 handleFunc("/perf", perfChangesHandler) 23 } 24 25 // perfSummaryHandler draws the main benchmarking page. 26 func perfChangesHandler(w http.ResponseWriter, r *http.Request) { 27 d := dashboardForRequest(r) 28 c := d.Context(appengine.NewContext(r)) 29 30 page, _ := strconv.Atoi(r.FormValue("page")) 31 if page < 0 { 32 page = 0 33 } 34 35 pc, err := GetPerfConfig(c, r) 36 if err != nil { 37 logErr(w, r, err) 38 return 39 } 40 41 commits, err := dashPerfCommits(c, page) 42 if err != nil { 43 logErr(w, r, err) 44 return 45 } 46 47 // Fetch PerfResult's for the commits. 48 var uiCommits []*perfChangesCommit 49 rc := MakePerfResultCache(c, commits[0], false) 50 51 // But first compare tip with the last release. 52 if page == 0 { 53 res0 := &PerfResult{CommitHash: knownTags[lastRelease]} 54 if err := datastore.Get(c, res0.Key(c), res0); err != nil && err != datastore.ErrNoSuchEntity { 55 logErr(w, r, fmt.Errorf("getting PerfResult: %v", err)) 56 return 57 } 58 if err != datastore.ErrNoSuchEntity { 59 uiCom, err := handleOneCommit(pc, commits[0], rc, res0) 60 if err != nil { 61 logErr(w, r, err) 62 return 63 } 64 uiCom.IsSummary = true 65 uiCom.ParentHash = lastRelease 66 uiCommits = append(uiCommits, uiCom) 67 } 68 } 69 70 for _, com := range commits { 71 uiCom, err := handleOneCommit(pc, com, rc, nil) 72 if err != nil { 73 logErr(w, r, err) 74 return 75 } 76 uiCommits = append(uiCommits, uiCom) 77 } 78 79 p := &Pagination{} 80 if len(commits) == commitsPerPage { 81 p.Next = page + 1 82 } 83 if page > 0 { 84 p.Prev = page - 1 85 p.HasPrev = true 86 } 87 88 data := &perfChangesData{d, p, uiCommits} 89 90 var buf bytes.Buffer 91 if err := perfChangesTemplate.Execute(&buf, data); err != nil { 92 logErr(w, r, err) 93 return 94 } 95 96 buf.WriteTo(w) 97 } 98 99 func handleOneCommit(pc *PerfConfig, com *Commit, rc *PerfResultCache, baseRes *PerfResult) (*perfChangesCommit, error) { 100 uiCom := new(perfChangesCommit) 101 uiCom.Commit = com 102 res1 := rc.Get(com.Num) 103 for builder, benchmarks1 := range res1.ParseData() { 104 for benchmark, data1 := range benchmarks1 { 105 if benchmark != "meta-done" || !data1.OK { 106 uiCom.NumResults++ 107 } 108 if !data1.OK { 109 v := new(perfChangesChange) 110 v.diff = 10000 111 v.Style = "fail" 112 v.Builder = builder 113 v.Link = fmt.Sprintf("log/%v", data1.Artifacts["log"]) 114 v.Val = builder 115 v.Hint = builder 116 if benchmark != "meta-done" { 117 v.Hint += "/" + benchmark 118 } 119 m := findMetric(uiCom, "failure") 120 m.BadChanges = append(m.BadChanges, v) 121 } 122 } 123 res0 := baseRes 124 if res0 == nil { 125 var err error 126 res0, err = rc.NextForComparison(com.Num, builder) 127 if err != nil { 128 return nil, err 129 } 130 if res0 == nil { 131 continue 132 } 133 } 134 changes := significantPerfChanges(pc, builder, res0, res1) 135 changes = dedupPerfChanges(changes) 136 for _, ch := range changes { 137 v := new(perfChangesChange) 138 v.Builder = builder 139 v.Benchmark, v.Procs = splitBench(ch.Bench) 140 v.diff = ch.Diff 141 v.Val = fmt.Sprintf("%+.2f%%", ch.Diff) 142 v.Hint = fmt.Sprintf("%v/%v", builder, ch.Bench) 143 v.Link = fmt.Sprintf("perfdetail?commit=%v&commit0=%v&builder=%v&benchmark=%v", com.Hash, res0.CommitHash, builder, v.Benchmark) 144 m := findMetric(uiCom, ch.Metric) 145 if v.diff > 0 { 146 v.Style = "bad" 147 m.BadChanges = append(m.BadChanges, v) 148 } else { 149 v.Style = "good" 150 m.GoodChanges = append(m.GoodChanges, v) 151 } 152 } 153 } 154 155 // Sort metrics and changes. 156 for _, m := range uiCom.Metrics { 157 sort.Sort(m.GoodChanges) 158 sort.Sort(m.BadChanges) 159 } 160 sort.Sort(uiCom.Metrics) 161 // Need at least one metric for UI. 162 if len(uiCom.Metrics) == 0 { 163 uiCom.Metrics = append(uiCom.Metrics, &perfChangesMetric{}) 164 } 165 uiCom.Metrics[0].First = true 166 return uiCom, nil 167 } 168 169 // Find builder-procs with the maximum absolute diff for every benchmark-metric, drop the rest. 170 func dedupPerfChanges(changes []*PerfChange) (deduped []*PerfChange) { 171 maxDiff := make(map[string]float64) 172 maxBench := make(map[string]string) 173 // First, find the maximum. 174 for _, ch := range changes { 175 bench, _ := splitBench(ch.Bench) 176 k := bench + "|" + ch.Metric 177 v := ch.Diff 178 if v < 0 { 179 v = -v 180 } 181 if maxDiff[k] < v { 182 maxDiff[k] = v 183 maxBench[k] = ch.Builder + "|" + ch.Bench 184 } 185 } 186 // Then, remove the rest. 187 for _, ch := range changes { 188 bench, _ := splitBench(ch.Bench) 189 k := bench + "|" + ch.Metric 190 if maxBench[k] == ch.Builder+"|"+ch.Bench { 191 deduped = append(deduped, ch) 192 } 193 } 194 return 195 } 196 197 func findMetric(c *perfChangesCommit, metric string) *perfChangesMetric { 198 for _, m := range c.Metrics { 199 if m.Name == metric { 200 return m 201 } 202 } 203 m := new(perfChangesMetric) 204 m.Name = metric 205 c.Metrics = append(c.Metrics, m) 206 return m 207 } 208 209 type uiPerfConfig struct { 210 Builders []uiPerfConfigElem 211 Benchmarks []uiPerfConfigElem 212 Metrics []uiPerfConfigElem 213 Procs []uiPerfConfigElem 214 CommitsFrom []uiPerfConfigElem 215 CommitsTo []uiPerfConfigElem 216 } 217 218 type uiPerfConfigElem struct { 219 Name string 220 Selected bool 221 } 222 223 var perfChangesTemplate = template.Must( 224 template.New("perf_changes.html").Funcs(tmplFuncs).ParseFiles("build/perf_changes.html"), 225 ) 226 227 type perfChangesData struct { 228 Dashboard *Dashboard 229 Pagination *Pagination 230 Commits []*perfChangesCommit 231 } 232 233 type perfChangesCommit struct { 234 *Commit 235 IsSummary bool 236 NumResults int 237 Metrics perfChangesMetricSlice 238 } 239 240 type perfChangesMetric struct { 241 Name string 242 First bool 243 BadChanges perfChangesChangeSlice 244 GoodChanges perfChangesChangeSlice 245 } 246 247 type perfChangesChange struct { 248 Builder string 249 Benchmark string 250 Link string 251 Hint string 252 Style string 253 Val string 254 Procs int 255 diff float64 256 } 257 258 type perfChangesMetricSlice []*perfChangesMetric 259 260 func (l perfChangesMetricSlice) Len() int { return len(l) } 261 func (l perfChangesMetricSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 262 func (l perfChangesMetricSlice) Less(i, j int) bool { 263 if l[i].Name == "failure" || l[j].Name == "failure" { 264 return l[i].Name == "failure" 265 } 266 return l[i].Name < l[j].Name 267 } 268 269 type perfChangesChangeSlice []*perfChangesChange 270 271 func (l perfChangesChangeSlice) Len() int { return len(l) } 272 func (l perfChangesChangeSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 273 func (l perfChangesChangeSlice) Less(i, j int) bool { 274 vi, vj := l[i].diff, l[j].diff 275 if vi > 0 && vj > 0 { 276 return vi > vj 277 } else if vi < 0 && vj < 0 { 278 return vi < vj 279 } else { 280 panic("comparing positive and negative diff") 281 } 282 }