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