aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorropo <jorropo.pgm@gmail.com>2025-01-29 13:25:37 +0100
committerGopher Robot <gobot@golang.org>2026-04-06 09:13:53 -0700
commit0fa3564a74b802219d3df0852fbe921b3fe9c5be (patch)
treeb7f0f3a7e065c99a7adba3f37e39827847cd4941
parent3e5efa048ac647c7554eea3a4ed230f265841115 (diff)
downloadgo-0fa3564a74b802219d3df0852fbe921b3fe9c5be.tar.xz
math/big: optimize writeMultiple to use StringWriter and ByteWriter
This overhauls writeMultiple to reduce allocations: - return early if called with a count of 0 rather than allocating a buffer - if writing one byte, which happens when padding, try io.ByteWriter - try io.StringWriter - fallback to io.Writer as currently done Unlike what is suggested in #71465 I did not used io.WriteString to avoid a regression where we would allocate the byte slice once per iteration of count. goos: linux goarch: amd64 pkg: math/big cpu: AMD Ryzen 5 3600 6-Core Processor │ /tmp/old │ /tmp/new │ │ sec/op │ sec/op vs base │ Format-12 70.45µ ± 1% 63.55µ ± 2% -9.80% (p=0.000 n=10) │ /tmp/old │ /tmp/new │ │ B/op │ B/op vs base │ Format-12 14.73Ki ± 0% 10.96Ki ± 0% -25.58% (p=0.000 n=10) │ /tmp/old │ /tmp/new │ │ allocs/op │ allocs/op vs base │ Format-12 1098.0 ± 0% 649.0 ± 0% -40.89% (p=0.000 n=10) Fixes #71465 Change-Id: I44565c540b2d73c8737ac9733687141b645d9856 Reviewed-on: https://go-review.googlesource.com/c/go/+/645215 Reviewed-by: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Auto-Submit: Jorropo <jorropo.pgm@gmail.com>
-rw-r--r--src/math/big/intconv.go23
-rw-r--r--src/math/big/intconv_test.go12
2 files changed, 31 insertions, 4 deletions
diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go
index 51e75ffc52..7b92575a27 100644
--- a/src/math/big/intconv.go
+++ b/src/math/big/intconv.go
@@ -42,12 +42,27 @@ func (x *Int) String() string {
// write count copies of text to s.
func writeMultiple(s fmt.State, text string, count int) {
- if len(text) > 0 {
- b := []byte(text)
- for ; count > 0; count-- {
- s.Write(b)
+ if len(text) <= 0 || count <= 0 {
+ return
+ }
+ if len(text) == 1 {
+ if bw, ok := s.(io.ByteWriter); ok {
+ for range count {
+ bw.WriteByte(text[0])
+ }
+ return
}
}
+ if sw, ok := s.(io.StringWriter); ok {
+ for range count {
+ sw.WriteString(text)
+ }
+ return
+ }
+ b := []byte(text)
+ for range count {
+ s.Write(b)
+ }
}
var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter
diff --git a/src/math/big/intconv_test.go b/src/math/big/intconv_test.go
index cf337db63a..7b61be121c 100644
--- a/src/math/big/intconv_test.go
+++ b/src/math/big/intconv_test.go
@@ -374,6 +374,10 @@ var formatTests = []struct {
}
func TestFormat(t *testing.T) {
+ testFormat(t)
+}
+
+func testFormat(t testing.TB) {
for i, test := range formatTests {
var x *Int
if test.input != "<nil>" {
@@ -390,6 +394,14 @@ func TestFormat(t *testing.T) {
}
}
+func BenchmarkFormat(b *testing.B) {
+ // This benchmark is very rough but it's good enough to show
+ // https://go.dev/issue/71465 results.
+ for range b.N {
+ testFormat(b)
+ }
+}
+
type scanTest struct {
input string
format string