[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.
This commit is contained in:
stasatdaglabs 2020-05-19 16:29:21 +03:00 committed by GitHub
parent eb8b841850
commit e9e1ef4772
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 16 deletions

View File

@ -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) {

View File

@ -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
}

25
util/bigintpool/pool.go Normal file
View File

@ -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)
}

View File

@ -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