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 }