aboutsummaryrefslogtreecommitdiff
path: root/src/strings
diff options
context:
space:
mode:
authortdakkota <tanc13ya.ru@gmail.com>2025-05-03 19:45:02 +0300
committerGopher Robot <gobot@golang.org>2025-05-05 19:08:23 -0700
commit35b4fd9f373cbe13778eb259a19c496c9c613a1f (patch)
tree0c0c2e82e0c8da8b49dee6dd04e32d07120c9e23 /src/strings
parentf0d736ded081b33b92524050b451a820f67c2a16 (diff)
downloadgo-35b4fd9f373cbe13778eb259a19c496c9c613a1f.tar.xz
bytes, strings: reduce Split{,After}Seq heap allocations
This CL slightly changes flow of splitSeq to help compiler to inline the iterator closure. goos: linux goarch: amd64 pkg: strings cpu: AMD Ryzen 9 5950X 16-Core Processor │ sec/op │ sec/op vs base │ SplitSeqEmptySeparator-32 3.590m ± 0% 3.430m ± 2% -4.46% (p=0.000 n=30) SplitSeqSingleByteSeparator-32 647.0µ ± 0% 656.1µ ± 0% +1.41% (p=0.000 n=30) SplitSeqMultiByteSeparator-32 423.9µ ± 1% 384.5µ ± 0% -9.31% (p=0.000 n=30) SplitAfterSeqEmptySeparator-32 3.372m ± 4% 3.514m ± 0% +4.20% (p=0.000 n=30) SplitAfterSeqSingleByteSeparator-32 648.5µ ± 2% 537.6µ ± 0% -17.10% (p=0.000 n=30) SplitAfterSeqMultiByteSeparator-32 423.3µ ± 2% 364.4µ ± 2% -13.91% (p=0.000 n=30) geomean 984.7µ 917.3µ -6.85% │ B/op │ B/op vs base │ SplitSeqEmptySeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) SplitSeqSingleByteSeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) SplitSeqMultiByteSeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) SplitAfterSeqEmptySeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) SplitAfterSeqSingleByteSeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) SplitAfterSeqMultiByteSeparator-32 24.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=30) geomean 24.00 ? For #73524 Change-Id: Ic83c5751a41c65030356a208e4ad1f500723e695 Reviewed-on: https://go-review.googlesource.com/c/go/+/669735 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: qiu laidongfeng2 <2645477756@qq.com> Commit-Queue: Alan Donovan <adonovan@google.com>
Diffstat (limited to 'src/strings')
-rw-r--r--src/strings/iter.go21
-rw-r--r--src/strings/iter_test.go52
2 files changed, 62 insertions, 11 deletions
diff --git a/src/strings/iter.go b/src/strings/iter.go
index 3fd2c9da97..a42e78ee09 100644
--- a/src/strings/iter.go
+++ b/src/strings/iter.go
@@ -32,25 +32,24 @@ func Lines(s string) iter.Seq[string] {
}
// explodeSeq returns an iterator over the runes in s.
-func explodeSeq(s string) iter.Seq[string] {
- return func(yield func(string) bool) {
- for len(s) > 0 {
- _, size := utf8.DecodeRuneInString(s)
- if !yield(s[:size]) {
- return
- }
- s = s[size:]
+func explodeSeq(s string, yield func(string) bool) {
+ for len(s) > 0 {
+ _, size := utf8.DecodeRuneInString(s)
+ if !yield(s[:size]) {
+ return
}
+ s = s[size:]
}
}
// splitSeq is SplitSeq or SplitAfterSeq, configured by how many
// bytes of sep to include in the results (none or all).
func splitSeq(s, sep string, sepSave int) iter.Seq[string] {
- if len(sep) == 0 {
- return explodeSeq(s)
- }
return func(yield func(string) bool) {
+ if len(sep) == 0 {
+ explodeSeq(s, yield)
+ return
+ }
for {
i := Index(s, sep)
if i < 0 {
diff --git a/src/strings/iter_test.go b/src/strings/iter_test.go
new file mode 100644
index 0000000000..2db599377f
--- /dev/null
+++ b/src/strings/iter_test.go
@@ -0,0 +1,52 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strings_test
+
+import (
+ . "strings"
+ "testing"
+)
+
+func BenchmarkSplitSeqEmptySeparator(b *testing.B) {
+ for range b.N {
+ for range SplitSeq(benchInputHard, "") {
+ }
+ }
+}
+
+func BenchmarkSplitSeqSingleByteSeparator(b *testing.B) {
+ for range b.N {
+ for range SplitSeq(benchInputHard, "/") {
+ }
+ }
+}
+
+func BenchmarkSplitSeqMultiByteSeparator(b *testing.B) {
+ for range b.N {
+ for range SplitSeq(benchInputHard, "hello") {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqEmptySeparator(b *testing.B) {
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, "") {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqSingleByteSeparator(b *testing.B) {
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, "/") {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqMultiByteSeparator(b *testing.B) {
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, "hello") {
+ }
+ }
+}