mirror of
https://github.com/metabarcoding/obitools4.git
synced 2025-06-29 16:20:46 +00:00
226 lines
5.9 KiB
Go
226 lines
5.9 KiB
Go
package obistats
|
|
|
|
//
|
|
// Dupplicated code from internal module available at :
|
|
// https://github.com/golang-design/bench.git
|
|
//
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// A Collection is a collection of benchmark results.
|
|
type Collection struct {
|
|
// Configs, Groups, and Units give the set of configs,
|
|
// groups, and units from the keys in Stats in an order
|
|
// meant to match the order the benchmarks were read in.
|
|
Configs, Groups, Units []string
|
|
|
|
// Benchmarks gives the set of benchmarks from the keys in
|
|
// Stats by group in an order meant to match the order
|
|
// benchmarks were read in.
|
|
Benchmarks map[string][]string
|
|
|
|
// Metrics holds the accumulated metrics for each key.
|
|
Metrics map[Key]*Metrics
|
|
|
|
// DeltaTest is the test to use to decide if a change is significant.
|
|
// If nil, it defaults to UTest.
|
|
DeltaTest DeltaTest
|
|
|
|
// Alpha is the p-value cutoff to report a change as significant.
|
|
// If zero, it defaults to 0.05.
|
|
Alpha float64
|
|
|
|
// AddGeoMean specifies whether to add a line to the table
|
|
// showing the geometric mean of all the benchmark results.
|
|
AddGeoMean bool
|
|
|
|
// SplitBy specifies the labels to split results by.
|
|
// By default, results will only be split by full name.
|
|
SplitBy []string
|
|
|
|
// Order specifies the row display order for this table.
|
|
// If Order is nil, the table rows are printed in order of
|
|
// first appearance in the input.
|
|
Order Order
|
|
}
|
|
|
|
// A Key identifies one metric (e.g., "ns/op", "B/op") from one
|
|
// benchmark (function name sans "Benchmark" prefix) and optional
|
|
// group in one configuration (input file name).
|
|
type Key struct {
|
|
Config, Group, Benchmark, Unit string
|
|
}
|
|
|
|
// A Metrics holds the measurements of a single metric
|
|
// (for example, ns/op or MB/s)
|
|
// for all runs of a particular benchmark.
|
|
type Metrics struct {
|
|
Unit string // unit being measured
|
|
Values []float64 // measured values
|
|
RValues []float64 // Values with outliers removed
|
|
Min float64 // min of RValues
|
|
Mean float64 // mean of RValues
|
|
Max float64 // max of RValues
|
|
}
|
|
|
|
// FormatMean formats m.Mean using scaler.
|
|
func (m *Metrics) FormatMean(scaler Scaler) string {
|
|
var s string
|
|
if scaler != nil {
|
|
s = scaler(m.Mean)
|
|
} else {
|
|
s = fmt.Sprint(m.Mean)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// FormatDiff computes and formats the percent variation of max and min compared to mean.
|
|
// If b.Mean or b.Max is zero, FormatDiff returns an empty string.
|
|
func (m *Metrics) FormatDiff() string {
|
|
if m.Mean == 0 || m.Max == 0 {
|
|
return ""
|
|
}
|
|
diff := 1 - m.Min/m.Mean
|
|
if d := m.Max/m.Mean - 1; d > diff {
|
|
diff = d
|
|
}
|
|
|
|
return fmt.Sprintf("±%.0f%%", diff*100.0)
|
|
}
|
|
|
|
// Format returns a textual formatting of "Mean ±Diff" using scaler.
|
|
func (m *Metrics) Format(scaler Scaler) string {
|
|
if m.Unit == "" {
|
|
return ""
|
|
}
|
|
mean := m.FormatMean(scaler)
|
|
diff := m.FormatDiff()
|
|
if diff == "" {
|
|
return mean + " "
|
|
}
|
|
return fmt.Sprintf("%s %3s", mean, diff)
|
|
}
|
|
|
|
// computeStats updates the derived statistics in m from the raw
|
|
// samples in m.Values.
|
|
func (m *Metrics) computeStats() {
|
|
// Discard outliers.
|
|
values := Sample{Xs: m.Values}
|
|
q1, q3 := values.Percentile(0.25), values.Percentile(0.75)
|
|
lo, hi := q1-1.5*(q3-q1), q3+1.5*(q3-q1)
|
|
for _, value := range m.Values {
|
|
if lo <= value && value <= hi {
|
|
m.RValues = append(m.RValues, value)
|
|
}
|
|
}
|
|
|
|
// Compute statistics of remaining data.
|
|
m.Min, m.Max = Bounds(m.RValues)
|
|
m.Mean = Mean(m.RValues)
|
|
}
|
|
|
|
// addMetrics returns the metrics with the given key from c,
|
|
// creating a new one if needed.
|
|
func (c *Collection) addMetrics(key Key) *Metrics {
|
|
if c.Metrics == nil {
|
|
c.Metrics = make(map[Key]*Metrics)
|
|
}
|
|
if stat, ok := c.Metrics[key]; ok {
|
|
return stat
|
|
}
|
|
|
|
addString := func(strings *[]string, add string) {
|
|
for _, s := range *strings {
|
|
if s == add {
|
|
return
|
|
}
|
|
}
|
|
*strings = append(*strings, add)
|
|
}
|
|
addString(&c.Configs, key.Config)
|
|
addString(&c.Groups, key.Group)
|
|
if c.Benchmarks == nil {
|
|
c.Benchmarks = make(map[string][]string)
|
|
}
|
|
benchmarks := c.Benchmarks[key.Group]
|
|
addString(&benchmarks, key.Benchmark)
|
|
c.Benchmarks[key.Group] = benchmarks
|
|
addString(&c.Units, key.Unit)
|
|
m := &Metrics{Unit: key.Unit}
|
|
c.Metrics[key] = m
|
|
return m
|
|
}
|
|
|
|
// // AddFile adds the benchmark results in the formatted data
|
|
// // (read from the reader r) to the named configuration.
|
|
// func (c *Collection) AddFile(config string, f io.Reader) error {
|
|
// c.Configs = append(c.Configs, config)
|
|
// key := Key{Config: config}
|
|
// br := benchfmt.NewReader(f)
|
|
// for br.Next() {
|
|
// c.addResult(key, br.Result())
|
|
// }
|
|
// return br.Err()
|
|
// }
|
|
|
|
// // AddData adds the benchmark results in the formatted data
|
|
// // (read from the reader r) to the named configuration.
|
|
// func (c *Collection) AddData(config string, data []byte) error {
|
|
// return c.AddFile(config, bytes.NewReader(data))
|
|
// }
|
|
|
|
// AddResults adds the benchmark results to the named configuration.
|
|
// func (c *Collection) AddResults(config string, results []*benchfmt.Result) {
|
|
// c.Configs = append(c.Configs, config)
|
|
// key := Key{Config: config}
|
|
// for _, r := range results {
|
|
// c.addResult(key, r)
|
|
// }
|
|
// }
|
|
|
|
// func (c *Collection) addResult(key Key, r *benchfmt.Result) {
|
|
// f := strings.Fields(r.Content)
|
|
// if len(f) < 4 {
|
|
// return
|
|
// }
|
|
// name := f[0]
|
|
// if !strings.HasPrefix(name, "Benchmark") {
|
|
// return
|
|
// }
|
|
// name = strings.TrimPrefix(name, "Benchmark")
|
|
// n, _ := strconv.Atoi(f[1])
|
|
// if n == 0 {
|
|
// return
|
|
// }
|
|
// key.Group = c.makeGroup(r)
|
|
// key.Benchmark = name
|
|
// for i := 2; i+2 <= len(f); i += 2 {
|
|
// val, err := strconv.ParseFloat(f[i], 64)
|
|
// if err != nil {
|
|
// continue
|
|
// }
|
|
// key.Unit = f[i+1]
|
|
// m := c.addMetrics(key)
|
|
// m.Values = append(m.Values, val)
|
|
// }
|
|
// }
|
|
|
|
// func (c *Collection) makeGroup(r *benchfmt.Result) string {
|
|
// var out string
|
|
// for _, s := range c.SplitBy {
|
|
// v := r.NameLabels[s]
|
|
// if v == "" {
|
|
// v = r.Labels[s]
|
|
// }
|
|
// if v != "" {
|
|
// if out != "" {
|
|
// out = out + " "
|
|
// }
|
|
// out += fmt.Sprintf("%s:%s", s, v)
|
|
// }
|
|
// }
|
|
// return out
|
|
// }
|