From: Egon Elbre Date: Wed, 19 Apr 2023 08:43:51 +0000 (+0300) Subject: slices: optimize Index and Compact for large types X-Git-Tag: go1.21rc1~844 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9d53d7aa02a8e5fca52fb638116592b7f0b04823;p=gostls13.git slices: optimize Index and Compact for large types Using `for i, v := range` loops causes extra copies. Try to get rid of as much copies as possible. goos: windows goarch: amd64 pkg: slices cpu: AMD Ryzen Threadripper 2950X 16-Core Processor │ old.txt~ │ new.txt~ │ │ sec/op │ sec/op vs base │ EqualFunc_Large-32 1.077m ± 1% 1.072m ± 1% ~ (p=0.631 n=10) Index_Large-32 346.329µ ± 1% 6.510µ ± 24% -98.12% (p=0.000 n=10) IndexFunc_Large-32 502.9µ ± 0% 381.2µ ± 1% -24.21% (p=0.000 n=10) Compact_Large-32 409.5µ ± 1% 145.2µ ± 9% -64.54% (p=0.000 n=10) CompactFunc_Large-32 693.5µ ± 1% 663.1µ ± 3% -4.39% (p=0.000 n=10) geomean 556.3µ 191.3µ -65.61% Change-Id: I36a2f2172cf30d97a5aa6f8d7cf6981d67daec62 Reviewed-on: https://go-review.googlesource.com/c/go/+/486235 Run-TryBot: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Bryan Mills --- diff --git a/src/slices/slices.go b/src/slices/slices.go index 4a35ec5c23..dd414635ce 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -43,8 +43,8 @@ func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool { // Index returns the index of the first occurrence of v in s, // or -1 if not present. func Index[E comparable](s []E, v E) int { - for i, vs := range s { - if v == vs { + for i := range s { + if v == s[i] { return i } } @@ -54,8 +54,8 @@ func Index[E comparable](s []E, v E) int { // IndexFunc returns the first index i satisfying f(s[i]), // or -1 if none do. func IndexFunc[E any](s []E, f func(E) bool) int { - for i, v := range s { - if f(v) { + for i := range s { + if f(s[i]) { return i } } @@ -178,12 +178,12 @@ func Compact[S ~[]E, E comparable](s S) S { return s } i := 1 - last := s[0] - for _, v := range s[1:] { - if v != last { - s[i] = v + for k := 1; k < len(s); k++ { + if s[k] != s[k-1] { + if i != k { + s[i] = s[k] + } i++ - last = v } } return s[:i] @@ -195,12 +195,12 @@ func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { return s } i := 1 - last := s[0] - for _, v := range s[1:] { - if !eq(v, last) { - s[i] = v + for k := 1; k < len(s); k++ { + if !eq(s[k], s[k-1]) { + if i != k { + s[i] = s[k] + } i++ - last = v } } return s[:i] diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go index 0f3df43e06..4d893617f7 100644 --- a/src/slices/slices_test.go +++ b/src/slices/slices_test.go @@ -124,6 +124,16 @@ func TestEqualFunc(t *testing.T) { } } +func BenchmarkEqualFunc_Large(b *testing.B) { + type Large [4 * 1024]byte + + xs := make([]Large, 1024) + ys := make([]Large, 1024) + for i := 0; i < b.N; i++ { + _ = EqualFunc(xs, ys, func(x, y Large) bool { return x == y }) + } +} + var indexTests = []struct { s []int v int @@ -170,6 +180,15 @@ func equalToIndex[T any](f func(T, T) bool, v1 T) func(T) bool { } } +func BenchmarkIndex_Large(b *testing.B) { + type Large [4 * 1024]byte + + ss := make([]Large, 1024) + for i := 0; i < b.N; i++ { + _ = Index(ss, Large{1}) + } +} + func TestIndexFunc(t *testing.T) { for _, test := range indexTests { if got := IndexFunc(test.s, equalToIndex(equal[int], test.v)); got != test.want { @@ -186,6 +205,17 @@ func TestIndexFunc(t *testing.T) { } } +func BenchmarkIndexFunc_Large(b *testing.B) { + type Large [4 * 1024]byte + + ss := make([]Large, 1024) + for i := 0; i < b.N; i++ { + _ = IndexFunc(ss, func(e Large) bool { + return e == Large{1} + }) + } +} + func TestContains(t *testing.T) { for _, test := range indexTests { if got := Contains(test.s, test.v); got != (test.want != -1) { @@ -468,7 +498,15 @@ func BenchmarkCompact(b *testing.B) { } }) } +} + +func BenchmarkCompact_Large(b *testing.B) { + type Large [4 * 1024]byte + ss := make([]Large, 1024) + for i := 0; i < b.N; i++ { + _ = Compact(ss) + } } func TestCompactFunc(t *testing.T) { @@ -487,6 +525,15 @@ func TestCompactFunc(t *testing.T) { } } +func BenchmarkCompactFunc_Large(b *testing.B) { + type Large [4 * 1024]byte + + ss := make([]Large, 1024) + for i := 0; i < b.N; i++ { + _ = CompactFunc(ss, func(a, b Large) bool { return a == b }) + } +} + func TestGrow(t *testing.T) { s1 := []int{1, 2, 3}