From 3a0966f1b33ecc617861597023bd128b7b2b4225 Mon Sep 17 00:00:00 2001 From: Shravan Asati Date: Wed, 3 Jan 2024 19:47:19 +0530 Subject: [PATCH] add range in results and rounding floats --- internal/export.go | 9 +++++++-- internal/utils.go | 20 +++++++++++++++++--- main.go | 18 ++++++++++++++---- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/internal/export.go b/internal/export.go index c188d9e..bd7770a 100644 --- a/internal/export.go +++ b/internal/export.go @@ -16,6 +16,8 @@ type Result struct { Iterations int Average string StandardDeviation string + Min string + Max string } // todo in all exports, include individual run details @@ -29,6 +31,7 @@ Ended: {{ .Ended }} Executed Command: {{ .Command }} Total iterations: {{ .Iterations }} Average time taken: {{ .Average }} ± {{ .StandardDeviation }} +Range: {{ .Min }} ... {{ .Max }} ` var summaryColor = ` @@ -40,6 +43,7 @@ ${yellow}Ended: ${green}{{ .Ended }} ${reset} ${yellow}Executed Command: ${green}{{ .Command }} ${reset} ${yellow}Total iterations: ${green}{{ .Iterations }} ${reset} ${yellow}Average time taken: ${green}{{ .Average }} ± {{ .StandardDeviation }} ${reset} +${yellow}Range: ${green}{{ .Min }} ... {{ .Max }} ${reset} ` // Consolify prints the benchmark summary of the Result struct to the console, with color codes. @@ -101,6 +105,7 @@ func markdownify(r *Result) { | Executed Command | {{.Command}} | | Total iterations | {{.Iterations}} | | Average time taken | {{.Average}} ± {{ .StandardDeviation }} | +| Range | {{.Min}} ... {{ .Max }} | ` tmpl, err := template.New("summary").Parse(text) if err != nil { @@ -133,8 +138,8 @@ func jsonify(r *Result) ([]byte, error) { // csvify converts the Result struct to CSV. func csvify(r *Result) { text := ` -Started,Ended,Executed Command,Total iterations,Average time taken -{{.Started}}, {{.Ended}}, {{.Command}}, {{.Iterations}}, {{.Average}} ± {{ .StandardDeviation }} +Started,Ended,Executed Command,Total iterations,Average time taken,Range +{{.Started}}, {{.Ended}}, {{.Command}}, {{.Iterations}}, {{.Average}} ± {{ .StandardDeviation }}, {{.Min}} ... {{.Max}} ` tmpl, err := template.New("summary").Parse(text) if err != nil { diff --git a/internal/utils.go b/internal/utils.go index f0445e1..ac594c2 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -19,6 +19,15 @@ func format(text string, params map[string]string) string { return text } +// MapFunc returns a slice of all elements in the given slice mapped by the given function. +func MapFunc[T any, S any](function func (T) S, slice []T) ([]S) { + mappedSlice := make([]S, len(slice)) + for i, v := range slice { + mappedSlice[i] = function(v) + } + return mappedSlice +} + // writeToFile writes text string to the given filename. func writeToFile(text, filename string) (err error) { f, err := os.Create(filename) @@ -31,6 +40,11 @@ func writeToFile(text, filename string) (err error) { return err } +func roundFloat(num float64, digits int) float64 { + tenMultiplier := math.Pow10(digits) + return math.Round(num * tenMultiplier) / tenMultiplier +} + type numberLike interface { ~int | ~float64 | ~int32 | ~int64 | ~float32 } @@ -49,7 +63,7 @@ func ComputeAverageAndStandardDeviation[T numberLike](population []T) (float64, deviationSum /= n deviationSum = math.Sqrt(deviationSum) - return avg, deviationSum + return roundFloat(avg, 2), roundFloat(deviationSum, 2) } func checkPathExists(fp string) bool { @@ -104,11 +118,11 @@ func DurationFromNumber[T numberLike](number T, unit time.Duration) (time.Durati // this function is only used internally, panic if unknown time unit is passed panic("unknown time unit in DurationFromNumber: " + unit.String()) } - timeString := fmt.Sprintf("%.2v%v", number, suffix) + timeString := fmt.Sprintf("%v%v", number, suffix) duration, err := time.ParseDuration(timeString) if err != nil { // again, function only used internally, invalid duration must not be present - panic("unable to parse duration in DurationFromNumber: " + err.Error()) + panic("unable to parse duration" + timeString + "in DurationFromNumber: " + err.Error()) } return duration } diff --git a/main.go b/main.go index 4683281..62bcee1 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "slices" "strings" "time" @@ -22,8 +23,6 @@ const ( // NO_COLOR is a global variable that is used to determine whether or not to enable color output. var NO_COLOR bool = false -// todo add range too - func run(command []string, verbose bool, ignoreError bool) (time.Duration, error) { // todo add shell support // todo measure shell spawn time too and deduct it from runs @@ -59,6 +58,7 @@ func main() { updateCh := make(chan string, 1) go internal.CheckForUpdates(VERSION, &updateCh) + defer fmt.Println(<-updateCh) // * basic configuration commando. @@ -122,6 +122,10 @@ func main() { internal.Log("red", "Application error: cannot parse flag values.") } + if iterations <= 0 { + return + } + // warmup runs for i := 0; i < warmupRuns; i++ { // todo replace running logs with a progress bar in non-verbose mode @@ -132,7 +136,7 @@ func main() { } } - // actual runs + // actual runs, each entry stored in microseconds var runs []int64 started := time.Now().Format("02-01-2006 15:04:05") // * looping for given iterations @@ -152,6 +156,10 @@ func main() { avg, stddev := internal.ComputeAverageAndStandardDeviation(runs) avgDuration := internal.DurationFromNumber(avg, time.Microsecond) stddevDuration := internal.DurationFromNumber(stddev, time.Microsecond) + max_ := slices.Max(runs) + min_ := slices.Min(runs) + maxDuration := internal.DurationFromNumber(max_, time.Microsecond) + minDuration := internal.DurationFromNumber(min_, time.Microsecond) result := internal.Result{ Started: started, Ended: ended, @@ -159,6 +167,8 @@ func main() { Iterations: iterations, Average: avgDuration.String(), StandardDeviation: stddevDuration.String(), + Max: maxDuration.String(), + Min: minDuration.String(), } result.Consolify() @@ -172,7 +182,7 @@ func main() { result.Export(exportFormat) }) + // todo detect statistical outliers commando.Parse(nil) - fmt.Println(<-updateCh) }