mirror of
https://github.com/golang/go
synced 2024-11-13 17:50:23 -07:00
f9489bed72
R=rsc CC=golang-dev https://golang.org/cl/5338043
169 lines
2.8 KiB
Go
169 lines
2.8 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"container/heap"
|
|
"flag"
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
const nRequester = 100
|
|
const nWorker = 10
|
|
|
|
var roundRobin = flag.Bool("r", false, "use round-robin scheduling")
|
|
|
|
// Simulation of some work: just sleep for a while and report how long.
|
|
func op() int {
|
|
n := rand.Int63n(1e9)
|
|
time.Sleep(nWorker * n)
|
|
return int(n)
|
|
}
|
|
|
|
type Request struct {
|
|
fn func() int
|
|
c chan int
|
|
}
|
|
|
|
func requester(work chan Request) {
|
|
c := make(chan int)
|
|
for {
|
|
time.Sleep(rand.Int63n(nWorker * 2e9))
|
|
work <- Request{op, c}
|
|
<-c
|
|
}
|
|
}
|
|
|
|
type Worker struct {
|
|
i int
|
|
requests chan Request
|
|
pending int
|
|
}
|
|
|
|
func (w *Worker) work(done chan *Worker) {
|
|
for {
|
|
req := <-w.requests
|
|
req.c <- req.fn()
|
|
done <- w
|
|
}
|
|
}
|
|
|
|
type Pool []*Worker
|
|
|
|
func (p Pool) Len() int { return len(p) }
|
|
|
|
func (p Pool) Less(i, j int) bool {
|
|
return p[i].pending < p[j].pending
|
|
}
|
|
|
|
func (p *Pool) Swap(i, j int) {
|
|
a := *p
|
|
a[i], a[j] = a[j], a[i]
|
|
a[i].i = i
|
|
a[j].i = j
|
|
}
|
|
|
|
func (p *Pool) Push(x interface{}) {
|
|
a := *p
|
|
n := len(a)
|
|
a = a[0 : n+1]
|
|
w := x.(*Worker)
|
|
a[n] = w
|
|
w.i = n
|
|
*p = a
|
|
}
|
|
|
|
func (p *Pool) Pop() interface{} {
|
|
a := *p
|
|
*p = a[0 : len(a)-1]
|
|
w := a[len(a)-1]
|
|
w.i = -1 // for safety
|
|
return w
|
|
}
|
|
|
|
type Balancer struct {
|
|
pool Pool
|
|
done chan *Worker
|
|
i int
|
|
}
|
|
|
|
func NewBalancer() *Balancer {
|
|
done := make(chan *Worker, nWorker)
|
|
b := &Balancer{make(Pool, 0, nWorker), done, 0}
|
|
for i := 0; i < nWorker; i++ {
|
|
w := &Worker{requests: make(chan Request, nRequester)}
|
|
heap.Push(&b.pool, w)
|
|
go w.work(b.done)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Balancer) balance(work chan Request) {
|
|
for {
|
|
select {
|
|
case req := <-work:
|
|
b.dispatch(req)
|
|
case w := <-b.done:
|
|
b.completed(w)
|
|
}
|
|
b.print()
|
|
}
|
|
}
|
|
|
|
func (b *Balancer) print() {
|
|
sum := 0
|
|
sumsq := 0
|
|
for _, w := range b.pool {
|
|
fmt.Printf("%d ", w.pending)
|
|
sum += w.pending
|
|
sumsq += w.pending * w.pending
|
|
}
|
|
avg := float64(sum) / float64(len(b.pool))
|
|
variance := float64(sumsq)/float64(len(b.pool)) - avg*avg
|
|
fmt.Printf(" %.2f %.2f\n", avg, variance)
|
|
}
|
|
|
|
func (b *Balancer) dispatch(req Request) {
|
|
if *roundRobin {
|
|
w := b.pool[b.i]
|
|
w.requests <- req
|
|
w.pending++
|
|
b.i++
|
|
if b.i >= len(b.pool) {
|
|
b.i = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
w := heap.Pop(&b.pool).(*Worker)
|
|
w.requests <- req
|
|
w.pending++
|
|
// fmt.Printf("started %p; now %d\n", w, w.pending)
|
|
heap.Push(&b.pool, w)
|
|
}
|
|
|
|
func (b *Balancer) completed(w *Worker) {
|
|
if *roundRobin {
|
|
w.pending--
|
|
return
|
|
}
|
|
|
|
w.pending--
|
|
// fmt.Printf("finished %p; now %d\n", w, w.pending)
|
|
heap.Remove(&b.pool, w.i)
|
|
heap.Push(&b.pool, w)
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
work := make(chan Request)
|
|
for i := 0; i < nRequester; i++ {
|
|
go requester(work)
|
|
}
|
|
NewBalancer().balance(work)
|
|
}
|