mirror of
https://github.com/samber/lo.git
synced 2026-04-22 15:37:14 +08:00
perf: preallocate maps and slices in CountValues, UniqKeys, UniqValues, FilterKeys, FilterValues, FilterKeysErr, FilterValuesErr (#833)
* perf: preallocate maps and slices in CountValues, UniqKeys, UniqValues, FilterKeys, FilterValues, FilterKeysErr, FilterValuesErr Add size hints to map and slice allocations to avoid repeated grow-and-copy reallocations: - CountValues: make(map[T]int, len(collection)) - UniqKeys/UniqValues: make([]K, 0, size) - FilterKeys/FilterValues/FilterKeysErr/FilterValuesErr: make([]K, 0, len(in)) * oops
This commit is contained in:
@@ -67,18 +67,6 @@ func BenchmarkValues(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUniqValues(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
m1 := genMap(n)
|
||||
m2 := genMap(n)
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lo.UniqValues(m1, m2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValueOr(b *testing.B) {
|
||||
m := genMap(100)
|
||||
b.Run("hit", func(b *testing.B) {
|
||||
@@ -289,28 +277,6 @@ func BenchmarkFilterMapToSlice(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilterKeys(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
m := genMap(n)
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lo.FilterKeys(m, func(_ string, v int) bool { return v%2 == 0 })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilterValues(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
m := genMap(n)
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lo.FilterValues(m, func(_ string, v int) bool { return v%2 == 0 })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// find.go
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -738,18 +704,6 @@ func BenchmarkIntersectBy(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDifference(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
a := genSliceInt(n)
|
||||
c := genSliceInt(n)
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = lo.Difference(a, c)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnion(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
a := genSliceInt(n)
|
||||
@@ -1052,32 +1006,6 @@ func BenchmarkCoreToSlicePtr(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromSlicePtr(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
ptrs := lo.ToSlicePtr(genSliceInt(n))
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lo.FromSlicePtr(ptrs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromSlicePtrOr(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
ptrs := lo.ToSlicePtr(genSliceInt(n))
|
||||
// sprinkle nils
|
||||
for j := 0; j < n/10; j++ {
|
||||
ptrs[j*10] = nil
|
||||
}
|
||||
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = lo.FromSlicePtrOr(ptrs, -1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToAnySlice(b *testing.B) {
|
||||
for _, n := range coreLengths {
|
||||
ints := genSliceInt(n)
|
||||
|
||||
@@ -74,52 +74,46 @@ func BenchmarkMap(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// @TODO: also apply to UniqValues.
|
||||
func BenchmarkUniqKeys(b *testing.B) {
|
||||
m := []map[int64]int64{
|
||||
mapGenerator(100000),
|
||||
mapGenerator(100000),
|
||||
mapGenerator(100000),
|
||||
mapGenerator(1000),
|
||||
mapGenerator(1000),
|
||||
mapGenerator(1000),
|
||||
}
|
||||
|
||||
// allocate just in time + ordered
|
||||
b.Run("lo.UniqKeys.jit-alloc", func(b *testing.B) {
|
||||
b.Run("lo.UniqKeys", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
seen := make(map[int64]struct{})
|
||||
result := make([]int64, 0)
|
||||
|
||||
for i := range m {
|
||||
for k := range m[i] {
|
||||
if _, exists := seen[k]; exists {
|
||||
continue
|
||||
}
|
||||
seen[k] = struct{}{}
|
||||
result = append(result, k) //nolint:staticcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// preallocate + unordered
|
||||
b.Run("lo.UniqKeys.preallocate", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
size := 0
|
||||
for i := range m {
|
||||
size += len(m[i])
|
||||
}
|
||||
seen := make(map[int64]struct{}, size)
|
||||
|
||||
for i := range m {
|
||||
for k := range m[i] {
|
||||
seen[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]int64, 0, len(seen))
|
||||
|
||||
for k := range seen {
|
||||
result = append(result, k) //nolint:staticcheck
|
||||
}
|
||||
_ = 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 })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func UniqKeys[K comparable, V any](in ...map[K]V) []K {
|
||||
}
|
||||
|
||||
seen := make(map[K]struct{}, size)
|
||||
result := make([]K, 0)
|
||||
result := make([]K, 0, size)
|
||||
|
||||
for i := range in {
|
||||
for k := range in[i] {
|
||||
@@ -76,7 +76,7 @@ func UniqValues[K, V comparable](in ...map[K]V) []V {
|
||||
}
|
||||
|
||||
seen := make(map[V]struct{}, size)
|
||||
result := make([]V, 0)
|
||||
result := make([]V, 0, size)
|
||||
|
||||
for i := range in {
|
||||
for _, v := range in[i] {
|
||||
@@ -465,7 +465,7 @@ func FilterMapToSliceErr[K comparable, V, R any](in map[K]V, iteratee func(key K
|
||||
// It is a mix of lo.Filter() and lo.Keys().
|
||||
// Play: https://go.dev/play/p/OFlKXlPrBAe
|
||||
func FilterKeys[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) []K {
|
||||
result := make([]K, 0)
|
||||
result := make([]K, 0, len(in))
|
||||
|
||||
for k, v := range in {
|
||||
if predicate(k, v) {
|
||||
@@ -480,7 +480,7 @@ func FilterKeys[K comparable, V any](in map[K]V, predicate func(key K, value V)
|
||||
// It is a mix of lo.Filter() and lo.Values().
|
||||
// Play: https://go.dev/play/p/YVD5r_h-LX-
|
||||
func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) []V {
|
||||
result := make([]V, 0)
|
||||
result := make([]V, 0, len(in))
|
||||
|
||||
for k, v := range in {
|
||||
if predicate(k, v) {
|
||||
@@ -498,7 +498,7 @@ func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V
|
||||
// The order of the keys in the input map is not specified.
|
||||
// Play: https://go.dev/play/p/j2gUQzCTu4t
|
||||
func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]K, error) {
|
||||
result := make([]K, 0)
|
||||
result := make([]K, 0, len(in))
|
||||
|
||||
for k, v := range in {
|
||||
ok, err := predicate(k, v)
|
||||
@@ -520,7 +520,7 @@ func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value
|
||||
// The order of the keys in the input map is not specified.
|
||||
// Play: https://go.dev/play/p/hKvHlqLzbdE
|
||||
func FilterValuesErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]V, error) {
|
||||
result := make([]V, 0)
|
||||
result := make([]V, 0, len(in))
|
||||
|
||||
for k, v := range in {
|
||||
ok, err := predicate(k, v)
|
||||
|
||||
@@ -1003,7 +1003,7 @@ func CountByErr[T any](collection []T, predicate func(item T) (bool, error)) (in
|
||||
// CountValues counts the number of each element in the collection.
|
||||
// Play: https://go.dev/play/p/-p-PyLT4dfy
|
||||
func CountValues[T comparable](collection []T) map[T]int {
|
||||
result := make(map[T]int)
|
||||
result := make(map[T]int, len(collection))
|
||||
|
||||
for i := range collection {
|
||||
result[collection[i]]++
|
||||
|
||||
Reference in New Issue
Block a user