]> Cypherpunks repositories - gostls13.git/commitdiff
slices: optimize Index and Compact for large types
authorEgon Elbre <egonelbre@gmail.com>
Wed, 19 Apr 2023 08:43:51 +0000 (11:43 +0300)
committerGopher Robot <gobot@golang.org>
Wed, 19 Apr 2023 19:09:29 +0000 (19:09 +0000)
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 <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
src/slices/slices.go
src/slices/slices_test.go

index 4a35ec5c23f86eb1429499c9ca1add47c6328c67..dd414635ce2330f84e570973ee6391dadf4c8b0d 100644 (file)
@@ -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]
index 0f3df43e06150abc84e269792bfeb55a0530b007..4d893617f78636895833a06eb4912c4cb2568d6f 100644 (file)
@@ -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}