From e9e1ef47721e7dcfb24626b357f1e3a6848202bc Mon Sep 17 00:00:00 2001 From: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Date: Tue, 19 May 2020 16:29:21 +0300 Subject: [PATCH] [NOD-1006] Make use of a pool to avoid excessive allocation of big.Ints (#722) * [NOD-1006] Make CompactToBig take an out param so that we can reuse the same big.Int in averageTarget. * [NOD-1006] Fix merge errors. * [NOD-1006] Use CompactToBigWithDestination only in averageTarget. * [NOD-1006] Fix refactor errors. * [NOD-1006] Fix refactor errors. * [NOD-1006] Optimize averageTarget with a big.Int pool. * [NOD-1006] Defer releasing bigInts. * [NOD-1006] Use a pool for requiredDifficulty as well. * [NOD-1006] Move the big int pool to utils. * [NOD-1006] Remove unnecessary line. --- blockdag/blockwindow.go | 15 +++++++++++---- blockdag/difficulty.go | 19 ++++++++++++++----- util/bigintpool/pool.go | 25 +++++++++++++++++++++++++ util/math.go | 21 ++++++++++++++------- 4 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 util/bigintpool/pool.go diff --git a/blockdag/blockwindow.go b/blockdag/blockwindow.go index 5d657d719..4e2420003 100644 --- a/blockdag/blockwindow.go +++ b/blockdag/blockwindow.go @@ -2,6 +2,7 @@ package blockdag import ( "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/bigintpool" "github.com/pkg/errors" "math" "math/big" @@ -53,13 +54,19 @@ func (window blockWindow) minMaxTimestamps() (min, max int64) { return } -func (window blockWindow) averageTarget() *big.Int { - averageTarget := big.NewInt(0) +func (window blockWindow) averageTarget(averageTarget *big.Int) { + averageTarget.SetInt64(0) + + target := bigintpool.Acquire(0) + defer bigintpool.Release(target) for _, node := range window { - target := util.CompactToBig(node.bits) + util.CompactToBigWithDestination(node.bits, target) averageTarget.Add(averageTarget, target) } - return averageTarget.Div(averageTarget, big.NewInt(int64(len(window)))) + + windowLen := bigintpool.Acquire(int64(len(window))) + defer bigintpool.Release(windowLen) + averageTarget.Div(averageTarget, windowLen) } func (window blockWindow) medianTimestamp() (int64, error) { diff --git a/blockdag/difficulty.go b/blockdag/difficulty.go index aab65f055..3aebd9279 100644 --- a/blockdag/difficulty.go +++ b/blockdag/difficulty.go @@ -5,7 +5,7 @@ package blockdag import ( - "math/big" + "github.com/kaspanet/kaspad/util/bigintpool" "time" "github.com/kaspanet/kaspad/util" @@ -30,11 +30,20 @@ func (dag *BlockDAG) requiredDifficulty(bluestParent *blockNode, newBlockTime ti // averageWindowTarget * (windowMinTimestamp / (targetTimePerBlock * windowSize)) // The result uses integer division which means it will be slightly // rounded down. - newTarget := targetsWindow.averageTarget() + newTarget := bigintpool.Acquire(0) + defer bigintpool.Release(newTarget) + windowTimeStampDifference := bigintpool.Acquire(windowMaxTimeStamp - windowMinTimestamp) + defer bigintpool.Release(windowTimeStampDifference) + targetTimePerBlock := bigintpool.Acquire(dag.targetTimePerBlock) + defer bigintpool.Release(targetTimePerBlock) + difficultyAdjustmentWindowSize := bigintpool.Acquire(int64(dag.difficultyAdjustmentWindowSize)) + defer bigintpool.Release(difficultyAdjustmentWindowSize) + + targetsWindow.averageTarget(newTarget) newTarget. - Mul(newTarget, big.NewInt(windowMaxTimeStamp-windowMinTimestamp)). - Div(newTarget, big.NewInt(dag.targetTimePerBlock)). - Div(newTarget, big.NewInt(int64(dag.difficultyAdjustmentWindowSize))) + Mul(newTarget, windowTimeStampDifference). + Div(newTarget, targetTimePerBlock). + Div(newTarget, difficultyAdjustmentWindowSize) if newTarget.Cmp(dag.dagParams.PowMax) > 0 { return dag.powMaxBits } diff --git a/util/bigintpool/pool.go b/util/bigintpool/pool.go new file mode 100644 index 000000000..d2eab80ff --- /dev/null +++ b/util/bigintpool/pool.go @@ -0,0 +1,25 @@ +package bigintpool + +import ( + "math/big" + "sync" +) + +var bigIntPool = sync.Pool{ + New: func() interface{} { + return big.NewInt(0) + }, +} + +// Acquire acquires a big.Int from the pool and +// initializes it to x. +func Acquire(x int64) *big.Int { + bigInt := bigIntPool.Get().(*big.Int) + bigInt.SetInt64(x) + return bigInt +} + +// Release returns the given big.Int to the pool. +func Release(toRelease *big.Int) { + bigIntPool.Put(toRelease) +} diff --git a/util/math.go b/util/math.go index c7a4e50b9..9b0bb9c55 100644 --- a/util/math.go +++ b/util/math.go @@ -53,6 +53,16 @@ func FastLog2Floor(n uint64) uint8 { // The formula to calculate N is: // N = (-1^sign) * mantissa * 256^(exponent-3) func CompactToBig(compact uint32) *big.Int { + destination := big.NewInt(0) + CompactToBigWithDestination(compact, destination) + return destination +} + +// CompactToBigWithDestination is a version of CompactToBig that +// takes a destination parameter. This is useful for saving memory, +// as then the destination big.Int can be reused. +// See CompactToBig for further details. +func CompactToBigWithDestination(compact uint32, destination *big.Int) { // Extract the mantissa, sign bit, and exponent. mantissa := compact & 0x007fffff isNegative := compact&0x00800000 != 0 @@ -63,21 +73,18 @@ func CompactToBig(compact uint32) *big.Int { // treat the exponent as the number of bytes and shift the mantissa // right or left accordingly. This is equivalent to: // N = mantissa * 256^(exponent-3) - var bn *big.Int if exponent <= 3 { mantissa >>= 8 * (3 - exponent) - bn = big.NewInt(int64(mantissa)) + destination.SetInt64(int64(mantissa)) } else { - bn = big.NewInt(int64(mantissa)) - bn.Lsh(bn, 8*(exponent-3)) + destination.SetInt64(int64(mantissa)) + destination.Lsh(destination, 8*(exponent-3)) } // Make it negative if the sign bit is set. if isNegative { - bn = bn.Neg(bn) + destination.Neg(destination) } - - return bn } // BigToCompact converts a whole number N to a compact representation using