Skip to content

Commit

Permalink
apd: Improve SetFloat64 efficiency
Browse files Browse the repository at this point in the history
Prefer to use append and allocate scratch space to improve
allocation efficiency of SetFloat64.

```
name         old time/op    new time/op    delta
SetFloat-10     261ns ± 0%     243ns ± 1%   -6.66%  (p=0.000 n=9+9)

name         old alloc/op   new alloc/op   delta
SetFloat-10     91.0B ± 0%     67.0B ± 0%  -26.37%  (p=0.000
n=10+10)

name         old allocs/op  new allocs/op  delta
SetFloat-10      4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000
n=10+10)
```
  • Loading branch information
Yevgeniy Miretskiy committed Jul 14, 2023
1 parent 4c2545f commit 83e12c8
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 36 deletions.
20 changes: 20 additions & 0 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,23 @@ func BenchmarkDecimalString(b *testing.B) {
_ = corpus[rng.Intn(len(corpus))].String()
}
}

func BenchmarkDecimalSetFloat(b *testing.B) {
rng := rand.New(rand.NewSource(461210934723948))
corpus := func() []float64 {
res := make([]float64, 8192)
for i := range res {
res[i] = rng.ExpFloat64()
}
return res
}()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var d Decimal
_, err := d.SetFloat64(corpus[rng.Intn(len(corpus))])
if err != nil {
b.Fatal(err)
}
}
}
70 changes: 34 additions & 36 deletions decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

// Decimal is an arbitrary-precision decimal. Its value is:
//
// Negative × Coeff × 10**Exponent
// Negative × Coeff × 10**Exponent
//
// Coeff must be positive. If it is negative results may be incorrect and
// apd may panic.
Expand Down Expand Up @@ -198,6 +198,7 @@ func (c *Context) SetString(d *Decimal, s string) (*Decimal, Condition, error) {
}

// Set sets d's fields to the values of x and returns d.
//
//gcassert:inline
func (d *Decimal) Set(x *Decimal) *Decimal {
if d == x {
Expand Down Expand Up @@ -241,7 +242,8 @@ func (d *Decimal) setCoefficient(x int64) {
// SetFloat64 sets d's Coefficient and Exponent to x and returns d. d will
// hold the exact value of f.
func (d *Decimal) SetFloat64(f float64) (*Decimal, error) {
_, _, err := d.SetString(strconv.FormatFloat(f, 'E', -1, 64))
var buf [32]byte // Avoid most of the allocations in strconv.
_, _, err := d.SetString(string(strconv.AppendFloat(buf[:0], f, 'E', -1, 64)))
return d, err
}

Expand Down Expand Up @@ -425,22 +427,21 @@ func (d *Decimal) setBig(b *BigInt) *BigInt {
// For example, the following values are ordered from lowest to highest. Note
// the difference in ordering between 1.2300 and 1.23.
//
// -NaN
// -NaNSignaling
// -Infinity
// -127
// -1.00
// -1
// -0.000
// -0
// 0
// 1.2300
// 1.23
// 1E+9
// Infinity
// NaNSignaling
// NaN
//
// -NaN
// -NaNSignaling
// -Infinity
// -127
// -1.00
// -1
// -0.000
// -0
// 0
// 1.2300
// 1.23
// 1E+9
// Infinity
// NaNSignaling
// NaN
func (d *Decimal) CmpTotal(x *Decimal) int {
do := d.cmpOrder()
xo := x.cmpOrder()
Expand Down Expand Up @@ -493,9 +494,9 @@ func (d *Decimal) cmpOrder() int {

// Cmp compares x and y and sets d to:
//
// -1 if x < y
// 0 if x == y
// +1 if x > y
// -1 if x < y
// 0 if x == y
// +1 if x > y
//
// This comparison respects the normal rules of special values (like NaN),
// and does not compare them.
Expand All @@ -510,11 +511,10 @@ func (c *Context) Cmp(d, x, y *Decimal) (Condition, error) {

// Cmp compares d and x and returns:
//
// -1 if d < x
// 0 if d == x
// +1 if d > x
// undefined if d or x are NaN
//
// -1 if d < x
// 0 if d == x
// +1 if d > x
// undefined if d or x are NaN
func (d *Decimal) Cmp(x *Decimal) int {
ds := d.Sign()
xs := x.Sign()
Expand Down Expand Up @@ -600,7 +600,6 @@ func (d *Decimal) Cmp(x *Decimal) int {
//
// -1 if d.Negative == true
// +1 if d.Negative == false
//
func (d *Decimal) Sign() int {
if d.Form == Finite && d.Coeff.Sign() == 0 {
return 0
Expand Down Expand Up @@ -803,15 +802,14 @@ func (d *Decimal) MarshalText() ([]byte, error) {
// NullDecimal represents a string that may be null. NullDecimal implements
// the database/sql.Scanner interface so it can be used as a scan destination:
//
// var d NullDecimal
// err := db.QueryRow("SELECT num FROM foo WHERE id=?", id).Scan(&d)
// ...
// if d.Valid {
// // use d.Decimal
// } else {
// // NULL value
// }
//
// var d NullDecimal
// err := db.QueryRow("SELECT num FROM foo WHERE id=?", id).Scan(&d)
// ...
// if d.Valid {
// // use d.Decimal
// } else {
// // NULL value
// }
type NullDecimal struct {
Decimal Decimal
Valid bool // Valid is true if Decimal is not NULL
Expand Down

0 comments on commit 83e12c8

Please sign in to comment.