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.
│ 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: I187065ea32394b241951928bada7a698a9f45cd9
Reviewed-on: https://go-review.googlesource.com/c/exp/+/486735
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
diff --git a/slices/slices.go b/slices/slices.go
index cff0cd4..2540bd6 100644
--- a/slices/slices.go
+++ b/slices/slices.go
@@ -104,8 +104,8 @@
// 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
}
}
@@ -115,8 +115,8 @@
// 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
}
}
@@ -207,12 +207,12 @@
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]
@@ -224,12 +224,12 @@
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/slices/slices_test.go b/slices/slices_test.go
index 1d9ffd2..6ecd822 100644
--- a/slices/slices_test.go
+++ b/slices/slices_test.go
@@ -126,6 +126,16 @@
}
}
+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 compareIntTests = []struct {
s1, s2 []int
want int
@@ -334,6 +344,15 @@
}
}
+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 {
@@ -350,6 +369,17 @@
}
}
+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) {
@@ -572,7 +602,15 @@
}
})
}
+}
+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) {
@@ -591,6 +629,15 @@
}
}
+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}