package main import ( // "strings" "rand" "testing" // "time" "fmt" "container/list" ) type Buftype int const ( RingGrow = Buftype(iota) RingOrig RingOrigGrow Ring RingCont RingContGrow RingContCheck List ListCont Null Ntypes ) var bfuns = [](func(chan<- T) chan<- T) { RingOrigGrow: BufferRingOrigGrow, Ring: BufferRing, RingOrig: BufferRingOrig, RingCont: BufferRingCont, RingContGrow: BufferRingContGrow, RingContCheck: BufferRingContCheck, RingGrow: BufferRingGrow, List: BufferList, ListCont: BufferListCont, Null: BufferNull, } var names = []string { RingOrigGrow: "RingOrigGrow", RingOrig: "RingOrig", Ring: "Ring", RingContGrow: "RingContGrow", RingContCheck: "RingContCheck", RingCont: "RingCont", RingGrow: "RingGrow", List: "List", ListCont: "ListCont", Null: "Null", } var ftypes = map[func(chan<- T) chan<- T] Buftype { BufferRingOrigGrow: RingOrigGrow, BufferRingOrig: RingOrig, BufferRingGrow: RingGrow, BufferRing: Ring, BufferRingContGrow: RingContGrow, BufferRingContCheck: RingContCheck, BufferRingCont: RingCont, BufferList: List, BufferListCont: ListCont, BufferNull: Null, } func main() { b := make([]testing.Benchmark, Ntypes) t := make([]testing.Test, Ntypes) for i := Buftype(0); i < Ntypes; i++ { t[i].Name = names[i] t[i].F = Test(bfuns[i]) b[i].Name = names[i] b[i].F = Benchmark(bfuns[i]) } testing.Main(t) testing.RunBenchmarks(b) } func Test(bfn func(chan<- T) chan<- T) func(t *testing.T) { return func(t *testing.T) { testBuffer(t, bfn) } } func testBuffer(t *testing.T, bfn func(chan<- T) chan<- T) { fmt.Printf("testing %s\n", names[ftypes[bfn]]) if bfn == BufferNull { return } rc := make(chan T) c := bfn(rc) for i := T(0); i < 10; i++ { c <- i } for i := T(0); i < 10; i++ { if j := <-rc; j != i { t.Fail() return } } for i := T(0); i < 10; i++ { c <- i } for i := T(0); i < 8; i++ { if j := <-rc; j != i { t.Fail() return } } for i := T(10); i < 20; i++ { c <- i } close(c) i := T(8) for v := range(rc) { if v != i { t.Fail() return } i++ } } type T int const nvals = 1000000 const maxvals = 1000 func Benchmark(bfn func(chan<- T) chan<- T)(func (b *testing.B)) { return func(b *testing.B) { transfer(b, bfn) } } func transfer(b *testing.B, bfn func(chan<- T) chan<- T) { rc := make(chan T, 100) done := make(chan bool, 2) go func() { consume(rc, b.N) done <- true }() go func() { c := bfn(rc) produce(c, b.N) close(c) done <- true }() <-done <-done } func consume(c <-chan T, n int) { for n > 0 { i := rand.Int() % maxvals if i > n { i = n } for j := 0; j < i; j++ { <-c } n -= i // time.Sleep(1) } } func produce(c chan<- T, n int) { for n > 0 { i := rand.Int() % maxvals if i > n { i = n } for j := 0; j < i; j++ { c <- 99 } n -= i // time.Sleep(1) } } func dum() { fmt.Print() } func BufferNull(out chan<- T) chan<- T { return out } func BufferRingGrow(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. var outc chan<- T for { select { case e := <-in: if closed(in) { in = nil if n == 0 { close(out) return } } else { j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ if n == len(buf) { // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } outc = out } case outc <- buf[i]: buf[i] = zero if i++; i == len(buf) { i = 0 } n-- if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil } } } }() return in } func BufferRingOrig(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. for { outc := out switch { case n == 0: // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil case n == len(buf): // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b case len(buf) > 128 && n*3 < len(buf): // buffer too big, shrink it b := make([]T, len(buf) / 2) j := i + n if j > len(buf) { // wrap around k := j - len(buf) j = len(buf) copy(b, buf[i:j]) copy(b[j - i:], buf[0:k]) }else{ // contiguous copy(b, buf[i:j]) } i = 0 buf = b } select { case e := <-in: if closed(in) { in = nil } else { j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ } case outc <- buf[i]: buf[i] = zero if i++; i == len(buf) { i = 0 } n-- } } }() return in } func BufferRingOrigGrow(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. for { outc := out switch { case n == 0: // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil case n == len(buf): // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } select { case e := <-in: if closed(in) { in = nil } else { j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ } case outc <- buf[i]: buf[i] = zero if i++; i == len(buf) { i = 0 } n-- } } }() return in } func BufferRing(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. var outc chan<- T for { select { case e := <-in: if closed(in) { in = nil if n == 0 { close(out) return } } else { j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ if n == len(buf) { // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } outc = out } case outc <- buf[i]: buf[i] = zero if i++; i == len(buf) { i = 0 } n-- if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil } if len(buf) > 128 && n*3 < len(buf) { // buffer too big, shrink it b := make([]T, len(buf) / 2) j := i + n if j > len(buf) { // wrap around k := j - len(buf) j = len(buf) copy(b, buf[i:j]) copy(b[j - i:], buf[0:k]) }else{ // contiguous copy(b, buf[i:j]) } i = 0 buf = b } } } }() return in } func BufferRingCont(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. var outc chan<- T for { select { case e := <-in: for { if closed(in) { in = nil if n == 0 { close(out) return } break } j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ outc = out // enable output if n == len(buf) { // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } var ok bool if e, ok = <-in; !ok { break } } case outc <- buf[i]: for { buf[i] = zero if i++; i == len(buf) { i = 0 } n-- if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil break } if len(buf) > 128 && n*3 < len(buf) { // buffer too big, shrink it b := make([]T, len(buf) / 2) j := i + n if j > len(buf) { // wrap around k := j - len(buf) j = len(buf) copy(b, buf[i:j]) copy(b[j - i:], buf[0:k]) }else{ // contiguous copy(b, buf[i:j]) } i = 0 buf = b } if ok := outc <- buf[i]; !ok { break } } } } }() return in } func BufferRingContCheck(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. var outc chan<- T for { select { case e := <-in: for added := 0; added < 1000; added++ { if closed(in) { in = nil if n == 0 { close(out) return } break } j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ outc = out // enable output if n == len(buf) { // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } var ok bool if e, ok = <-in; !ok { break } } case outc <- buf[i]: for { buf[i] = zero if i++; i == len(buf) { i = 0 } n-- if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil break } if len(buf) > 128 && n*3 < len(buf) { // buffer too big, shrink it b := make([]T, len(buf) / 2) j := i + n if j > len(buf) { // wrap around k := j - len(buf) j = len(buf) copy(b, buf[i:j]) copy(b[j - i:], buf[0:k]) }else{ // contiguous copy(b, buf[i:j]) } i = 0 buf = b } if ok := outc <- buf[i]; !ok { break } } } } }() return in } func BufferRingContGrow(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var zero T var buf = make([]T, 10) var i = 0 // location of first value in buffer. var n = 0 // number of items in buffer. var outc chan<- T for { select { case e := <-in: for { if closed(in) { in = nil if n == 0 { close(out) return } break } j := i + n if j >= len(buf) { j -= len(buf) } buf[j] = e n++ if n == len(buf) { // buffer full: expand it b := make([]T, n*2) copy(b, buf[i:]) copy(b[n-i:], buf[0:i]) i = 0 buf = b } outc = out var ok bool if e, ok = <-in; !ok { break } } case outc <- buf[i]: for { buf[i] = zero if i++; i == len(buf) { i = 0 } n-- if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil break } if ok := outc <- buf[i]; !ok { break } } } } }() return in } func BufferList(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var buf = list.New() for { outc := out var v T n := buf.Len() if n == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil }else{ v = buf.Front().Value.(T) } select { case e := <-in: if closed(in) { in = nil } else { buf.PushBack(e) } case outc <- v: buf.Remove(buf.Front()) } } }() return in } func BufferListCont(out chan<- T) chan<- T { in := make(chan T, 100) go func() { var buf = list.New() var outc chan<- T var v T for { select { case e := <-in: if buf.Len() == 0 && !closed(in) { outc = out v = e } for { if closed(in) { in = nil if buf.Len() == 0 { close(out) return } break } buf.PushBack(e) var ok bool if e, ok = <-in; !ok { break } } case outc <- v: for { buf.Remove(buf.Front()) if buf.Len() == 0 { // buffer empty: don't try to send on output if in == nil { close(out) return } outc = nil break } v = buf.Front().Value.(T) if ok := outc <- v; !ok { break } } } } }() return in }