diff options
| author | Jorropo <jorropo.pgm@gmail.com> | 2025-01-29 13:25:37 +0100 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-04-06 09:13:53 -0700 |
| commit | 0fa3564a74b802219d3df0852fbe921b3fe9c5be (patch) | |
| tree | b7f0f3a7e065c99a7adba3f37e39827847cd4941 | |
| parent | 3e5efa048ac647c7554eea3a4ed230f265841115 (diff) | |
| download | go-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.go | 23 | ||||
| -rw-r--r-- | src/math/big/intconv_test.go | 12 |
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 |
