diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 5fb5b26..45d5cb7 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,9 +1,12 @@ name: CI -on: [push, pull_request] +on: + - push + - pull_request jobs: build: + name: Build and test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -21,3 +24,42 @@ jobs: - name: Upload coverage to Codecov run: bash <(curl -s https://codecov.io/bash) + + benchmark: + name: Benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.18 + + # Run benchmark with `go test -bench` and stores the output to a file + - name: Run benchmark + run: go test -bench . | tee output.txt + + # Download previous benchmark result from cache (if exists) + - name: Download previous benchmark data + uses: actions/cache@v3 + with: + path: ./cache + key: ${{ runner.os }}-benchmark + + # Run `github-action-benchmark` action + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + # What benchmark tool the output.txt came from + tool: 'go' + # Where the output from the benchmark tool is stored + output-file-path: output.txt + # Where the previous data file is stored + external-data-json-path: ./cache/benchmark-data.json + # Workflow will fail when an alert happens + fail-on-alert: true + # GitHub API token to make a commit comment + github-token: ${{ secrets.GITHUB_TOKEN }} + # Enable alert commit comment + comment-on-alert: true diff --git a/sliceutils.go b/sliceutils.go index ebcf44e..a4e54f7 100644 --- a/sliceutils.go +++ b/sliceutils.go @@ -114,7 +114,7 @@ func Filter[T any](slice []T, filterFn func(T) bool) []T { if slice == nil { return nil } - outSlice := make([]T, 0) + outSlice := make([]T, 0, len(slice)) for _, val := range slice { if filterFn(val) { outSlice = append(outSlice, val) diff --git a/sliceutils_test.go b/sliceutils_test.go index 854b3ff..5c3bbda 100644 --- a/sliceutils_test.go +++ b/sliceutils_test.go @@ -7,6 +7,10 @@ import ( "github.com/stretchr/testify/assert" ) +//////////////////////////////// +//********** TESTS ***********// +//////////////////////////////// + func TestAll(t *testing.T) { t.Run("All elements evaluate to true", func(t *testing.T) { slice := []int{1, 4, 6, 2, 3, 7} @@ -695,3 +699,51 @@ func TestUnion(t *testing.T) { assert.Nil(t, union) }) } + +//////////////////////////////// +//******** BENCHMARKS ********// +//////////////////////////////// + +func BenchmarkAll(b *testing.B) { + slice := []string{"boo", "bar", "baz", "hib", "heb", "obe", "lob", "suber", + "library", "functional function", "slice", "NOW", "hey"} + + b.Run("Do all strings contain rune", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var _ = All(slice, func(x string) bool { return strings.ContainsRune(x, rune('b')) }) + } + }) +} + +func BenchmarkAny(b *testing.B) { + slice := []string{"foo", "bar", "baz", "his", "her", "one", "log", "super", + "library", "functional function", "slice", "NOW", "hey"} + + b.Run("Does any string contain rune", func(b *testing.B) { + for i := 0; i < b.N; i++ { + var _ = Any(slice, func(x string) bool { return strings.ContainsRune(x, rune('W')) }) + } + }) +} + +func BenchmarkFilter(b *testing.B) { + b.Run("Filter to include strings shorter than 4 characters", func(b *testing.B) { + slice := []string{"foo", "bar", "baz", "his", "her", "one", "log", "super", + "library", "functional function", "slice", "NOW", "hey"} + + for i := 0; i < b.N; i++ { + var _ = Filter(slice, func(x string) bool { return len(x) < 4 }) + } + }) +} + +func BenchmarkFilterInPlace(b *testing.B) { + b.Run("Filter in-place to include strings shorter than 4 characters", func(b *testing.B) { + for i := 0; i < b.N; i++ { + slice := []string{"foo", "bar", "baz", "his", "her", "one", "log", "super", + "library", "functional function", "slice", "NOW", "hey"} + + FilterInPlace(&slice, func(x string) bool { return len(x) < 4 }) + } + }) +}