fix some algo edge cases
This commit is contained in:
parent
99e8b6f6b6
commit
9ee50d312b
92
adapative.go
Normal file
92
adapative.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package pool
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// arbitrary values to be most likely out of sync with anything else:
|
||||||
|
minNightshiftTime = 729 * time.Millisecond // ~1sec
|
||||||
|
maxNightshiftTime = 59049 * time.Second // ~2/3day
|
||||||
|
)
|
||||||
|
|
||||||
|
type adaptive struct {
|
||||||
|
activeTime time.Time
|
||||||
|
nsTO time.Duration
|
||||||
|
idle bool
|
||||||
|
average int
|
||||||
|
deviation int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAdaptiveAlgo() *adaptive {
|
||||||
|
return &adaptive{idle: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(v int) int {
|
||||||
|
if v >= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0 - v
|
||||||
|
}
|
||||||
|
|
||||||
|
func divE(v int) int {
|
||||||
|
return (3 * v) >> 3 // 1 / 2.72 => 3 / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func mulE(v int) int {
|
||||||
|
return (11 * v) >> 2 // 2.72 => 11 / 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func movingAverage(prev, currv int) int {
|
||||||
|
return prev + divE(currv-prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func movingAbsoluteDeviation(prev, currv, currav int) int {
|
||||||
|
return prev + divE(abs(currv-currav)-prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func targetCapacity(av, dev int) int {
|
||||||
|
return av + mulE(dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *adaptive) target(s Stats) int {
|
||||||
|
av := movingAverage(a.average, s.Active)
|
||||||
|
dev := movingAbsoluteDeviation(a.deviation, s.Active, av)
|
||||||
|
a.average = av
|
||||||
|
a.deviation = dev
|
||||||
|
return targetCapacity(av, dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *adaptive) nightshift(s Stats) time.Duration {
|
||||||
|
if a.idle && s.Active == 0 {
|
||||||
|
return a.nsTO
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.idle && s.Active > 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
a.idle = !a.idle
|
||||||
|
if !a.idle {
|
||||||
|
a.activeTime = now
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
a.nsTO = now.Sub(a.activeTime)
|
||||||
|
a.nsTO = (3 * a.nsTO) >> 3
|
||||||
|
if a.nsTO < minNightshiftTime {
|
||||||
|
a.nsTO = minNightshiftTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.nsTO > maxNightshiftTime {
|
||||||
|
a.nsTO = maxNightshiftTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.nsTO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *adaptive) Target(s Stats) (int, time.Duration) {
|
||||||
|
t := a.target(s)
|
||||||
|
ns := a.nightshift(s)
|
||||||
|
return t, ns
|
||||||
|
}
|
||||||
163
algo.go
163
algo.go
@ -1,163 +0,0 @@
|
|||||||
package pool
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// arbitrary values to be most likely out of sync with anything else:
|
|
||||||
minNightshiftTime = 729 * time.Millisecond // ~1sec
|
|
||||||
maxNightshiftTime = 59049 * time.Second // ~2/3day
|
|
||||||
)
|
|
||||||
|
|
||||||
type adaptive struct {
|
|
||||||
lastTurnedActive time.Time
|
|
||||||
nightshiftTime time.Duration
|
|
||||||
idle bool
|
|
||||||
average int
|
|
||||||
deviation int
|
|
||||||
}
|
|
||||||
|
|
||||||
type maxTimeout struct {
|
|
||||||
max int
|
|
||||||
to time.Duration
|
|
||||||
items []time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeAdaptiveAlgo() *adaptive {
|
|
||||||
return &adaptive{idle: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func abs(v int) int {
|
|
||||||
if v >= 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0 - v
|
|
||||||
}
|
|
||||||
|
|
||||||
func divE(v int) int {
|
|
||||||
return (3 * v) >> 3 // 1 / 2.72 => 3 / 8
|
|
||||||
}
|
|
||||||
|
|
||||||
func mulE(v int) int {
|
|
||||||
return (11 * v) >> 2 // 2.72 => 11 / 4
|
|
||||||
}
|
|
||||||
|
|
||||||
func movingAverage(prev, currv int) int {
|
|
||||||
return prev + divE(currv-prev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func movingAbsoluteDeviation(prev, currv, currav int) int {
|
|
||||||
return prev + divE(abs(currv-currav)-prev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func targetCapacity(av, dev int) int {
|
|
||||||
return av + mulE(dev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *adaptive) target(s Stats) int {
|
|
||||||
av := movingAverage(a.average, s.Active)
|
|
||||||
dev := movingAbsoluteDeviation(a.deviation, s.Active, av)
|
|
||||||
a.average = av
|
|
||||||
a.deviation = dev
|
|
||||||
return targetCapacity(av, dev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *adaptive) nightshift(s Stats) time.Duration {
|
|
||||||
if a.idle && s.Active == 0 {
|
|
||||||
return a.nightshiftTime
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.idle && s.Active > 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
a.idle = !a.idle
|
|
||||||
if !a.idle {
|
|
||||||
a.lastTurnedActive = now
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
a.nightshiftTime = now.Sub(a.lastTurnedActive)
|
|
||||||
a.nightshiftTime = (3 * a.nightshiftTime) >> 3
|
|
||||||
if a.nightshiftTime < minNightshiftTime {
|
|
||||||
a.nightshiftTime = minNightshiftTime
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.nightshiftTime > maxNightshiftTime {
|
|
||||||
a.nightshiftTime = maxNightshiftTime
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.nightshiftTime
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *adaptive) Target(s Stats) (int, time.Duration) {
|
|
||||||
t := a.target(s)
|
|
||||||
ns := a.nightshift(s)
|
|
||||||
return t, ns
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMaxTimeout(max int, to time.Duration) *maxTimeout {
|
|
||||||
return &maxTimeout{
|
|
||||||
max: max,
|
|
||||||
to: to,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *maxTimeout) Target(s Stats) (int, time.Duration) {
|
|
||||||
if a.max <= 0 && a.to <= 0 {
|
|
||||||
return s.Idle, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.max > 0 && a.to <= 0 {
|
|
||||||
t := s.Idle
|
|
||||||
if t > a.max {
|
|
||||||
t = a.max
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var zero time.Time
|
|
||||||
if len(a.items) > s.Idle {
|
|
||||||
for i := s.Idle; i < len(a.items); i++ {
|
|
||||||
a.items[i] = zero
|
|
||||||
}
|
|
||||||
|
|
||||||
a.items = a.items[:s.Idle]
|
|
||||||
if len(a.items) == 0 {
|
|
||||||
a.items = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
for len(a.items) < s.Idle {
|
|
||||||
a.items = append(a.items, now)
|
|
||||||
}
|
|
||||||
|
|
||||||
var drop int
|
|
||||||
for drop < len(a.items) && a.items[drop].Add(a.to).Before(now) {
|
|
||||||
a.items[drop] = zero
|
|
||||||
drop++
|
|
||||||
}
|
|
||||||
|
|
||||||
if drop > 0 {
|
|
||||||
for i := 0; i < drop; i++ {
|
|
||||||
a.items[i] = zero
|
|
||||||
}
|
|
||||||
|
|
||||||
a.items = a.items[drop:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.items) == 0 {
|
|
||||||
a.items = nil
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
t := len(a.items)
|
|
||||||
if a.max > 0 && t > a.max {
|
|
||||||
t = a.max
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, a.items[0].Add(a.to).Sub(now)
|
|
||||||
}
|
|
||||||
44
algo_test.go
44
algo_test.go
@ -1,44 +0,0 @@
|
|||||||
package pool_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"code.squareroundforest.org/arpio/pool"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type maxAlgo int
|
|
||||||
|
|
||||||
type timeoutAlgo struct {
|
|
||||||
to time.Duration
|
|
||||||
items []time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a maxAlgo) Target(s pool.Stats) (int, time.Duration) {
|
|
||||||
if s.Idle <= int(a) {
|
|
||||||
return s.Idle, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(a), 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *timeoutAlgo) Target(s pool.Stats) (int, time.Duration) {
|
|
||||||
now := time.Now()
|
|
||||||
for len(a.items) < s.Idle {
|
|
||||||
a.items = append(a.items, now)
|
|
||||||
}
|
|
||||||
|
|
||||||
t := s.Idle
|
|
||||||
for len(a.items) > 0 && a.items[0].Add(a.to).Before(now) {
|
|
||||||
t--
|
|
||||||
a.items = a.items[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if t < 0 {
|
|
||||||
t = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.items) == 0 {
|
|
||||||
return t, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, a.items[0].Add(a.to).Sub(now)
|
|
||||||
}
|
|
||||||
79
maxto.go
Normal file
79
maxto.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package pool
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type maxTimeout struct {
|
||||||
|
max int
|
||||||
|
to time.Duration
|
||||||
|
items []time.Time
|
||||||
|
lastPut int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMaxTimeout(max int, to time.Duration) *maxTimeout {
|
||||||
|
return &maxTimeout{
|
||||||
|
max: max,
|
||||||
|
to: to,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *maxTimeout) Target(s Stats) (int, time.Duration) {
|
||||||
|
if a.max <= 0 && a.to <= 0 {
|
||||||
|
return s.Idle, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.max > 0 && a.to <= 0 {
|
||||||
|
t := s.Idle
|
||||||
|
if t > a.max {
|
||||||
|
t = a.max
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var zero time.Time
|
||||||
|
if len(a.items) > s.Idle {
|
||||||
|
for i := s.Idle; i < len(a.items); i++ {
|
||||||
|
a.items[i] = zero
|
||||||
|
}
|
||||||
|
|
||||||
|
a.items = a.items[:s.Idle]
|
||||||
|
if len(a.items) == 0 {
|
||||||
|
a.items = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
for len(a.items) < s.Idle {
|
||||||
|
a.items = append(a.items, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
puts := s.Put - a.lastPut
|
||||||
|
if puts > 0 && len(a.items) >= puts {
|
||||||
|
a.lastPut = s.Put
|
||||||
|
for i := len(a.items) - puts; i < len(a.items); i++ {
|
||||||
|
a.items[i] = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop int
|
||||||
|
for drop < len(a.items) && a.items[drop].Add(a.to).Before(now) {
|
||||||
|
a.items[drop] = zero
|
||||||
|
drop++
|
||||||
|
}
|
||||||
|
|
||||||
|
if drop > 0 {
|
||||||
|
a.items = a.items[drop:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.items) == 0 {
|
||||||
|
a.items = nil
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
t := len(a.items)
|
||||||
|
if a.max > 0 && t > a.max {
|
||||||
|
t = a.max
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, a.items[0].Add(a.to).Sub(now)
|
||||||
|
}
|
||||||
1
pool.go
1
pool.go
@ -202,6 +202,7 @@ func (p pool[R]) freeIdle() {
|
|||||||
t, f := p.options.Algo.Target(s.stats)
|
t, f := p.options.Algo.Target(s.stats)
|
||||||
prev := s.stats.Free
|
prev := s.stats.Free
|
||||||
s = p.freeItems(s, t)
|
s = p.freeItems(s, t)
|
||||||
|
s.stats.Idle = len(s.items)
|
||||||
if s.stats.Free > prev {
|
if s.stats.Free > prev {
|
||||||
p.sendEvent(FreeOperation, s.stats)
|
p.sendEvent(FreeOperation, s.stats)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user