Skip to content

Commit

Permalink
perf: Use SDK math v1.3.0 to speedup BigDec casts to/from Dec (backport
Browse files Browse the repository at this point in the history
#7577) (#7594)

* perf: Use SDK math v1.3.0 to speedup BigDec casts to/from Dec (#7577)

* Using sdk math v1.3.0, create BigDecFromDecMut, and use that wherever applicable

* tryfix build

* One more speedup

* Add CeilMut

* Use CeilMut

(cherry picked from commit ef33c2c)

# Conflicts:
#	go.mod
#	go.sum

* tidy

---------

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Adam Tucker <adamleetucker@outlook.com>
  • Loading branch information
3 people authored Feb 22, 2024
1 parent 79a33d1 commit 17d482c
Show file tree
Hide file tree
Showing 17 changed files with 85 additions and 187 deletions.
18 changes: 8 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/CosmWasm/wasmd v0.45.1-0.20231128163306-4b9b61faeaa3
github.com/CosmWasm/wasmvm v1.5.2
github.com/cometbft/cometbft v0.37.4
github.com/cometbft/cometbft-db v0.8.0
github.com/cometbft/cometbft-db v0.10.0
github.com/cosmos/cosmos-proto v1.0.0-beta.3
github.com/cosmos/cosmos-sdk v0.47.8
github.com/cosmos/go-bip39 v1.0.0
Expand All @@ -27,9 +27,9 @@ require (
github.com/mattn/go-sqlite3 v1.14.17
github.com/ory/dockertest/v3 v3.10.0
github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3
github.com/osmosis-labs/osmosis/osmomath v0.0.9
github.com/osmosis-labs/osmosis/osmoutils v0.0.10-0.20240221055803-03fd233402eb
github.com/osmosis-labs/osmosis/x/epochs v0.0.5
github.com/osmosis-labs/osmosis/osmomath v0.0.9-0.20240222171503-685566578734
github.com/osmosis-labs/osmosis/osmoutils v0.0.9-0.20240222004208-b602d1901059
github.com/osmosis-labs/osmosis/x/epochs v0.0.4
github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.10
github.com/osmosis-labs/sqs/sqsdomain v0.0.0-20240208035010-35d5b08f4975
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -70,23 +70,21 @@ require (
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Djarvur/go-err113 v0.1.0 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect
github.com/alecthomas/go-check-sumtype v0.1.3 // indirect
github.com/alexkohler/nakedret/v2 v2.0.2 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/aws/aws-sdk-go v1.44.224 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/btcutil v1.1.3 // indirect
github.com/butuzov/mirror v1.1.0 // indirect
github.com/catenacyber/perfsprint v0.2.0 // indirect
github.com/ccojocar/zxcvbn-go v1.0.1 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/cockroachdb/errors v1.10.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677 // indirect
github.com/cockroachdb/pebble v1.0.0 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect
github.com/cosmos/cosmos-db v1.0.0 // indirect
Expand Down Expand Up @@ -124,7 +122,7 @@ require (
github.com/kkHAIKE/contextcheck v1.1.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/linxGnu/grocksdb v1.7.16 // indirect
github.com/linxGnu/grocksdb v1.8.11 // indirect
github.com/macabu/inamedparam v0.1.2 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/maratori/testableexamples v1.0.0 // indirect
Expand All @@ -135,7 +133,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nunnatsa/ginkgolinter v0.14.1 // indirect
github.com/osmosis-labs/osmosis/v22 v22.0.0-rc0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
Expand Down Expand Up @@ -362,7 +360,7 @@ require (
github.com/zimmski/osutil v0.0.0-20190128123334-0d0b3ca231ac // indirect
github.com/zondax/hid v0.9.2 // indirect
gitlab.com/bosi/decorder v0.4.1 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.etcd.io/bbolt v1.3.8 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect
Expand Down
161 changes: 20 additions & 141 deletions go.sum

Large diffs are not rendered by default.

50 changes: 33 additions & 17 deletions osmomath/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"testing"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -36,14 +37,15 @@ const (
)

var (
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(BigDecPrecision), nil)
squaredPrecisionReuse = new(big.Int).Mul(precisionReuse, precisionReuse)
precisionReuseSDK = new(big.Int).Exp(big.NewInt(10), big.NewInt(DecPrecision), nil)
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2))
precisionMultipliers []*big.Int
zeroInt = big.NewInt(0)
oneInt = big.NewInt(1)
tenInt = big.NewInt(10)
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(BigDecPrecision), nil)
squaredPrecisionReuse = new(big.Int).Mul(precisionReuse, precisionReuse)
precisionReuseSDK = new(big.Int).Exp(big.NewInt(10), big.NewInt(DecPrecision), nil)
bigDecDecPrecisionFactorDiff = new(big.Int).Exp(big.NewInt(10), big.NewInt(BigDecPrecision-DecPrecision), nil)
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2))
precisionMultipliers []*big.Int
zeroInt = big.NewInt(0)
oneInt = big.NewInt(1)
tenInt = big.NewInt(10)

// log_2(e)
// From: https://www.wolframalpha.com/input?i=log_2%28e%29+with+37+digits
Expand Down Expand Up @@ -131,6 +133,10 @@ func NewBigDecFromBigInt(i *big.Int) BigDec {
return NewBigDecFromBigIntWithPrec(i, 0)
}

func NewBigDecFromBigIntMut(i *big.Int) BigDec {
return NewBigDecFromBigIntMutWithPrec(i, 0)
}

// create a new BigDec from big integer assuming whole numbers
// CONTRACT: prec <= BigDecPrecision
func NewBigDecFromBigIntWithPrec(i *big.Int, prec int64) BigDec {
Expand Down Expand Up @@ -617,7 +623,10 @@ func (d BigDec) MustFloat64() float64 {
// Dec returns the osmomath.Dec representation of a BigDec.
// Values in any additional decimal places are truncated.
func (d BigDec) Dec() Dec {
return d.DecWithPrecision(DecPrecision)
dec := math.LegacyNewDec(0)
decBi := dec.BigIntMut()
decBi.Quo(d.i, bigDecDecPrecisionFactorDiff)
return dec
}

// DecWithPrecision converts BigDec to Dec with desired precision
Expand All @@ -636,7 +645,7 @@ func (d BigDec) DecWithPrecision(precision uint64) Dec {

// Truncate any additional decimal values that exist due to BigDec's additional precision
// This relies on big.Int's Quo function doing floor division
intRepresentation := new(big.Int).Quo(d.BigInt(), precisionFactor)
intRepresentation := new(big.Int).Quo(d.BigIntMut(), precisionFactor)

// convert int representation back to SDK Dec precision
truncatedDec := NewDecFromBigIntWithPrec(intRepresentation, int64(precision))
Expand Down Expand Up @@ -680,6 +689,12 @@ func BigDecFromDec(d Dec) BigDec {
return NewBigDecFromBigIntMutWithPrec(d.BigInt(), DecPrecision)
}

// BigDecFromDec returns the BigDec representation of an Dec.
// Values in any additional decimal places are truncated.
func BigDecFromDecMut(d Dec) BigDec {
return NewBigDecFromBigIntMutWithPrec(d.BigIntMut(), DecPrecision)
}

// BigDecFromSDKInt returns the BigDec representation of an sdkInt.
// Values in any additional decimal places are truncated.
func BigDecFromSDKInt(i Int) BigDec {
Expand Down Expand Up @@ -846,18 +861,19 @@ func (d BigDec) TruncateDec() BigDec {
// or equal to the given decimal.
func (d BigDec) Ceil() BigDec {
tmp := new(big.Int).Set(d.i)
return BigDec{i: tmp}.CeilMut()
}

quo, rem := tmp, big.NewInt(0)
quo, rem = quo.QuoRem(tmp, precisionReuse, rem)
func (d BigDec) CeilMut() BigDec {
quo, rem := d.i, big.NewInt(0)
quo, rem = quo.QuoRem(quo, precisionReuse, rem)

// no need to round with a zero remainder regardless of sign
if rem.Sign() == 0 {
return NewBigDecFromBigInt(quo)
} else if rem.Sign() == -1 {
return NewBigDecFromBigInt(quo)
if rem.Sign() <= 0 {
return NewBigDecFromBigIntMut(quo)
}

return NewBigDecFromBigInt(quo.Add(quo, oneInt))
return NewBigDecFromBigIntMut(quo.Add(quo, oneInt))
}

// MaxSortableDec is the largest Dec that can be passed into SortableDecBytes()
Expand Down
4 changes: 3 additions & 1 deletion osmomath/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,10 @@ func (s *decimalTestSuite) TestDecCeil() {
}

for i, tc := range testCases {
tc_input_copy := tc.input.Clone()
res := tc.input.Ceil()
s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input)
s.Require().Equal(tc.input, tc_input_copy, "unexpected mutation of input in test case %d, input: %v", i, tc.input)
s.Require().True(tc.expected.Equal(res), "unexpected result for test case %d, input: %v, got %v, expected %v:", i, tc.input, res, tc.expected)
}
}

Expand Down
2 changes: 1 addition & 1 deletion osmomath/rounding_direction.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func DivIntByU64ToBigDec(i Int, u uint64, round RoundingDirection) (BigDec, erro
if u == 0 {
return BigDec{}, errors.New("div by zero")
}
d := BigDecFromDec(i.ToLegacyDec())
d := BigDecFromDecMut(i.ToLegacyDec())
if round == RoundUp {
return d.QuoRoundUp(NewBigDec(int64(u))), nil
} else if round == RoundDown {
Expand Down
2 changes: 1 addition & 1 deletion osmomath/sqrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func MonotonicSqrtBigDec(d BigDec) (BigDec, error) {
if check.Cmp(shiftedD) == -1 {
r.Add(r, oneBigInt)
}
root := NewBigDecFromBigIntWithPrec(r, 36)
root := NewBigDecFromBigIntMutWithPrec(r, 36)

return root, nil
}
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (s *KeeperTestSuite) swap(pool types.ConcentratedPoolExtension, swapInFunde
RoundingDir: osmomath.RoundDown,
}

result := errTolerance.CompareBigDec(osmomath.BigDecFromDec(swapInFunded.Amount.ToLegacyDec()), osmomath.BigDecFromDec(amountInSwapResult.Amount.ToLegacyDec()))
result := errTolerance.CompareBigDec(osmomath.BigDecFromDecMut(swapInFunded.Amount.ToLegacyDec()), osmomath.BigDecFromDecMut(amountInSwapResult.Amount.ToLegacyDec()))

if result != 0 {
// Note: did some investigations into why this happens.
Expand Down
5 changes: 3 additions & 2 deletions x/concentrated-liquidity/lp.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,11 +568,12 @@ func (k Keeper) initializeInitialPositionForPool(ctx sdk.Context, pool types.Con
if err != nil {
return err
}
initialCurSqrtPriceBigDec := osmomath.BigDecFromDecMut(initialCurSqrtPrice)

// Calculate the initial tick from the initial spot price
// We round down here so that the tick is rounded to
// the nearest possible value given the tick spacing.
initialTick, err := math.SqrtPriceToTickRoundDownSpacing(osmomath.BigDecFromDec(initialCurSqrtPrice), pool.GetTickSpacing())
initialTick, err := math.SqrtPriceToTickRoundDownSpacing(initialCurSqrtPriceBigDec, pool.GetTickSpacing())
if err != nil {
return err
}
Expand All @@ -584,7 +585,7 @@ func (k Keeper) initializeInitialPositionForPool(ctx sdk.Context, pool types.Con
// However, there are ticks only at 100_000_000 X/Y and 100_000_100 X/Y.
// In such a case, we do not want to round the sqrt price to 100_000_000 X/Y, but rather
// let it float within the possible tick range.
pool.SetCurrentSqrtPrice(osmomath.BigDecFromDec(initialCurSqrtPrice))
pool.SetCurrentSqrtPrice(initialCurSqrtPriceBigDec)
pool.SetCurrentTick(initialTick)
err = k.setPool(ctx, pool)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/lp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1710,7 +1710,7 @@ func (s *KeeperTestSuite) TestInitializeInitialPositionForPool() {
sqrt := func(x int64) osmomath.BigDec {
sqrt, err := osmomath.MonotonicSqrt(osmomath.NewDec(x))
s.Require().NoError(err)
return osmomath.BigDecFromDec(sqrt)
return osmomath.BigDecFromDecMut(sqrt)
}

type sendTest struct {
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/math/math.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func CalcAmount0Delta(liq, sqrtPriceA, sqrtPriceB osmomath.BigDec, roundUp bool)
// Note that the order of divisions is important here. First, we divide by a larger number (sqrtPriceB) and then by a smaller number (sqrtPriceA).
// This leads to a smaller error amplification. This only matters in cases where at least one of the sqrt prices is below 1.
// TODO (perf): QuoRoundUpMut with no reallocation.
return liq.MulRoundUp(diff).QuoRoundUpMut(sqrtPriceB).QuoRoundUpMut(sqrtPriceA).Ceil()
return liq.MulRoundUp(diff).QuoRoundUpMut(sqrtPriceB).QuoRoundUpMut(sqrtPriceA).CeilMut()
}
// These are truncated at precision end to round in favor of the pool when:
// - calculating amount out during swap
Expand Down Expand Up @@ -114,7 +114,7 @@ func CalcAmount1Delta(liq, sqrtPriceA, sqrtPriceB osmomath.BigDec, roundUp bool)
// Examples include:
// - calculating amountIn during swap
// - adding liquidity (request user to provide more tokens in in favor of the pool)
return liq.Mul(diff).Ceil()
return liq.Mul(diff).CeilMut()
}
// This is truncated at precision end to round in favor of the pool when:
// - calculating amount out during swap
Expand Down
5 changes: 3 additions & 2 deletions x/concentrated-liquidity/math/tick.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TickToSqrtPrice(tickIndex int64) (osmomath.BigDec, error) {
if err != nil {
return osmomath.BigDec{}, err
}
return osmomath.BigDecFromDec(sqrtPrice), nil
return osmomath.BigDecFromDecMut(sqrtPrice), nil
}

// For the newly extended range of [tick(MinSpotPriceV2), MinInitializedTick), we use the new math
Expand Down Expand Up @@ -111,10 +111,11 @@ func TickToPrice(tickIndex int64) (osmomath.BigDec, error) {
// original math based on 18 precision decimal on the range of [MinInitializedTick, tick(MaxSpotPrice)]
// For the newly extended range of [MinInitializedTickV2, MinInitializedTick), we use the new math
// based on 36 precision decimal.
// TODO: Delete this code difference, it yields the exact same number every time.
if tickIndex < types.MinInitializedTick {
price = additiveSpacing.AddMut(powTenBigDec(geometricExponentDelta))
} else {
price = osmomath.BigDecFromDec(PowTenInternal(geometricExponentDelta).Add(additiveSpacing.Dec()))
price = osmomath.BigDecFromDecMut((additiveSpacing.Dec()).AddMut(PowTenInternal(geometricExponentDelta)))
}

// defense in depth, this logic would not be reached due to use having checked if given tick is in between
Expand Down
5 changes: 3 additions & 2 deletions x/concentrated-liquidity/model/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,14 @@ func (p Pool) SpotPrice(ctx sdk.Context, quoteAssetDenom string, baseAssetDenom
return osmomath.BigDec{}, fmt.Errorf("quote asset denom (%s) is not in pool with (%s, %s) pair", quoteAssetDenom, p.Token0, p.Token1)
}

priceSquared := p.CurrentSqrtPrice.PowerInteger(2)
// The reason why we convert the result to Dec and then back to BigDec is to temporarily
// maintain backwards compatibility with the original implementation.
// TODO: remove before https://github.com/osmosis-labs/osmosis/issues/5726 is complete
if baseAssetDenom == p.Token0 {
return osmomath.BigDecFromDec(p.CurrentSqrtPrice.PowerInteger(2).Dec()), nil
return osmomath.BigDecFromDecMut(priceSquared.Dec()), nil
}
return osmomath.BigDecFromDec(osmomath.OneBigDec().Quo(p.CurrentSqrtPrice.PowerInteger(2)).Dec()), nil
return osmomath.BigDecFromDecMut(osmomath.OneBigDec().Quo(priceSquared).Dec()), nil
}

// GetToken0 returns the token0 of the pool
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/model/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ var (
DefaultReverseSpotPrice = osmomath.NewDec(1).Quo(DefaultSpotPrice)
DefaultSqrtSpotPrice = func() osmomath.BigDec {
sqrtPrice, _ := osmomath.MonotonicSqrt(DefaultSpotPrice)
return osmomath.BigDecFromDec(sqrtPrice)
return osmomath.BigDecFromDecMut(sqrtPrice)
}()
DefaultLiquidityAmt = osmomath.MustNewDecFromStr("1517882343.751510418088349649")
DefaultCurrTick int64 = 310000
DefaultCurrPrice = osmomath.NewDec(5000)
DefaultCurrSqrtPrice = func() osmomath.BigDec {
sqrtPrice, _ := osmomath.MonotonicSqrt(DefaultCurrPrice)
return osmomath.BigDecFromDec(sqrtPrice)
return osmomath.BigDecFromDecMut(sqrtPrice)
}() // 70.710678118654752440

DefaultSpreadFactor = osmomath.MustNewDecFromStr("0.01")
Expand Down
2 changes: 1 addition & 1 deletion x/concentrated-liquidity/swapstrategy/swap_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func GetSqrtPriceLimit(priceLimit osmomath.BigDec, zeroForOne bool) (osmomath.Bi
if err != nil {
return osmomath.BigDec{}, err
}
return osmomath.BigDecFromDec(sqrtPriceLimit), nil
return osmomath.BigDecFromDecMut(sqrtPriceLimit), nil
}

// On the newly extended lower price range, utilize the 36 decimal
Expand Down
2 changes: 1 addition & 1 deletion x/gamm/pool-models/balancer/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ func (p Pool) SpotPrice(ctx sdk.Context, quoteAsset, baseAsset string) (spotPric
supplyRatio := quote.Token.Amount.ToLegacyDec().Quo(base.Token.Amount.ToLegacyDec())
spotPriceDec := supplyRatio.Mul(invWeightRatio)

return osmomath.BigDecFromDec(spotPriceDec), err
return osmomath.BigDecFromDecMut(spotPriceDec), err
}

// calcPoolOutGivenSingleIn - balance pAo.
Expand Down
2 changes: 1 addition & 1 deletion x/gamm/pool-models/stableswap/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (p Pool) spotPrice(quoteDenom, baseDenom string) (spotPrice osmomath.Dec, e
}

func oneMinus(spreadFactor osmomath.Dec) osmomath.BigDec {
return osmomath.BigDecFromDec(osmomath.OneDec().Sub(spreadFactor))
return osmomath.BigDecFromDecMut(osmomath.OneDec().SubMut(spreadFactor))
}

// calcOutAmtGivenIn calculate amount of specified denom to output from a pool in osmomath.Dec given the input `tokenIn`
Expand Down
2 changes: 1 addition & 1 deletion x/gamm/pool-models/stableswap/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (p Pool) SpotPrice(ctx sdk.Context, quoteAssetDenom string, baseAssetDenom
if err != nil {
return osmomath.BigDec{}, err
}
return osmomath.BigDecFromDec(spotPriceDec), nil
return osmomath.BigDecFromDecMut(spotPriceDec), nil
}

func (p Pool) Copy() Pool {
Expand Down

0 comments on commit 17d482c

Please sign in to comment.