perf: optimize math.Int.Size for small values (#17497)

This commit is contained in:
Elias Naur 2023-08-22 15:54:34 -06:00 committed by GitHub
parent 780cad872b
commit 952328a4ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 5 deletions

View File

@ -34,6 +34,12 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j
# Changelog
## [Unreleased]
### Improvements
* [#17497](https://github.com/cosmos/cosmos-sdk/pull/17497) Optimize math.Int.Size for values that fit in 53 bits.
## [math/v1.1.2](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.1.2) - 2023-08-21
### Bug Fixes

View File

@ -22,3 +22,15 @@ func FuzzLegacyNewDecFromStr(f *testing.F) {
}
})
}
func FuzzSmallIntSize(f *testing.F) {
f.Add(int64(2<<53 - 1))
f.Add(-int64(2<<53 - 1))
f.Fuzz(func(t *testing.T, input int64) {
i := NewInt(input)
exp, _ := i.Marshal()
if i.Size() != len(exp) {
t.Fatalf("input %d: i.Size()=%d, len(input)=%d", input, i.Size(), len(exp))
}
})
}

View File

@ -4,6 +4,7 @@ import (
"encoding"
"encoding/json"
"fmt"
stdmath "math"
"math/big"
"strings"
"sync"
@ -429,6 +430,24 @@ func (i *Int) Unmarshal(data []byte) error {
// Size implements the gogo proto custom type interface.
func (i *Int) Size() int {
if i.i == nil {
return 1
}
// A float64 can store 52 bits exactly, which allows us to use
// math.Log10 to compute the size fast and garbage free.
if i.i.BitLen() <= 52 {
i64 := i.i.Int64()
if i64 == 0 {
return 1
}
size := 0
if i64 < 0 {
i64 = -i64
size++
}
return size + 1 + int(stdmath.Log10(float64(i64)))
}
// Slow path.
bz, _ := i.Marshal()
return len(bz)
}

View File

@ -592,14 +592,16 @@ func TestNewIntFromString(t *testing.T) {
}
func BenchmarkIntSize(b *testing.B) {
var tests []math.Int
for _, st := range sizeTests {
ii, _ := math.NewIntFromString(st.s)
tests = append(tests, ii)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, st := range sizeTests {
ii, _ := math.NewIntFromString(st.s)
for _, ii := range tests {
got := ii.Size()
if got != st.want {
b.Errorf("%q:: got=%d, want=%d", st.s, got, st.want)
}
sink = got
}
}