github.com/MetalBlockchain/metalgo@v1.11.9/utils/math/meter/continuous_meter.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package meter
     5  
     6  import (
     7  	"math"
     8  	"time"
     9  )
    10  
    11  var (
    12  	convertEToBase2 = math.Log(2)
    13  
    14  	_ Factory = (*ContinuousFactory)(nil)
    15  	_ Meter   = (*continuousMeter)(nil)
    16  )
    17  
    18  // ContinuousFactory implements the Factory interface by returning a continuous
    19  // time meter.
    20  type ContinuousFactory struct{}
    21  
    22  func (ContinuousFactory) New(halflife time.Duration) Meter {
    23  	return NewMeter(halflife)
    24  }
    25  
    26  type continuousMeter struct {
    27  	halflife float64
    28  	value    float64
    29  
    30  	numCoresRunning float64
    31  	lastUpdated     time.Time
    32  }
    33  
    34  // NewMeter returns a new Meter with the provided halflife
    35  func NewMeter(halflife time.Duration) Meter {
    36  	return &continuousMeter{
    37  		halflife: float64(halflife) / convertEToBase2,
    38  	}
    39  }
    40  
    41  func (a *continuousMeter) Inc(now time.Time, numCores float64) {
    42  	a.Read(now)
    43  	a.numCoresRunning += numCores
    44  }
    45  
    46  func (a *continuousMeter) Dec(now time.Time, numCores float64) {
    47  	a.Read(now)
    48  	a.numCoresRunning -= numCores
    49  }
    50  
    51  func (a *continuousMeter) Read(now time.Time) float64 {
    52  	timeSincePreviousUpdate := a.lastUpdated.Sub(now)
    53  	if timeSincePreviousUpdate >= 0 {
    54  		return a.value
    55  	}
    56  	a.lastUpdated = now
    57  
    58  	factor := math.Exp(float64(timeSincePreviousUpdate) / a.halflife)
    59  	a.value *= factor
    60  	a.value += a.numCoresRunning * (1 - factor)
    61  	return a.value
    62  }
    63  
    64  func (a *continuousMeter) TimeUntil(now time.Time, value float64) time.Duration {
    65  	currentValue := a.Read(now)
    66  	if currentValue <= value {
    67  		return time.Duration(0)
    68  	}
    69  	// Note that [factor] >= 1
    70  	factor := currentValue / value
    71  	// Note that [numHalfLives] >= 0
    72  	numHalflives := math.Log(factor)
    73  	duration := numHalflives * a.halflife
    74  	// Overflow protection
    75  	if duration > math.MaxInt64 {
    76  		return time.Duration(math.MaxInt64)
    77  	}
    78  	return time.Duration(duration)
    79  }