Last active
June 9, 2022 07:15
-
-
Save timstclair/be93968572d36e03cc4c to your computer and use it in GitHub Desktop.
Benchmark aggregating channels vs. reflect.Select
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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