github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2012/waza/balance.go (about)

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