github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/executionEMA.go (about)

     1  package runner
     2  
     3  import (
     4  	"math"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // TimeEMA is used to store exponential moving averages for a time duration
    10  type TimeEMA struct {
    11  	avgs map[time.Duration]time.Duration
    12  	last time.Time
    13  	sync.Mutex
    14  }
    15  
    16  // NewTimeEMA creates a new exponential moving average of a time duration
    17  // for a set of time windows with an initial execution time duration set
    18  func NewTimeEMA(windows []time.Duration, initial time.Duration) (emas *TimeEMA) {
    19  	emas = &TimeEMA{
    20  		avgs: make(map[time.Duration]time.Duration, len(windows)),
    21  		last: time.Now(),
    22  	}
    23  	for _, window := range windows {
    24  		emas.avgs[window] = initial
    25  	}
    26  
    27  	return emas
    28  }
    29  
    30  // Update is used to update the moving average based on a new duration that
    31  // has been observed
    32  //
    33  func (avgs *TimeEMA) Update(value time.Duration) {
    34  	avgs.Lock()
    35  	defer avgs.Unlock()
    36  
    37  	fdtime := time.Now().Sub(avgs.last)
    38  	avgs.last = time.Now()
    39  
    40  	for ftime, oldValue := range avgs.avgs {
    41  		alpha := 1.0 - math.Exp(-fdtime.Seconds()/ftime.Seconds())
    42  		r := alpha*value.Seconds() + (1.0-alpha)*oldValue.Seconds()
    43  		avgs.avgs[ftime] = time.Duration(time.Duration(r) * time.Second)
    44  	}
    45  }
    46  
    47  // Keys can be used to retrieve a list of the moving average periods across which
    48  // the average is calculated
    49  func (avgs *TimeEMA) Keys() (keys []time.Duration) {
    50  	avgs.Lock()
    51  	defer avgs.Unlock()
    52  
    53  	keys = make([]time.Duration, 0, len(avgs.avgs))
    54  	for k := range avgs.avgs {
    55  		keys = append(keys, k)
    56  	}
    57  	return keys
    58  }
    59  
    60  // Get retrieves a single time duration moving average for a specified window of time
    61  func (avgs *TimeEMA) Get(window time.Duration) (avg time.Duration, wasPresent bool) {
    62  	avgs.Lock()
    63  	defer avgs.Unlock()
    64  
    65  	avg, wasPresent = avgs.avgs[window]
    66  	return avg, wasPresent
    67  }