diff options
author | 2024-11-11 15:45:19 +0000 | |
---|---|---|
committer | 2024-11-11 15:45:19 +0000 | |
commit | e3c2b790fd4329494979bd27be7fa162600f1436 (patch) | |
tree | 4f33353453cf45a670149e3d9f7dedc56ad79a88 /internal/util/slices.go | |
parent | [chore]: Bump golang.org/x/net from 0.30.0 to 0.31.0 (#3536) (diff) | |
download | gotosocial-e3c2b790fd4329494979bd27be7fa162600f1436.tar.xz |
[performance] minimise log field allocations (#3529)
* when appending log field only do so by minimal amount
* move slice utils to separate package to fix import cycle, add GrowJust() and AppendJust() functions
* fix GrowJust() not returning slice of same length
* improved xslices tests
* make AppendJust() test check for slice contents, fix AppendJust() final copying behaviour
* add a +1 with field growth to try minimise allocation for log 'msg' field
Diffstat (limited to 'internal/util/slices.go')
-rw-r--r-- | internal/util/slices.go | 182 |
1 files changed, 0 insertions, 182 deletions
diff --git a/internal/util/slices.go b/internal/util/slices.go deleted file mode 100644 index 955fe8830..000000000 --- a/internal/util/slices.go +++ /dev/null @@ -1,182 +0,0 @@ -// GoToSocial -// Copyright (C) GoToSocial Authors admin@gotosocial.org -// SPDX-License-Identifier: AGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -package util - -import ( - "slices" -) - -// Deduplicate deduplicates entries in the given slice. -func Deduplicate[T comparable](in []T) []T { - var ( - inL = len(in) - unique = make(map[T]struct{}, inL) - deduped = make([]T, 0, inL) - ) - - for _, v := range in { - if _, ok := unique[v]; ok { - // Already have this. - continue - } - - unique[v] = struct{}{} - deduped = append(deduped, v) - } - - return deduped -} - -// DeduplicateFunc deduplicates entries in the given -// slice, using the result of key() to gauge uniqueness. -func DeduplicateFunc[T any, C comparable](in []T, key func(v T) C) []T { - var ( - inL = len(in) - unique = make(map[C]struct{}, inL) - deduped = make([]T, 0, inL) - ) - - if key == nil { - panic("nil func") - } - - for _, v := range in { - k := key(v) - - if _, ok := unique[k]; ok { - // Already have this. - continue - } - - unique[k] = struct{}{} - deduped = append(deduped, v) - } - - return deduped -} - -// Gather will collect the values of type V from input type []T, -// passing each item to 'get' and appending V to the return slice. -func Gather[T, V any](out []V, in []T, get func(T) V) []V { - if get == nil { - panic("nil func") - } - - // Starting write index - // in the resliced / re - // alloc'd output slice. - start := len(out) - - // Total required slice len. - total := start + len(in) - - if total > cap(out) { - // Reallocate output with - // capacity for total len. - out2 := make([]V, len(out), total) - copy(out2, out) - out = out2 - } - - // Reslice with capacity - // up to total required. - out = out[:total] - - // Gather vs from 'in'. - for i, v := range in { - j := start + i - out[j] = get(v) - } - - return out -} - -// GatherIf is functionally similar to Gather(), but only when return bool is true. -// If you don't need to check the boolean, Gather() will be very slightly faster. -func GatherIf[T, V any](out []V, in []T, get func(T) (V, bool)) []V { - if get == nil { - panic("nil func") - } - - if cap(out)-len(out) < len(in) { - // Reallocate output with capacity for 'in'. - out2 := make([]V, len(out), cap(out)+len(in)) - copy(out2, out) - out = out2 - } - - // Gather vs from 'in'. - for _, v := range in { - if v, ok := get(v); ok { - out = append(out, v) - } - } - - return out -} - -// Collate will collect the values of type K from input type []T, -// passing each item to 'get' and deduplicating the end result. -// This is equivalent to calling Gather() followed by Deduplicate(). -func Collate[T any, K comparable](in []T, get func(T) K) []K { - if get == nil { - panic("nil func") - } - - ks := make([]K, 0, len(in)) - km := make(map[K]struct{}, len(in)) - - for i := 0; i < len(in); i++ { - // Get next k. - k := get(in[i]) - - if _, ok := km[k]; !ok { - // New value, add - // to map + slice. - ks = append(ks, k) - km[k] = struct{}{} - } - } - - return ks -} - -// OrderBy orders a slice of given type by the provided alternative slice of comparable type. -func OrderBy[T any, K comparable](in []T, keys []K, key func(T) K) { - if key == nil { - panic("nil func") - } - - // Create lookup of keys->idx. - m := make(map[K]int, len(in)) - for i, k := range keys { - m[k] = i - } - - // Sort according to the reverse lookup. - slices.SortFunc(in, func(a, b T) int { - ai := m[key(a)] - bi := m[key(b)] - if ai < bi { - return -1 - } else if bi < ai { - return +1 - } - return 0 - }) -} |