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  }