refactor(benchmarks): reorganize benchark/ directory (#840)

This commit is contained in:
Samuel Berthe
2026-03-08 00:14:41 +01:00
committed by GitHub
parent 191e7112e8
commit fd29945fb1
23 changed files with 2055 additions and 1910 deletions
+70
View File
@@ -0,0 +1,70 @@
# Benchmark Guidelines
## File Organization
Benchmark files follow the naming convention:
```
benchmark/{package}_{category}_bench_test.go
```
- **package**: `core`, `it`, `mutable`, `parallel`
- **category**: `slice`, `map`, `find`, `intersect`, `math`, `string`, `type_manipulation`, `condition`, `tuples`
Shared data generators live in `helpers_test.go` (and `it_helpers_test.go` for `go1.23` iter helpers).
## Performance PRs
Every performance improvement PR **must** include a `benchstat` comparison in the PR description. Without before/after numbers, the PR will not be merged.
### How to produce a benchstat report
1. Check out `master` and run the "before" benchmarks:
```bash
git stash && git switch master
go test ./benchmark/... -bench=BenchmarkXxx -benchmem -count=6 -cpu=1 | tee /tmp/before.txt
```
2. Switch to your branch and run the "after" benchmarks:
```bash
git switch my-branch && git stash pop
go test ./benchmark/... -bench=BenchmarkXxx -benchmem -count=6 -cpu=1 | tee /tmp/after.txt
```
3. Compare with `benchstat`:
```bash
benchstat /tmp/before.txt /tmp/after.txt
```
4. Paste the full `benchstat` output in the PR description.
### What to include in the PR description
- The optimization technique (pre-allocation, direct indexing, value receivers, etc.)
- The `benchstat` table showing time/op, allocs/op, and bytes/op deltas
- An explanation of **why** the change is faster, not just **what** changed
### When NOT to submit a performance PR
- If `benchstat` shows no statistically significant improvement (p >= 0.05)
- If the improvement is < 5% and adds code complexity
- If the change regresses other benchmarks — always run the full suite, not just the targeted benchmark
## Adding New Benchmarks
When adding a new helper function to the library, add a corresponding benchmark in the appropriate `{package}_{category}_bench_test.go` file. Use the standard parametric pattern:
```go
func BenchmarkMyFunc(b *testing.B) {
for _, n := range lengths {
data := genSliceInt(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MyFunc(data, ...)
}
})
}
}
```
Use shared generators from `helpers_test.go` — do not create local generator functions.
File diff suppressed because it is too large Load Diff
+310
View File
@@ -0,0 +1,310 @@
package benchmark
import (
"strconv"
"testing"
"github.com/samber/lo"
)
func BenchmarkIndexOf(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[n-1] // worst case: last element
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.IndexOf(ints, target)
}
})
}
}
func BenchmarkLastIndexOf(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[0]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.LastIndexOf(ints, target)
}
})
}
}
func BenchmarkHasPrefix(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
prefix := ints[:n/10+1]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.HasPrefix(ints, prefix)
}
})
}
}
func BenchmarkHasSuffix(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
suffix := ints[n-n/10-1:]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.HasSuffix(ints, suffix)
}
})
}
}
func BenchmarkFind(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[n-1]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.Find(ints, func(v int) bool { return v == target })
}
})
}
}
func BenchmarkFindIndexOf(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[n-1]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _, _ = lo.FindIndexOf(ints, func(v int) bool { return v == target })
}
})
}
}
func BenchmarkFindLastIndexOf(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[0]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _, _ = lo.FindLastIndexOf(ints, func(v int) bool { return v == target })
}
})
}
}
func BenchmarkFindOrElse(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FindOrElse(ints, -1, func(v int) bool { return v == -999 })
}
})
}
}
func BenchmarkFindKey(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.FindKey(m, n/2)
}
})
}
}
func BenchmarkFindKeyBy(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.FindKeyBy(m, func(_ string, v int) bool { return v == n/2 })
}
})
}
}
func BenchmarkFindUniques(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FindUniques(ints)
}
})
}
}
func BenchmarkFindUniquesBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FindUniquesBy(ints, func(v int) int { return v % 50 })
}
})
}
}
func BenchmarkFindDuplicates(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FindDuplicates(ints)
}
})
}
}
func BenchmarkFindDuplicatesBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FindDuplicatesBy(ints, func(v int) int { return v % 50 })
}
})
}
}
func BenchmarkMin(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Min(ints)
}
})
}
}
func BenchmarkMinIndex(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.MinIndex(ints)
}
})
}
}
func BenchmarkMinBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MinBy(ints, func(a, b int) bool { return a < b })
}
})
}
}
func BenchmarkMinIndexBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.MinIndexBy(ints, func(a, b int) bool { return a < b })
}
})
}
}
func BenchmarkMax(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Max(ints)
}
})
}
}
func BenchmarkMaxIndex(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.MaxIndex(ints)
}
})
}
}
func BenchmarkMaxBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MaxBy(ints, func(a, b int) bool { return a > b })
}
})
}
}
func BenchmarkMaxIndexBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.MaxIndexBy(ints, func(a, b int) bool { return a > b })
}
})
}
}
func BenchmarkFirst(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_, _ = lo.First(ints)
}
}
func BenchmarkFirstOrEmpty(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_ = lo.FirstOrEmpty(ints)
}
}
func BenchmarkLast(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_, _ = lo.Last(ints)
}
}
func BenchmarkLastOrEmpty(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_ = lo.LastOrEmpty(ints)
}
}
func BenchmarkNth(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_, _ = lo.Nth(ints, 50)
}
}
func BenchmarkSample(b *testing.B) {
ints := genSliceInt(100)
for i := 0; i < b.N; i++ {
_ = lo.Sample(ints)
}
}
func BenchmarkSamples(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Samples(ints, n/4)
}
})
}
}
+198
View File
@@ -0,0 +1,198 @@
package benchmark
import (
"strconv"
"testing"
"github.com/samber/lo"
)
func BenchmarkContains(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[n-1]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Contains(ints, target)
}
})
}
}
func BenchmarkContainsBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
target := ints[n-1]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ContainsBy(ints, func(v int) bool { return v == target })
}
})
}
}
func BenchmarkEvery(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
subset := ints[:n/2]
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Every(ints, subset)
}
})
}
}
func BenchmarkEveryBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.EveryBy(ints, func(v int) bool { return v >= 0 })
}
})
}
}
func BenchmarkSome(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
subset := []int{ints[n-1]}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Some(ints, subset)
}
})
}
}
func BenchmarkSomeBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.SomeBy(ints, func(v int) bool { return v < 0 })
}
})
}
}
func BenchmarkNone(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
subset := []int{-1, -2, -3}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.None(ints, subset)
}
})
}
}
func BenchmarkNoneBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.NoneBy(ints, func(v int) bool { return v < 0 })
}
})
}
}
func BenchmarkIntersect(b *testing.B) {
for _, n := range lengths {
a := genSliceInt(n)
c := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Intersect(a, c)
}
})
}
}
func BenchmarkIntersectBy(b *testing.B) {
for _, n := range lengths {
a := genSliceInt(n)
c := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.IntersectBy(func(v int) int { return v }, a, c)
}
})
}
}
func BenchmarkUnion(b *testing.B) {
for _, n := range lengths {
a := genSliceInt(n)
c := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Union(a, c)
}
})
}
}
func BenchmarkWithout(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Without(ints, 1, 2, 3, 4, 5)
}
})
}
}
func BenchmarkWithoutBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.WithoutBy(ints, func(v int) int { return v % 100 }, 1, 2, 3, 4, 5)
}
})
}
}
func BenchmarkWithoutEmpty(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
// sprinkle some zeroes
for j := 0; j < n/10; j++ {
ints[j*10] = 0
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.WithoutEmpty(ints) //nolint:staticcheck
}
})
}
}
func BenchmarkWithoutNth(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.WithoutNth(ints, 0, n/2, n-1)
}
})
}
}
func BenchmarkElementsMatch(b *testing.B) {
for _, n := range lengths {
a := genSliceInt(n)
c := make([]int, n)
copy(c, a)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ElementsMatch(a, c)
}
})
}
}
+340
View File
@@ -0,0 +1,340 @@
package benchmark
import (
"strconv"
"testing"
"github.com/samber/lo"
lop "github.com/samber/lo/parallel"
"github.com/thoas/go-funk"
)
func BenchmarkKeys(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Keys(m)
}
})
}
}
func BenchmarkUniqKeys(b *testing.B) {
for _, n := range lengths {
m1 := genMap(n)
m2 := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.UniqKeys(m1, m2)
}
})
}
}
func BenchmarkHasKey(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
key := strconv.Itoa(n / 2)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.HasKey(m, key)
}
})
}
}
func BenchmarkValues(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Values(m)
}
})
}
}
func BenchmarkUniqValues(b *testing.B) {
m := []map[int64]int64{
mapGenerator(1000),
mapGenerator(1000),
mapGenerator(1000),
}
b.Run("lo.UniqValues", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.UniqValues(m...)
}
})
}
func BenchmarkValueOr(b *testing.B) {
m := genMap(100)
b.Run("hit", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ValueOr(m, "50", -1)
}
})
b.Run("miss", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ValueOr(m, "missing", -1)
}
})
}
func BenchmarkPickBy(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.PickBy(m, func(_ string, v int) bool { return v%2 == 0 })
}
})
}
}
func BenchmarkPickByKeys(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
keys := make([]string, n/2)
for i := range keys {
keys[i] = strconv.Itoa(i * 2)
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.PickByKeys(m, keys)
}
})
}
}
func BenchmarkPickByValues(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
vals := make([]int, n/2)
for i := range vals {
vals[i] = i * 2
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.PickByValues(m, vals)
}
})
}
}
func BenchmarkOmitBy(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.OmitBy(m, func(_ string, v int) bool { return v%2 == 0 })
}
})
}
}
func BenchmarkOmitByKeys(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
keys := make([]string, n/4)
for i := range keys {
keys[i] = strconv.Itoa(i)
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.OmitByKeys(m, keys)
}
})
}
}
func BenchmarkOmitByValues(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
vals := make([]int, n/4)
for i := range vals {
vals[i] = i
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.OmitByValues(m, vals)
}
})
}
}
func BenchmarkEntries(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Entries(m)
}
})
}
}
func BenchmarkFromEntries(b *testing.B) {
for _, n := range lengths {
entries := make([]lo.Entry[string, int], n)
for i := 0; i < n; i++ {
entries[i] = lo.Entry[string, int]{Key: strconv.Itoa(i), Value: i}
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromEntries(entries)
}
})
}
}
func BenchmarkInvert(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Invert(m)
}
})
}
}
func BenchmarkAssign(b *testing.B) {
for _, n := range lengths {
m1 := genMap(n)
m2 := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Assign(m1, m2)
}
})
}
}
func BenchmarkChunkEntries(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ChunkEntries(m, 5)
}
})
}
}
func BenchmarkMapKeys(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MapKeys(m, func(_ int, k string) string { return k + "_x" })
}
})
}
}
func BenchmarkMapValues(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MapValues(m, func(v int, _ string) int { return v * 2 })
}
})
}
}
func BenchmarkMapEntries(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MapEntries(m, func(k string, v int) (string, int) { return k, v * 2 })
}
})
}
}
func BenchmarkMapToSlice(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MapToSlice(m, func(k string, v int) string { return k + "=" + strconv.Itoa(v) })
}
})
}
}
func BenchmarkFilterMapToSlice(b *testing.B) {
for _, n := range lengths {
m := genMap(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FilterMapToSlice(m, func(k string, v int) (string, bool) { return k, v%2 == 0 })
}
})
}
}
func BenchmarkFilterKeys(b *testing.B) {
m := mapGenerator(1000)
b.Run("lo.FilterKeys", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.FilterKeys(m, func(k, v int64) bool { return k%2 == 0 })
}
})
}
func BenchmarkFilterValues(b *testing.B) {
m := mapGenerator(1000)
b.Run("lo.FilterValues", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.FilterValues(m, func(k, v int64) bool { return v%2 == 0 })
}
})
}
// ---------------------------------------------------------------------------
// Comparison benchmarks (lo vs lop vs go-funk vs manual loop)
// ---------------------------------------------------------------------------
func BenchmarkMapComparison(b *testing.B) {
arr := sliceGenerator(1000000)
b.Run("lo.Map", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.Map(arr, func(x int64, i int) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("lop.Map", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lop.Map(arr, func(x int64, i int) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("reflect", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = funk.Map(arr, func(x int64) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("for", func(b *testing.B) {
for n := 0; n < b.N; n++ {
results := make([]string, len(arr))
for i, item := range arr {
result := strconv.FormatInt(item, 10)
results[i] = result
}
}
})
}
+124
View File
@@ -0,0 +1,124 @@
package benchmark
import (
"strconv"
"testing"
"github.com/samber/lo"
)
func BenchmarkRange(b *testing.B) {
for _, n := range lengths {
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Range(n)
}
})
}
}
func BenchmarkRangeFrom(b *testing.B) {
for _, n := range lengths {
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.RangeFrom(0, n)
}
})
}
}
func BenchmarkRangeWithSteps(b *testing.B) {
for _, n := range lengths {
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.RangeWithSteps(0, n, 1)
}
})
}
}
func BenchmarkClamp(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Clamp(15, 0, 10)
}
}
func BenchmarkSum(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Sum(ints)
}
})
}
}
func BenchmarkSumBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.SumBy(ints, func(v int) int { return v })
}
})
}
}
func BenchmarkProduct(b *testing.B) {
for _, n := range lengths {
floats := make([]float64, n)
for j := range floats {
floats[j] = 1.0001
}
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Product(floats)
}
})
}
}
func BenchmarkProductBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ProductBy(ints, func(v int) float64 { return float64(v) * 0.001 })
}
})
}
}
func BenchmarkMean(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Mean(ints)
}
})
}
}
func BenchmarkMeanBy(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.MeanBy(ints, func(v int) int { return v })
}
})
}
}
func BenchmarkMode(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Mode(ints)
}
})
}
}
@@ -2,15 +2,13 @@ package benchmark
import ( import (
"fmt" "fmt"
"math/rand" "sort"
"strconv" "strconv"
"testing" "testing"
"github.com/samber/lo" "github.com/samber/lo"
) )
var lengths = []int{10, 100, 1000}
func BenchmarkChunk(b *testing.B) { func BenchmarkChunk(b *testing.B) {
for _, n := range lengths { for _, n := range lengths {
strs := genSliceString(n) strs := genSliceString(n)
@@ -31,34 +29,6 @@ func BenchmarkChunk(b *testing.B) {
} }
} }
func genSliceString(n int) []string {
res := make([]string, 0, n)
for i := 0; i < n; i++ {
res = append(res, strconv.Itoa(rand.Intn(100_000)))
}
return res
}
func genSliceInt(n int) []int {
res := make([]int, 0, n)
for i := 0; i < n; i++ {
res = append(res, rand.Intn(100_000))
}
return res
}
type heavy = [100]int
func genSliceHeavy(n int) []heavy {
result := make([]heavy, n)
for i := range result {
for j := range result[i] {
result[i][j] = i + j
}
}
return result
}
func BenchmarkFlatten(b *testing.B) { func BenchmarkFlatten(b *testing.B) {
for _, n := range lengths { for _, n := range lengths {
ints := make([][]int, 0, n) ints := make([][]int, 0, n)
@@ -219,53 +189,6 @@ func BenchmarkReplace(b *testing.B) {
} }
} }
func BenchmarkToSlicePtr(b *testing.B) {
preallocated := make([]int, 100000)
for i := 0; i < b.N; i++ {
_ = lo.ToSlicePtr(preallocated)
}
}
func BenchmarkFromSlicePtr(b *testing.B) {
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceInt(n))
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtr(ptrs)
}
})
}
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceString(n))
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtr(ptrs)
}
})
}
}
func BenchmarkFromSlicePtrOr(b *testing.B) {
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceInt(n))
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtrOr(ptrs, -1)
}
})
}
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceString(n))
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtrOr(ptrs, "default")
}
})
}
}
func BenchmarkReject(b *testing.B) { func BenchmarkReject(b *testing.B) {
for _, n := range lengths { for _, n := range lengths {
strs := genSliceString(n) strs := genSliceString(n)
@@ -364,14 +287,6 @@ func BenchmarkRepeatBy(b *testing.B) {
} }
} }
type clonableString struct {
val string
}
func (c clonableString) Clone() clonableString {
return clonableString{c.val}
}
func BenchmarkFill(b *testing.B) { func BenchmarkFill(b *testing.B) {
for _, n := range lengths { for _, n := range lengths {
collection := make([]clonableString, n) collection := make([]clonableString, n)
@@ -869,6 +784,18 @@ func BenchmarkIsSortedBy(b *testing.B) {
} }
} }
func BenchmarkIsSortedBySorted(b *testing.B) {
for _, n := range lengths {
data := genSliceInt(n)
sort.Ints(data)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
lo.IsSortedBy(data, func(v int) int { return v })
}
})
}
}
func BenchmarkSplice(b *testing.B) { func BenchmarkSplice(b *testing.B) {
for _, n := range lengths { for _, n := range lengths {
ints := genSliceInt(n) ints := genSliceInt(n)
@@ -1046,3 +973,43 @@ func BenchmarkDifference(b *testing.B) {
}) })
} }
} }
func BenchmarkFromSlicePtr(b *testing.B) {
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceInt(n))
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtr(ptrs)
}
})
}
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceString(n))
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtr(ptrs)
}
})
}
}
func BenchmarkFromSlicePtrOr(b *testing.B) {
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceInt(n))
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtrOr(ptrs, -1)
}
})
}
for _, n := range lengths {
ptrs := lo.ToSlicePtr(genSliceString(n))
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.FromSlicePtrOr(ptrs, "default")
}
})
}
}
+77
View File
@@ -0,0 +1,77 @@
package benchmark
import (
"testing"
"github.com/samber/lo"
)
func BenchmarkRandomString(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.RandomString(64, lo.AlphanumericCharset)
}
}
func BenchmarkSubstring(b *testing.B) {
s := lo.RandomString(1000, lo.LettersCharset)
for i := 0; i < b.N; i++ {
_ = lo.Substring(s, 100, 200)
}
}
func BenchmarkChunkString(b *testing.B) {
s := lo.RandomString(1000, lo.LettersCharset)
for i := 0; i < b.N; i++ {
_ = lo.ChunkString(s, 10)
}
}
func BenchmarkRuneLength(b *testing.B) {
s := lo.RandomString(1000, lo.LettersCharset)
for i := 0; i < b.N; i++ {
_ = lo.RuneLength(s)
}
}
func BenchmarkPascalCase(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.PascalCase("some_long_variable_name")
}
}
func BenchmarkCamelCase(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.CamelCase("some_long_variable_name")
}
}
func BenchmarkKebabCase(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.KebabCase("someLongVariableName")
}
}
func BenchmarkSnakeCase(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.SnakeCase("someLongVariableName")
}
}
func BenchmarkWords(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Words("someLongVariableName")
}
}
func BenchmarkCapitalize(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.Capitalize("hello world")
}
}
func BenchmarkEllipsis(b *testing.B) {
s := lo.RandomString(200, lo.LettersCharset)
for i := 0; i < b.N; i++ {
_ = lo.Ellipsis(s, 50)
}
}
@@ -30,3 +30,17 @@ func BenchmarkZip2_Unequal(b *testing.B) {
}) })
} }
} }
func BenchmarkUnzip2(b *testing.B) {
for _, n := range lengths {
tuples := make([]lo.Tuple2[int, string], n)
for i := range tuples {
tuples[i] = lo.Tuple2[int, string]{A: i, B: "x"}
}
b.Run(fmt.Sprintf("n_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
lo.Unzip2(tuples)
}
})
}
}
@@ -0,0 +1,79 @@
package benchmark
import (
"strconv"
"testing"
"github.com/samber/lo"
)
func BenchmarkToPtr(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ToPtr(42)
}
}
func BenchmarkFromPtr(b *testing.B) {
p := lo.ToPtr(42)
for i := 0; i < b.N; i++ {
_ = lo.FromPtr(p)
}
}
func BenchmarkFromPtrOr(b *testing.B) {
var p *int
for i := 0; i < b.N; i++ {
_ = lo.FromPtrOr(p, 99)
}
}
func BenchmarkToSlicePtr(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ToSlicePtr(ints)
}
})
}
}
func BenchmarkToAnySlice(b *testing.B) {
for _, n := range lengths {
ints := genSliceInt(n)
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.ToAnySlice(ints)
}
})
}
}
func BenchmarkFromAnySlice(b *testing.B) {
for _, n := range lengths {
anys := lo.ToAnySlice(genSliceInt(n))
b.Run(strconv.Itoa(n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.FromAnySlice[int](anys)
}
})
}
}
func BenchmarkIsEmpty(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.IsEmpty(0)
}
}
func BenchmarkIsNotEmpty(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = lo.IsNotEmpty(42)
}
}
func BenchmarkCoalesce(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = lo.Coalesce(0, 0, 0, 42, 99)
}
}
+79
View File
@@ -0,0 +1,79 @@
package benchmark
import (
"math/rand"
"strconv"
"time"
)
var lengths = []int{10, 100, 1000}
func genSliceString(n int) []string {
res := make([]string, 0, n)
for i := 0; i < n; i++ {
res = append(res, strconv.Itoa(rand.Intn(100_000)))
}
return res
}
func genSliceInt(n int) []int {
res := make([]int, 0, n)
for i := 0; i < n; i++ {
res = append(res, rand.Intn(100_000))
}
return res
}
type heavy = [100]int
func genSliceHeavy(n int) []heavy {
result := make([]heavy, n)
for i := range result {
for j := range result[i] {
result[i][j] = i + j
}
}
return result
}
func genMap(n int) map[string]int {
m := make(map[string]int, n)
for i := 0; i < n; i++ {
m[strconv.Itoa(i)] = i
}
return m
}
type clonableString struct {
val string
}
func (c clonableString) Clone() clonableString {
return clonableString{c.val}
}
// sliceGenerator creates a random int64 slice (used by comparison benchmarks).
func sliceGenerator(size uint) []int64 {
r := rand.New(rand.NewSource(time.Now().Unix()))
result := make([]int64, size)
for i := uint(0); i < size; i++ {
result[i] = r.Int63()
}
return result
}
// mapGenerator creates a random int64 map (used by comparison benchmarks).
func mapGenerator(size uint) map[int64]int64 {
r := rand.New(rand.NewSource(time.Now().Unix()))
result := make(map[int64]int64, size)
for i := uint(0); i < size; i++ {
result[int64(i)] = r.Int63()
}
return result
}
+353
View File
@@ -0,0 +1,353 @@
//go:build go1.23
package benchmark
import (
"fmt"
"math/rand/v2"
"testing"
"github.com/samber/lo/it"
)
func BenchmarkItFind(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Find(ints, func(x int) bool { return x == 0 })
}
})
}
}
func BenchmarkItContains(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Contains(ints, 42)
}
})
}
}
func BenchmarkItContainsBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.ContainsBy(ints, func(x int) bool { return x == target })
}
})
}
}
func BenchmarkItEvery(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Every(ints, 1, 2, 3)
}
})
}
}
func BenchmarkItEveryBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.EveryBy(ints, func(x int) bool { return x >= 0 })
}
})
}
}
func BenchmarkItSome(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Some(ints, 1, 2, 3)
}
})
}
}
func BenchmarkItSomeBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.SomeBy(ints, func(x int) bool { return x == target })
}
})
}
}
func BenchmarkItNone(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.None(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItNoneBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.NoneBy(ints, func(x int) bool { return x == target })
}
})
}
}
func BenchmarkItIntersect(b *testing.B) {
for _, n := range itLengths {
a := genInts(n)
c := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Intersect(a, c) { //nolint:revive
}
}
})
}
}
func BenchmarkItUnion(b *testing.B) {
for _, n := range itLengths {
a := genInts(n)
c := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Union(a, c) { //nolint:revive
}
}
})
}
}
func BenchmarkItWithout(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Without(ints, 1, 2, 3, 4, 5) { //nolint:revive
}
}
})
}
}
func BenchmarkItWithoutNth(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.WithoutNth(ints, 0, n/2, n-1) { //nolint:revive
}
}
})
}
}
func BenchmarkItIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.IndexOf(ints, -1)
}
})
}
}
func BenchmarkItLastIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.LastIndexOf(ints, -1)
}
})
}
}
func BenchmarkItHasPrefix(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.HasPrefix(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItHasSuffix(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.HasSuffix(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItFindIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _, _ = it.FindIndexOf(ints, func(x int) bool { return x == -1 })
}
})
}
}
func BenchmarkItFindOrElse(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.FindOrElse(ints, -1, func(x int) bool { return x == -1 })
}
})
}
}
func BenchmarkItFindUniques(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FindUniques(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItFindDuplicates(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FindDuplicates(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItMin(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Min(ints)
}
})
}
}
func BenchmarkItMax(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Max(ints)
}
})
}
}
func BenchmarkItMinBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.MinBy(ints, func(a, b int) bool { return a < b })
}
})
}
}
func BenchmarkItMaxBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.MaxBy(ints, func(a, b int) bool { return a > b })
}
})
}
}
func BenchmarkItFirst(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.First(ints)
}
})
}
}
func BenchmarkItLast(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Last(ints)
}
})
}
}
func BenchmarkItNth(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Nth(ints, n/2)
}
})
}
}
func BenchmarkItSample(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Sample(ints)
}
})
}
}
func BenchmarkItSamples(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Samples(ints, 5) { //nolint:revive
}
}
})
}
}
+61
View File
@@ -0,0 +1,61 @@
//go:build go1.23
package benchmark
import (
"iter"
"math/rand/v2"
"strconv"
)
var itLengths = []int{10, 100, 1000}
func genStrings(n int) iter.Seq[string] {
return func(yield func(string) bool) {
for range n {
if !yield(strconv.Itoa(rand.IntN(100_000))) {
break
}
}
}
}
func genInts(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for range n {
if !yield(rand.IntN(100_000)) {
break
}
}
}
}
func genIntPtrSeq(n int) iter.Seq[*int] {
return func(yield func(*int) bool) {
for range n {
v := rand.IntN(100_000)
if !yield(&v) {
break
}
}
}
}
func genMapStringInt(n int) map[string]int {
m := make(map[string]int, n)
for i := range n {
m[strconv.Itoa(i)] = rand.IntN(100_000)
}
return m
}
func genMapSeq(n int) iter.Seq[map[string]int] {
return func(yield func(map[string]int) bool) {
for range n {
m := map[string]int{strconv.Itoa(rand.IntN(100_000)): rand.IntN(100_000)}
if !yield(m) {
break
}
}
}
}
+147
View File
@@ -0,0 +1,147 @@
//go:build go1.23
package benchmark
import (
"fmt"
"strings"
"testing"
"github.com/samber/lo/it"
)
func BenchmarkItKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Keys(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItUniqKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.UniqKeys(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Values(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItUniqValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.UniqValues(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItEntries(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Entries(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItFromEntries(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
entries := it.Entries(m)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
_ = it.FromEntries(entries)
}
})
}
}
func BenchmarkItInvert(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
entries := it.Entries(m)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Invert(entries) { //nolint:revive
}
}
})
}
}
func BenchmarkItAssign(b *testing.B) {
for _, n := range itLengths {
seq := genMapSeq(n)
b.Run(fmt.Sprintf("maps_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Assign(seq)
}
})
}
}
func BenchmarkItFilterKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.FilterKeys(m, func(_ string, v int) bool { return v%2 == 0 }) { //nolint:revive
}
}
})
}
}
func BenchmarkItFilterValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.FilterValues(m, func(_ string, v int) bool { return v%2 == 0 }) { //nolint:revive
}
}
})
}
}
func BenchmarkItChunkString(b *testing.B) {
for _, n := range itLengths {
var sb strings.Builder
for range n {
sb.WriteString("a")
}
s := sb.String()
b.Run(fmt.Sprintf("len_%d", n), func(b *testing.B) {
for range b.N {
for range it.ChunkString(s, 5) { //nolint:revive
}
}
})
}
}
+87
View File
@@ -0,0 +1,87 @@
//go:build go1.23
package benchmark
import (
"fmt"
"testing"
"github.com/samber/lo/it"
)
func BenchmarkItSum(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Sum(ints)
}
})
}
}
func BenchmarkItSumBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.SumBy(ints, func(x int) int { return x })
}
})
}
}
func BenchmarkItProduct(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Product(ints)
}
})
}
}
func BenchmarkItMean(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Mean(ints)
}
})
}
}
func BenchmarkItMode(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Mode(ints)
}
})
}
}
func BenchmarkItRange(b *testing.B) {
for _, n := range itLengths {
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Range(n) { //nolint:revive
}
}
})
}
}
func BenchmarkItLength(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Length(ints)
}
})
}
}
@@ -5,16 +5,11 @@ package benchmark
import ( import (
"fmt" "fmt"
"iter" "iter"
"math/rand/v2"
"strconv"
"strings"
"testing" "testing"
"github.com/samber/lo/it" "github.com/samber/lo/it"
) )
var itLengths = []int{10, 100, 1000}
func BenchmarkItChunk(b *testing.B) { func BenchmarkItChunk(b *testing.B) {
for _, n := range itLengths { for _, n := range itLengths {
strs := genStrings(n) strs := genStrings(n)
@@ -37,26 +32,6 @@ func BenchmarkItChunk(b *testing.B) {
} }
} }
func genStrings(n int) iter.Seq[string] {
return func(yield func(string) bool) {
for range n {
if !yield(strconv.Itoa(rand.IntN(100_000))) {
break
}
}
}
}
func genInts(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for range n {
if !yield(rand.IntN(100_000)) {
break
}
}
}
}
func BenchmarkItFlatten(b *testing.B) { func BenchmarkItFlatten(b *testing.B) {
for _, n := range itLengths { for _, n := range itLengths {
ints := make([]iter.Seq[int], 0, n) ints := make([]iter.Seq[int], 0, n)
@@ -241,105 +216,6 @@ func BenchmarkItTrimSuffix(b *testing.B) {
} }
} }
func BenchmarkItCountBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.CountBy(ints, func(x int) bool { return x%2 == 0 })
}
})
}
}
func BenchmarkItFind(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Find(ints, func(x int) bool { return x == 0 })
}
})
}
}
func BenchmarkItContainsBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.ContainsBy(ints, func(x int) bool { return x == target })
}
})
}
}
func BenchmarkItEveryBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.EveryBy(ints, func(x int) bool { return x >= 0 })
}
})
}
}
func BenchmarkItSomeBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.SomeBy(ints, func(x int) bool { return x == target })
}
})
}
}
func BenchmarkItNoneBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
target := rand.IntN(100_000)
for range b.N {
_ = it.NoneBy(ints, func(x int) bool { return x == target })
}
})
}
}
func genIntPtrSeq(n int) iter.Seq[*int] {
return func(yield func(*int) bool) {
for range n {
v := rand.IntN(100_000)
if !yield(&v) {
break
}
}
}
}
func genMapStringInt(n int) map[string]int {
m := make(map[string]int, n)
for i := range n {
m[strconv.Itoa(i)] = rand.IntN(100_000)
}
return m
}
func genMapSeq(n int) iter.Seq[map[string]int] {
return func(yield func(map[string]int) bool) {
for range n {
m := map[string]int{strconv.Itoa(rand.IntN(100_000)): rand.IntN(100_000)}
if !yield(m) {
break
}
}
}
}
func BenchmarkItFilter(b *testing.B) { func BenchmarkItFilter(b *testing.B) {
for _, n := range itLengths { for _, n := range itLengths {
ints := genInts(n) ints := genInts(n)
@@ -648,6 +524,17 @@ func BenchmarkItCount(b *testing.B) {
} }
} }
func BenchmarkItCountBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.CountBy(ints, func(x int) bool { return x%2 == 0 })
}
})
}
}
func BenchmarkItCountValues(b *testing.B) { func BenchmarkItCountValues(b *testing.B) {
for _, n := range itLengths { for _, n := range itLengths {
ints := genInts(n) ints := genInts(n)
@@ -765,537 +652,3 @@ func BenchmarkItBuffer(b *testing.B) {
}) })
} }
} }
func BenchmarkItContains(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Contains(ints, 42)
}
})
}
}
func BenchmarkItEvery(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Every(ints, 1, 2, 3)
}
})
}
}
func BenchmarkItSome(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Some(ints, 1, 2, 3)
}
})
}
}
func BenchmarkItNone(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.None(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItIntersect(b *testing.B) {
for _, n := range itLengths {
a := genInts(n)
c := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Intersect(a, c) { //nolint:revive
}
}
})
}
}
func BenchmarkItUnion(b *testing.B) {
for _, n := range itLengths {
a := genInts(n)
c := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Union(a, c) { //nolint:revive
}
}
})
}
}
func BenchmarkItWithout(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Without(ints, 1, 2, 3, 4, 5) { //nolint:revive
}
}
})
}
}
func BenchmarkItWithoutNth(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.WithoutNth(ints, 0, n/2, n-1) { //nolint:revive
}
}
})
}
}
func BenchmarkItIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.IndexOf(ints, -1)
}
})
}
}
func BenchmarkItLastIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.LastIndexOf(ints, -1)
}
})
}
}
func BenchmarkItHasPrefix(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.HasPrefix(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItHasSuffix(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.HasSuffix(ints, -1, -2, -3)
}
})
}
}
func BenchmarkItFindIndexOf(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _, _ = it.FindIndexOf(ints, func(x int) bool { return x == -1 })
}
})
}
}
func BenchmarkItFindOrElse(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.FindOrElse(ints, -1, func(x int) bool { return x == -1 })
}
})
}
}
func BenchmarkItFindUniques(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FindUniques(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItFindDuplicates(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FindDuplicates(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItMin(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Min(ints)
}
})
}
}
func BenchmarkItMax(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Max(ints)
}
})
}
}
func BenchmarkItMinBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.MinBy(ints, func(a, b int) bool { return a < b })
}
})
}
}
func BenchmarkItMaxBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.MaxBy(ints, func(a, b int) bool { return a > b })
}
})
}
}
func BenchmarkItFirst(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.First(ints)
}
})
}
}
func BenchmarkItLast(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Last(ints)
}
})
}
}
func BenchmarkItNth(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_, _ = it.Nth(ints, n/2)
}
})
}
}
func BenchmarkItSample(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Sample(ints)
}
})
}
}
func BenchmarkItSamples(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Samples(ints, 5) { //nolint:revive
}
}
})
}
}
func BenchmarkItSum(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Sum(ints)
}
})
}
}
func BenchmarkItSumBy(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.SumBy(ints, func(x int) int { return x })
}
})
}
}
func BenchmarkItProduct(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Product(ints)
}
})
}
}
func BenchmarkItMean(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Mean(ints)
}
})
}
}
func BenchmarkItMode(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Mode(ints)
}
})
}
}
func BenchmarkItRange(b *testing.B) {
for _, n := range itLengths {
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.Range(n) { //nolint:revive
}
}
})
}
}
func BenchmarkItLength(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Length(ints)
}
})
}
}
func BenchmarkItKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Keys(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItUniqKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.UniqKeys(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Values(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItUniqValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.UniqValues(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItEntries(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Entries(m) { //nolint:revive
}
}
})
}
}
func BenchmarkItFromEntries(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
entries := it.Entries(m)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
_ = it.FromEntries(entries)
}
})
}
}
func BenchmarkItInvert(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
entries := it.Entries(m)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.Invert(entries) { //nolint:revive
}
}
})
}
}
func BenchmarkItAssign(b *testing.B) {
for _, n := range itLengths {
seq := genMapSeq(n)
b.Run(fmt.Sprintf("maps_%d", n), func(b *testing.B) {
for range b.N {
_ = it.Assign(seq)
}
})
}
}
func BenchmarkItFilterKeys(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.FilterKeys(m, func(_ string, v int) bool { return v%2 == 0 }) { //nolint:revive
}
}
})
}
}
func BenchmarkItFilterValues(b *testing.B) {
for _, n := range itLengths {
m := genMapStringInt(n)
b.Run(fmt.Sprintf("map_%d", n), func(b *testing.B) {
for range b.N {
for range it.FilterValues(m, func(_ string, v int) bool { return v%2 == 0 }) { //nolint:revive
}
}
})
}
}
func BenchmarkItToSeqPtr(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.ToSeqPtr(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItFromSeqPtr(b *testing.B) {
for _, n := range itLengths {
ptrs := genIntPtrSeq(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FromSeqPtr(ptrs) { //nolint:revive
}
}
})
}
}
func BenchmarkItToAnySeq(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.ToAnySeq(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItChunkString(b *testing.B) {
for _, n := range itLengths {
s := ""
var sSb1288 strings.Builder
for range n {
sSb1288.WriteString("a")
}
s += sSb1288.String()
b.Run(fmt.Sprintf("len_%d", n), func(b *testing.B) {
for range b.N {
for range it.ChunkString(s, 5) { //nolint:revive
}
}
})
}
}
@@ -0,0 +1,46 @@
//go:build go1.23
package benchmark
import (
"fmt"
"testing"
"github.com/samber/lo/it"
)
func BenchmarkItToSeqPtr(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.ToSeqPtr(ints) { //nolint:revive
}
}
})
}
}
func BenchmarkItFromSeqPtr(b *testing.B) {
for _, n := range itLengths {
ptrs := genIntPtrSeq(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.FromSeqPtr(ptrs) { //nolint:revive
}
}
})
}
}
func BenchmarkItToAnySeq(b *testing.B) {
for _, n := range itLengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for range b.N {
for range it.ToAnySeq(ints) { //nolint:revive
}
}
})
}
}
-119
View File
@@ -1,119 +0,0 @@
package benchmark
import (
"math/rand"
"strconv"
"testing"
"time"
"github.com/samber/lo"
lop "github.com/samber/lo/parallel"
"github.com/thoas/go-funk"
)
func sliceGenerator(size uint) []int64 {
r := rand.New(rand.NewSource(time.Now().Unix()))
result := make([]int64, size)
for i := uint(0); i < size; i++ {
result[i] = r.Int63()
}
return result
}
func mapGenerator(size uint) map[int64]int64 {
r := rand.New(rand.NewSource(time.Now().Unix()))
result := make(map[int64]int64, size)
for i := uint(0); i < size; i++ {
result[int64(i)] = r.Int63()
}
return result
}
func BenchmarkMap(b *testing.B) {
arr := sliceGenerator(1000000)
b.Run("lo.Map", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.Map(arr, func(x int64, i int) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("lop.Map", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lop.Map(arr, func(x int64, i int) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("reflect", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = funk.Map(arr, func(x int64) string {
return strconv.FormatInt(x, 10)
})
}
})
b.Run("for", func(b *testing.B) {
for n := 0; n < b.N; n++ {
results := make([]string, len(arr))
for i, item := range arr {
result := strconv.FormatInt(item, 10)
results[i] = result
}
}
})
}
func BenchmarkUniqKeys(b *testing.B) {
m := []map[int64]int64{
mapGenerator(1000),
mapGenerator(1000),
mapGenerator(1000),
}
b.Run("lo.UniqKeys", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.UniqKeys(m...)
}
})
}
func BenchmarkUniqValues(b *testing.B) {
m := []map[int64]int64{
mapGenerator(1000),
mapGenerator(1000),
mapGenerator(1000),
}
b.Run("lo.UniqValues", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.UniqValues(m...)
}
})
}
func BenchmarkFilterKeys(b *testing.B) {
m := mapGenerator(1000)
b.Run("lo.FilterKeys", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.FilterKeys(m, func(k, v int64) bool { return k%2 == 0 })
}
})
}
func BenchmarkFilterValues(b *testing.B) {
m := mapGenerator(1000)
b.Run("lo.FilterValues", func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = lo.FilterValues(m, func(k, v int64) bool { return v%2 == 0 })
}
})
}
+4
View File
@@ -21,6 +21,8 @@ func TernaryF[T any](condition bool, ifFunc, elseFunc func() T) T {
return elseFunc() return elseFunc()
} }
// Perf: value receivers (not pointer) allow the compiler to fully inline the entire
// If().ElseIf().Else() chain, eliminating all function call overhead.
type ifElse[T any] struct { type ifElse[T any] struct {
result T result T
done bool done bool
@@ -90,6 +92,8 @@ func (i ifElse[T]) ElseF(resultF func() T) T {
return resultF() return resultF()
} }
// Perf: value receivers (not pointer) allow the compiler to fully inline the entire
// Switch().Case().Default() chain, eliminating all function call overhead.
type switchCase[T comparable, R any] struct { type switchCase[T comparable, R any] struct {
predicate T predicate T
result R result R
+2
View File
@@ -105,6 +105,8 @@ func Zip2[A, B any](a []A, b []B) []Tuple2[A, B] {
result := make([]Tuple2[A, B], size) result := make([]Tuple2[A, B], size)
// Perf: separate loops per input slice improve CPU cache locality (each loop reads
// one contiguous memory region) and enable bounds-check elimination by the compiler.
for i := range a { for i := range a {
result[i].A = a[i] result[i].A = a[i]
} }