first
This commit is contained in:
commit
81b25d1421
47
.gitignore
vendored
Normal file
47
.gitignore
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# Go build artifacts
|
||||||
|
bin/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Configuration files for editors and tools
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.bak
|
||||||
|
|
||||||
|
# Test binary, coverage, and results
|
||||||
|
*.test
|
||||||
|
*.cover
|
||||||
|
*.cov
|
||||||
|
*.profile
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
# OS-specific files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
23
LICENSE
Normal file
23
LICENSE
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified copies of
|
||||||
|
this license document, and changing it is allowed as long as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
94
README.md
Normal file
94
README.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# TimingBench
|
||||||
|
|
||||||
|
`TimingBench` is a Go module for benchmarking the execution time of a
|
||||||
|
function. It runs the function a specified number of times and returns the
|
||||||
|
minimum, maximum, mean, and median execution times, along with other
|
||||||
|
relevant statistics. It also supports context for cancellation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Measure min, max, mean, and median execution times of a function.
|
||||||
|
- Support for context-based cancellation.
|
||||||
|
- Provides detailed timing statistics.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the `timingbench` package, use the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get git.eeqj.de/sneak/timingbench
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Here's a simple example of how to use the `timingbench` module:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
"git.eeqj.de/sneak/timingbench"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Example function to benchmark
|
||||||
|
func sampleFunction() error {
|
||||||
|
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Seed the random number generator.
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
// Define the number of iterations.
|
||||||
|
iterations := 10
|
||||||
|
|
||||||
|
// Create a context with a timeout for cancellation.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Measure the execution times of the sample function.
|
||||||
|
result, err := timingbench.TimeFunction(ctx, sampleFunction, iterations)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error measuring function: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the timing results.
|
||||||
|
fmt.Println(result.String())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TimingResult
|
||||||
|
|
||||||
|
The `TimingResult` struct holds the timing statistics for the function executions:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TimingResult struct {
|
||||||
|
Min time.Duration // Minimum execution time
|
||||||
|
Max time.Duration // Maximum execution time
|
||||||
|
Mean time.Duration // Mean execution time
|
||||||
|
Median time.Duration // Median execution time
|
||||||
|
StartTime time.Time // Start time of the benchmarking
|
||||||
|
EndTime time.Time // End time of the benchmarking
|
||||||
|
Duration time.Duration // Total duration of the benchmarking
|
||||||
|
Iterations int // Number of iterations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the WTFPL License. See the
|
||||||
|
[LICENSE](./LICENSE) file for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please send patches via email.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
`timingbench` was written by [sneak](mailto:sneak@sneak.berlin) ([website](https://sneak.berlin)).
|
77
bench.go
Normal file
77
bench.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
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
|
||||||
|
}
|
129
bench_test.go
Normal file
129
bench_test.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package timingbench
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isPrime checks if a number is a prime number.
|
||||||
|
func isPrime(n int) bool {
|
||||||
|
if n <= 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 2; i*i <= n; i++ {
|
||||||
|
if n%i == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomSleep introduces a random delay between 1 and 20 milliseconds.
|
||||||
|
func randomSleep() {
|
||||||
|
time.Sleep(time.Duration(rand.Intn(20)+1) * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatePrimes generates prime numbers up to a given limit.
|
||||||
|
func generatePrimes(limit int) []int {
|
||||||
|
primes := []int{}
|
||||||
|
for i := 2; i <= limit; i++ {
|
||||||
|
if isPrime(i) {
|
||||||
|
primes = append(primes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return primes
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortRandomSlice generates a slice with random integers and sorts it.
|
||||||
|
func sortRandomSlice(size int) {
|
||||||
|
slice := make([]int, size)
|
||||||
|
for i := range slice {
|
||||||
|
slice[i] = rand.Intn(10000)
|
||||||
|
}
|
||||||
|
quickSort(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quickSort sorts a slice of integers using the quicksort algorithm.
|
||||||
|
func quickSort(arr []int) {
|
||||||
|
if len(arr) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
left, right := 0, len(arr)-1
|
||||||
|
|
||||||
|
pivotIndex := rand.Int() % len(arr)
|
||||||
|
|
||||||
|
arr[pivotIndex], arr[right] = arr[right], arr[pivotIndex]
|
||||||
|
|
||||||
|
for i := range arr {
|
||||||
|
if arr[i] < arr[right] {
|
||||||
|
|
||||||
|
arr[i], arr[left] = arr[left], arr[i]
|
||||||
|
left++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arr[left], arr[right] = arr[right], arr[left]
|
||||||
|
|
||||||
|
quickSort(arr[:left])
|
||||||
|
quickSort(arr[left+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiplyMatrices performs matrix multiplication of two randomly generated matrices.
|
||||||
|
func multiplyMatrices(size int) {
|
||||||
|
matrixA := make([][]int, size)
|
||||||
|
matrixB := make([][]int, size)
|
||||||
|
result := make([][]int, size)
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
matrixA[i] = make([]int, size)
|
||||||
|
matrixB[i] = make([]int, size)
|
||||||
|
result[i] = make([]int, size)
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
matrixA[i][j] = rand.Intn(100)
|
||||||
|
matrixB[i][j] = rand.Intn(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
sum := 0
|
||||||
|
for k := 0; k < size; k++ {
|
||||||
|
sum += matrixA[i][k] * matrixB[k][j]
|
||||||
|
}
|
||||||
|
result[i][j] = sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sampleFunction performs a mix of different operations to introduce variability in runtime.
|
||||||
|
func sampleFunction() error {
|
||||||
|
randomSleep()
|
||||||
|
generatePrimes(500 + rand.Intn(1500))
|
||||||
|
sortRandomSlice(500 + rand.Intn(1500))
|
||||||
|
multiplyMatrices(20 + rand.Intn(10))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeFunction(t *testing.T) {
|
||||||
|
// Seed the random number generator.
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
// Define the number of iterations.
|
||||||
|
iterations := 1000
|
||||||
|
|
||||||
|
// Create a context with a timeout for cancellation.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Measure the execution times of the sample function.
|
||||||
|
result, err := TimeFunction(ctx, sampleFunction, iterations)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error measuring function: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the timing results.
|
||||||
|
t.Logf(result.String())
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module git.eeqj.de/sneak/timingbench
|
||||||
|
|
||||||
|
go 1.22.2
|
||||||
|
|
||||||
|
require gonum.org/v1/gonum v0.15.0 // indirect
|
Loading…
Reference in New Issue
Block a user