Skip to content

Commit

Permalink
perf: optimize math.Int.Size for small values
Browse files Browse the repository at this point in the history
This is a simpler version of cosmos#16263 that only optimizes values that fit
in 53 bits. It is possible to optimize values that fit in 64 bits by
using big.Int.BitLen and math.Log2(10), but it doesn't seem worth the
complexity, especially given the revert of cosmos#16263.

I failed to beat big.Int.Marshal for values that don't fit in 64 bits.
  • Loading branch information
elias-orijtech committed Aug 21, 2023
1 parent 393dcc1 commit f33848c
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
12 changes: 12 additions & 0 deletions math/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
})
}
19 changes: 19 additions & 0 deletions math/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding"
"encoding/json"
"fmt"
stdmath "math"
"math/big"
"strings"
"sync"
Expand Down Expand Up @@ -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)
}
Expand Down
12 changes: 7 additions & 5 deletions math/int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down

0 comments on commit f33848c

Please sign in to comment.