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  }