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) {
|
func BenchmarkValueOr(b *testing.B) {
|
||||||
m := genMap(100)
|
m := genMap(100)
|
||||||
b.Run("hit", func(b *testing.B) {
|
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
|
// 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) {
|
func BenchmarkUnion(b *testing.B) {
|
||||||
for _, n := range coreLengths {
|
for _, n := range coreLengths {
|
||||||
a := genSliceInt(n)
|
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) {
|
func BenchmarkToAnySlice(b *testing.B) {
|
||||||
for _, n := range coreLengths {
|
for _, n := range coreLengths {
|
||||||
ints := genSliceInt(n)
|
ints := genSliceInt(n)
|
||||||
|
|||||||
@@ -74,52 +74,46 @@ func BenchmarkMap(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: also apply to UniqValues.
|
|
||||||
func BenchmarkUniqKeys(b *testing.B) {
|
func BenchmarkUniqKeys(b *testing.B) {
|
||||||
m := []map[int64]int64{
|
m := []map[int64]int64{
|
||||||
mapGenerator(100000),
|
mapGenerator(1000),
|
||||||
mapGenerator(100000),
|
mapGenerator(1000),
|
||||||
mapGenerator(100000),
|
mapGenerator(1000),
|
||||||
}
|
}
|
||||||
|
b.Run("lo.UniqKeys", func(b *testing.B) {
|
||||||
// allocate just in time + ordered
|
|
||||||
b.Run("lo.UniqKeys.jit-alloc", func(b *testing.B) {
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
seen := make(map[int64]struct{})
|
_ = lo.UniqKeys(m...)
|
||||||
result := make([]int64, 0)
|
}
|
||||||
|
})
|
||||||
for i := range m {
|
}
|
||||||
for k := range m[i] {
|
|
||||||
if _, exists := seen[k]; exists {
|
func BenchmarkUniqValues(b *testing.B) {
|
||||||
continue
|
m := []map[int64]int64{
|
||||||
}
|
mapGenerator(1000),
|
||||||
seen[k] = struct{}{}
|
mapGenerator(1000),
|
||||||
result = append(result, k) //nolint:staticcheck
|
mapGenerator(1000),
|
||||||
}
|
}
|
||||||
}
|
b.Run("lo.UniqValues", func(b *testing.B) {
|
||||||
}
|
for n := 0; n < b.N; n++ {
|
||||||
})
|
_ = lo.UniqValues(m...)
|
||||||
|
}
|
||||||
// preallocate + unordered
|
})
|
||||||
b.Run("lo.UniqKeys.preallocate", func(b *testing.B) {
|
}
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
size := 0
|
func BenchmarkFilterKeys(b *testing.B) {
|
||||||
for i := range m {
|
m := mapGenerator(1000)
|
||||||
size += len(m[i])
|
b.Run("lo.FilterKeys", func(b *testing.B) {
|
||||||
}
|
for n := 0; n < b.N; n++ {
|
||||||
seen := make(map[int64]struct{}, size)
|
_ = lo.FilterKeys(m, func(k, v int64) bool { return k%2 == 0 })
|
||||||
|
}
|
||||||
for i := range m {
|
})
|
||||||
for k := range m[i] {
|
}
|
||||||
seen[k] = struct{}{}
|
|
||||||
}
|
func BenchmarkFilterValues(b *testing.B) {
|
||||||
}
|
m := mapGenerator(1000)
|
||||||
|
b.Run("lo.FilterValues", func(b *testing.B) {
|
||||||
result := make([]int64, 0, len(seen))
|
for n := 0; n < b.N; n++ {
|
||||||
|
_ = lo.FilterValues(m, func(k, v int64) bool { return v%2 == 0 })
|
||||||
for k := range seen {
|
|
||||||
result = append(result, k) //nolint:staticcheck
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func UniqKeys[K comparable, V any](in ...map[K]V) []K {
|
|||||||
}
|
}
|
||||||
|
|
||||||
seen := make(map[K]struct{}, size)
|
seen := make(map[K]struct{}, size)
|
||||||
result := make([]K, 0)
|
result := make([]K, 0, size)
|
||||||
|
|
||||||
for i := range in {
|
for i := range in {
|
||||||
for k := range in[i] {
|
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)
|
seen := make(map[V]struct{}, size)
|
||||||
result := make([]V, 0)
|
result := make([]V, 0, size)
|
||||||
|
|
||||||
for i := range in {
|
for i := range in {
|
||||||
for _, v := range in[i] {
|
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().
|
// It is a mix of lo.Filter() and lo.Keys().
|
||||||
// Play: https://go.dev/play/p/OFlKXlPrBAe
|
// 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 {
|
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 {
|
for k, v := range in {
|
||||||
if predicate(k, v) {
|
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().
|
// It is a mix of lo.Filter() and lo.Values().
|
||||||
// Play: https://go.dev/play/p/YVD5r_h-LX-
|
// 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 {
|
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 {
|
for k, v := range in {
|
||||||
if predicate(k, v) {
|
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.
|
// The order of the keys in the input map is not specified.
|
||||||
// Play: https://go.dev/play/p/j2gUQzCTu4t
|
// 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) {
|
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 {
|
for k, v := range in {
|
||||||
ok, err := predicate(k, v)
|
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.
|
// The order of the keys in the input map is not specified.
|
||||||
// Play: https://go.dev/play/p/hKvHlqLzbdE
|
// 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) {
|
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 {
|
for k, v := range in {
|
||||||
ok, err := predicate(k, v)
|
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.
|
// CountValues counts the number of each element in the collection.
|
||||||
// Play: https://go.dev/play/p/-p-PyLT4dfy
|
// Play: https://go.dev/play/p/-p-PyLT4dfy
|
||||||
func CountValues[T comparable](collection []T) map[T]int {
|
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 {
|
for i := range collection {
|
||||||
result[collection[i]]++
|
result[collection[i]]++
|
||||||
|
|||||||
Reference in New Issue
Block a user