Skip to content

Instantly share code, notes, and snippets.

@timstclair
Last active June 9, 2022 07:15
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save timstclair/be93968572d36e03cc4c to your computer and use it in GitHub Desktop.
Save timstclair/be93968572d36e03cc4c to your computer and use it in GitHub Desktop.
Benchmark aggregating channels vs. reflect.Select
package scratch
import (
"reflect"
"testing"
)
const numProduce = 1000
const numChannels = 100
const expectedResult int64 = numChannels * numProduce * (numProduce - 1) / 2
func BenchmarkReflectSelect(b *testing.B) {
for i := 0; i < b.N; i++ {
checkResult(b, benchmarkReflectSelect())
}
}
func BenchmarkGoSelect(b *testing.B) {
for i := 0; i < b.N; i++ {
checkResult(b, benchmarkGoSelect())
}
}
func BenchmarkSanity(b *testing.B) {
for i := 0; i < b.N; i++ {
checkResult(b, benchmarkSanity())
}
}
// Use reflection to select from a slice of channels.
func benchmarkReflectSelect() int64 {
channels := makeChannels(numChannels)
var result int64 = 0
cases := make([]reflect.SelectCase, numChannels)
for i, ch := range channels {
cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
}
for finished := 0; finished < numChannels; {
i, value, ok := reflect.Select(cases)
if !ok {
cases = append(cases[:i], cases[i+1:]...)
finished++
} else {
result += value.Int()
}
}
return result
}
// Use an aggregate channel to select from a slice of channels
func benchmarkGoSelect() int64 {
channels := makeChannels(numChannels)
var result int64 = 0
done := make(chan struct{})
combinedChannel := make(chan int)
for i := 0; i < numChannels; i++ {
go func(c chan int) {
for v := range c {
combinedChannel <- v
}
done <- struct{}{}
}(channels[i])
}
finished := 0
for finished < numChannels {
select {
case i := <-combinedChannel:
result += int64(i)
case <-done:
finished++
}
}
close(combinedChannel)
close(done)
return result
}
// Sanity check.
func benchmarkSanity() int64 {
var result int64 = 0
for i := 0; i < numChannels; i++ {
for j := 0; j < numProduce; j++ {
result += int64(j)
}
}
return result
}
func checkResult(b *testing.B, result int64) {
if result != expectedResult {
b.Fatalf("Fail! Expected %v but got %v", expectedResult, result)
}
}
func produce(c chan int, n int) {
for i := 0; i < n; i++ {
c <- i
}
close(c)
}
func makeChannels(n int) []chan int {
chans := make([]chan int, n)
for i := 0; i < n; i++ {
c := make(chan int)
go produce(c, numProduce)
chans[i] = c
}
return chans
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment