2024-05-19 01:37:05 +00:00
|
|
|
package timingbench
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
|
2024-05-19 02:51:45 +00:00
|
|
|
"github.com/schollz/progressbar/v3"
|
2024-05-19 01:37:05 +00:00
|
|
|
"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
|
2024-05-19 01:49:03 +00:00
|
|
|
StdDev time.Duration
|
2024-05-19 01:37:05 +00:00
|
|
|
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(
|
2024-05-19 01:49:03 +00:00
|
|
|
"Timing Results:\n"+
|
|
|
|
"Start Time: %v\n"+
|
|
|
|
"End Time: %v\n"+
|
|
|
|
"Total Duration: %v\n"+
|
|
|
|
"Iterations: %d\n"+
|
|
|
|
"Min Duration: %v\n"+
|
|
|
|
"Max Duration: %v\n"+
|
|
|
|
"Mean Duration: %v\n"+
|
|
|
|
"Median Duration: %v\n"+
|
|
|
|
"Standard Deviation: %v\n",
|
|
|
|
r.StartTime.Format(time.RFC3339),
|
|
|
|
r.EndTime.Format(time.RFC3339),
|
|
|
|
r.Duration,
|
|
|
|
r.Iterations,
|
|
|
|
r.Min,
|
|
|
|
r.Max,
|
|
|
|
r.Mean,
|
|
|
|
r.Median,
|
|
|
|
r.StdDev,
|
2024-05-19 01:37:05 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-05-19 02:51:45 +00:00
|
|
|
// TimeFunction runs the given function fn a specified number of times
|
|
|
|
// and returns the timing results. It supports context for cancellation.
|
2024-05-22 21:20:31 +00:00
|
|
|
func TimeFunction(ctx context.Context, fn func(), iterations int) (TimingResult, error) {
|
2024-05-19 01:37:05 +00:00
|
|
|
if iterations <= 0 {
|
2024-05-19 02:51:45 +00:00
|
|
|
return TimingResult{}, errors.New(
|
|
|
|
"iterations must be greater than 0")
|
2024-05-19 01:37:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
times := make([]float64, 0, iterations)
|
|
|
|
startTime := time.Now().UTC()
|
|
|
|
|
2024-05-19 02:51:45 +00:00
|
|
|
// Create a progress bar with throttling
|
|
|
|
bar := progressbar.NewOptions(iterations,
|
|
|
|
progressbar.OptionSetDescription("Processing..."),
|
|
|
|
progressbar.OptionShowCount(),
|
|
|
|
progressbar.OptionShowIts(),
|
|
|
|
progressbar.OptionSetPredictTime(true),
|
|
|
|
progressbar.OptionClearOnFinish(),
|
|
|
|
progressbar.OptionThrottle(100*time.Millisecond), // Update every 100ms
|
|
|
|
)
|
|
|
|
|
2024-05-19 01:37:05 +00:00
|
|
|
for i := 0; i < iterations; i++ {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return TimingResult{}, ctx.Err()
|
|
|
|
default:
|
|
|
|
iterStart := time.Now().UTC()
|
2024-05-19 02:51:45 +00:00
|
|
|
fn()
|
2024-05-19 01:37:05 +00:00
|
|
|
elapsed := time.Since(iterStart)
|
|
|
|
times = append(times, float64(elapsed))
|
2024-05-19 02:51:45 +00:00
|
|
|
|
2024-05-22 21:20:31 +00:00
|
|
|
_ = bar.Add(1)
|
2024-05-19 01:37:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
2024-05-19 01:49:03 +00:00
|
|
|
stdDev := time.Duration(stat.StdDev(times, nil))
|
2024-05-19 01:37:05 +00:00
|
|
|
|
|
|
|
return TimingResult{
|
|
|
|
Min: min,
|
|
|
|
Max: max,
|
|
|
|
Mean: mean,
|
|
|
|
Median: median,
|
2024-05-19 01:49:03 +00:00
|
|
|
StdDev: stdDev,
|
2024-05-19 01:37:05 +00:00
|
|
|
StartTime: startTime,
|
|
|
|
EndTime: endTime,
|
|
|
|
Duration: totalDuration,
|
|
|
|
Iterations: iterations,
|
|
|
|
}, nil
|
|
|
|
}
|