mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-09 02:18:08 -06:00
[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
This commit is contained in:
parent
98eef328ea
commit
e3c2b790fd
29 changed files with 189 additions and 76 deletions
|
|
@ -15,12 +15,53 @@
|
|||
// 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
|
||||
package xslices
|
||||
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
// GrowJust increases slice capacity to guarantee
|
||||
// extra room 'size', where in the case that it does
|
||||
// need to allocate more it ONLY allocates 'size' extra.
|
||||
// This is different to typical slices.Grow behaviour,
|
||||
// which simply guarantees extra through append() which
|
||||
// may allocate more than necessary extra size.
|
||||
func GrowJust[T any](in []T, size int) []T {
|
||||
|
||||
if cap(in)-len(in) < size {
|
||||
// Reallocate enough for in + size.
|
||||
in2 := make([]T, len(in), len(in)+size)
|
||||
_ = copy(in2, in)
|
||||
in = in2
|
||||
}
|
||||
|
||||
return in
|
||||
}
|
||||
|
||||
// AppendJust appends extra elements to slice,
|
||||
// ONLY allocating at most len(extra) elements. This
|
||||
// is different to the typical append behaviour which
|
||||
// will append extra, in a manner to reduce the need
|
||||
// for new allocations on every call to append.
|
||||
func AppendJust[T any](in []T, extra ...T) []T {
|
||||
l := len(in)
|
||||
|
||||
if cap(in)-l < len(extra) {
|
||||
// Reallocate enough for + extra.
|
||||
in2 := make([]T, l+len(extra))
|
||||
_ = copy(in2, in)
|
||||
in = in2
|
||||
} else {
|
||||
// Reslice for + extra.
|
||||
in = in[:l+len(extra)]
|
||||
}
|
||||
|
||||
// Copy extra into slice.
|
||||
_ = copy(in[l:], extra)
|
||||
return in
|
||||
}
|
||||
|
||||
// Deduplicate deduplicates entries in the given slice.
|
||||
func Deduplicate[T comparable](in []T) []T {
|
||||
var (
|
||||
|
|
@ -15,22 +15,90 @@
|
|||
// 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_test
|
||||
package xslices_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
||||
)
|
||||
|
||||
var (
|
||||
testURLSlice = []*url.URL{}
|
||||
)
|
||||
func TestGrowJust(t *testing.T) {
|
||||
for _, l := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
for _, x := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
s := make([]int, l, l+x)
|
||||
for _, g := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
s2 := xslices.GrowJust(s, g)
|
||||
|
||||
// Slice length should not be different.
|
||||
assert.Equal(t, len(s), len(s2))
|
||||
|
||||
switch {
|
||||
// If slice already has capacity for
|
||||
// 'g' then it should not be changed.
|
||||
case cap(s) >= len(s)+g:
|
||||
assert.Equal(t, cap(s), cap(s2))
|
||||
|
||||
// Else, returned slice should only
|
||||
// have capacity for original length
|
||||
// plus extra elements, NOTHING MORE.
|
||||
default:
|
||||
assert.Equal(t, cap(s2), len(s)+g)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendJust(t *testing.T) {
|
||||
for _, l := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
for _, x := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
s := make([]int, l, l+x)
|
||||
|
||||
// Randomize slice.
|
||||
for i := range s {
|
||||
s[i] = rand.Int()
|
||||
}
|
||||
|
||||
for _, a := range []int{0, 2, 4, 8, 16, 32, 64} {
|
||||
toAppend := make([]int, a)
|
||||
|
||||
// Randomize appended vals.
|
||||
for i := range toAppend {
|
||||
toAppend[i] = rand.Int()
|
||||
}
|
||||
|
||||
s2 := xslices.AppendJust(s, toAppend...)
|
||||
|
||||
// Slice length should be as expected.
|
||||
assert.Equal(t, len(s)+a, len(s2))
|
||||
|
||||
// Slice contents should be as expected.
|
||||
assert.Equal(t, append(s, toAppend...), s2)
|
||||
|
||||
switch {
|
||||
// If slice already has capacity for
|
||||
// 'toAppend' then it should not change.
|
||||
case cap(s) >= len(s)+a:
|
||||
assert.Equal(t, cap(s), cap(s2))
|
||||
|
||||
// Else, returned slice should only
|
||||
// have capacity for original length
|
||||
// plus extra elements, NOTHING MORE.
|
||||
default:
|
||||
assert.Equal(t, len(s)+a, cap(s2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGather(t *testing.T) {
|
||||
out := util.Gather(nil, []*url.URL{
|
||||
out := xslices.Gather(nil, []*url.URL{
|
||||
{Scheme: "https", Host: "google.com", Path: "/some-search"},
|
||||
{Scheme: "http", Host: "example.com", Path: "/robots.txt"},
|
||||
}, (*url.URL).String)
|
||||
|
|
@ -41,7 +109,7 @@ func TestGather(t *testing.T) {
|
|||
t.Fatal("unexpected gather output")
|
||||
}
|
||||
|
||||
out = util.Gather([]string{
|
||||
out = xslices.Gather([]string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
}, []*url.URL{
|
||||
|
|
@ -59,7 +127,7 @@ func TestGather(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGatherIf(t *testing.T) {
|
||||
out := util.GatherIf(nil, []string{
|
||||
out := xslices.GatherIf(nil, []string{
|
||||
"hello world",
|
||||
"not hello world",
|
||||
"hello world",
|
||||
|
|
@ -73,7 +141,7 @@ func TestGatherIf(t *testing.T) {
|
|||
t.Fatal("unexpected gatherif output")
|
||||
}
|
||||
|
||||
out = util.GatherIf([]string{
|
||||
out = xslices.GatherIf([]string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
}, []string{
|
||||
Loading…
Add table
Add a link
Reference in a new issue