timingbench/bench.go

78 lines
1.9 KiB
Go

package timingbench
import (
"context"
"errors"
"fmt"
"sort"
"time"
"gonum.org/v1/gonum/stat"
)
// TimingResult holds the timing results for a function execution.
type TimingResult struct {
Min time.Duration
Max time.Duration
Mean time.Duration
Median time.Duration
StartTime time.Time
EndTime time.Time
Duration time.Duration
Iterations int
}
// String returns a formatted string representation of the TimingResult.
func (r TimingResult) String() string {
return fmt.Sprintf(
"Start Time: %v, End Time: %v, Duration: %v, Iterations: %d, Min: %v, Max: %v, Mean: %v, Median: %v",
r.StartTime, r.EndTime, r.Duration, r.Iterations, r.Min, r.Max, r.Mean, r.Median,
)
}
// TimeFunction runs the given function fn a specified number of times and returns the timing results.
// It supports context for cancellation.
func TimeFunction(ctx context.Context, fn func() error, iterations int) (TimingResult, error) {
if iterations <= 0 {
return TimingResult{}, errors.New("iterations must be greater than 0")
}
times := make([]float64, 0, iterations)
startTime := time.Now().UTC()
for i := 0; i < iterations; i++ {
select {
case <-ctx.Done():
return TimingResult{}, ctx.Err()
default:
iterStart := time.Now().UTC()
if err := fn(); err != nil {
return TimingResult{}, err
}
elapsed := time.Since(iterStart)
times = append(times, float64(elapsed))
}
}
endTime := time.Now().UTC()
totalDuration := endTime.Sub(startTime)
sort.Float64s(times)
min := time.Duration(times[0])
max := time.Duration(times[len(times)-1])
mean := time.Duration(stat.Mean(times, nil))
median := time.Duration(stat.Quantile(0.5, stat.Empirical, times, nil))
return TimingResult{
Min: min,
Max: max,
Mean: mean,
Median: median,
StartTime: startTime,
EndTime: endTime,
Duration: totalDuration,
Iterations: iterations,
}, nil
}