github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2010/io/balance.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build OMIT 6 7 package main 8 9 import ( 10 "container/heap" 11 "flag" 12 "fmt" 13 "math/rand" 14 "time" 15 ) 16 17 const nRequester = 100 18 const nWorker = 10 19 20 var roundRobin = flag.Bool("r", false, "use round-robin scheduling") 21 22 // Simulation of some work: just sleep for a while and report how long. 23 func op() int { 24 n := rand.Int63n(1e9) 25 time.Sleep(nWorker * n) 26 return int(n) 27 } 28 29 type Request struct { 30 fn func() int 31 c chan int 32 } 33 34 func requester(work chan Request) { 35 c := make(chan int) 36 for { 37 time.Sleep(rand.Int63n(nWorker * 2e9)) 38 work <- Request{op, c} 39 <-c 40 } 41 } 42 43 type Worker struct { 44 i int 45 requests chan Request 46 pending int 47 } 48 49 func (w *Worker) work(done chan *Worker) { 50 for { 51 req := <-w.requests 52 req.c <- req.fn() 53 done <- w 54 } 55 } 56 57 type Pool []*Worker 58 59 func (p Pool) Len() int { return len(p) } 60 61 func (p Pool) Less(i, j int) bool { 62 return p[i].pending < p[j].pending 63 } 64 65 func (p *Pool) Swap(i, j int) { 66 a := *p 67 a[i], a[j] = a[j], a[i] 68 a[i].i = i 69 a[j].i = j 70 } 71 72 func (p *Pool) Push(x interface{}) { 73 a := *p 74 n := len(a) 75 a = a[0 : n+1] 76 w := x.(*Worker) 77 a[n] = w 78 w.i = n 79 *p = a 80 } 81 82 func (p *Pool) Pop() interface{} { 83 a := *p 84 *p = a[0 : len(a)-1] 85 w := a[len(a)-1] 86 w.i = -1 // for safety 87 return w 88 } 89 90 type Balancer struct { 91 pool Pool 92 done chan *Worker 93 i int 94 } 95 96 func NewBalancer() *Balancer { 97 done := make(chan *Worker, nWorker) 98 b := &Balancer{make(Pool, 0, nWorker), done, 0} 99 for i := 0; i < nWorker; i++ { 100 w := &Worker{requests: make(chan Request, nRequester)} 101 heap.Push(&b.pool, w) 102 go w.work(b.done) 103 } 104 return b 105 } 106 107 func (b *Balancer) balance(work chan Request) { 108 for { 109 select { 110 case req := <-work: 111 b.dispatch(req) 112 case w := <-b.done: 113 b.completed(w) 114 } 115 b.print() 116 } 117 } 118 119 func (b *Balancer) print() { 120 sum := 0 121 sumsq := 0 122 for _, w := range b.pool { 123 fmt.Printf("%d ", w.pending) 124 sum += w.pending 125 sumsq += w.pending * w.pending 126 } 127 avg := float64(sum) / float64(len(b.pool)) 128 variance := float64(sumsq)/float64(len(b.pool)) - avg*avg 129 fmt.Printf(" %.2f %.2f\n", avg, variance) 130 } 131 132 func (b *Balancer) dispatch(req Request) { 133 if *roundRobin { 134 w := b.pool[b.i] 135 w.requests <- req 136 w.pending++ 137 b.i++ 138 if b.i >= len(b.pool) { 139 b.i = 0 140 } 141 return 142 } 143 144 w := heap.Pop(&b.pool).(*Worker) 145 w.requests <- req 146 w.pending++ 147 // fmt.Printf("started %p; now %d\n", w, w.pending) 148 heap.Push(&b.pool, w) 149 } 150 151 func (b *Balancer) completed(w *Worker) { 152 if *roundRobin { 153 w.pending-- 154 return 155 } 156 157 w.pending-- 158 // fmt.Printf("finished %p; now %d\n", w, w.pending) 159 heap.Remove(&b.pool, w.i) 160 heap.Push(&b.pool, w) 161 } 162 163 func main() { 164 flag.Parse() 165 work := make(chan Request) 166 for i := 0; i < nRequester; i++ { 167 go requester(work) 168 } 169 NewBalancer().balance(work) 170 }