Skip to content

Commit

Permalink
slices: optimize Index and Compact for large types
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
egonelbre authored and gopherbot committed Apr 19, 2023
1 parent 9cad0cc commit 9d53d7a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 14 deletions.
28 changes: 14 additions & 14 deletions src/slices/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand Down
47 changes: 47 additions & 0 deletions src/slices/slices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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}

Expand Down

0 comments on commit 9d53d7a

Please sign in to comment.