我试图找出哪种模式在 Go 中迭代一系列值的速度最快。每个都有明显的优点和缺点,但速度将是我用例的重要因素
不出所料,渠道是最慢的。
然而,我很惊讶看到回调和生成器模式之间存在巨大差异,我试图了解发生了什么。在我看来,性能应该差不多,但事实并非如此。
两者都调用了相同次数的匿名函数,但我想知道是否与上下文切换相关。
低层发生了什么?
我得到了这些结果:
BenchmarkIteratorMethods/generator-8 31970 36196 ns/op
BenchmarkIteratorMethods/callback-8 1000000 1193 ns/op
BenchmarkIteratorMethods/channel-8 7999 148906 ns/op
我的基准代码:
package test
import (
"testing"
)
const iteratorTestIterations = 1000
func iteratorGeneratorFunctionForTests() func() (int, bool) {
i := 0
return func() (int, bool) {
for i < iteratorTestIterations {
defer (func() {
i++
})()
return i, false
}
return 0, true
}
}
func iteratorCallbackForTests(callback func(value int) bool) {
for i := 0; i < iteratorTestIterations; i++ {
shouldContinue := callback(i)
if !shouldContinue {
break
}
}
}
func iteratorChannelForTests() chan int {
channel := make(chan int)
go (func() {
for i := 0; i < iteratorTestIterations; i++ {
channel <- i
}
close(channel)
})()
return channel
}
func BenchmarkIteratorMethods(b *testing.B) {
b.Run("generator", func(b *testing.B) {
for i := 0; i < b.N; i++ {
iterator := iteratorGeneratorFunctionForTests()
for {
value, end := iterator()
if end {
break
}
_ = value
}
}
})
b.Run("callback", func(b *testing.B) {
for i := 0; i < b.N; i++ {
iteratorCallbackForTests(func(value int) bool {
_ = value
return true
})
}
})
b.Run("channel", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for value := range iteratorChannelForTests() {
_ = value
}
}
})
}
答案 0 :(得分:2)
我找到了原因。它实际上与我的代码有关。带函数的延迟太慢了。
我用这个替换了第一个函数:
func iteratorGeneratorFunctionForTests() func() (int, bool) {
i := 0
return func() (int, bool) {
for i < iteratorTestIterations {
value := i
i++
return value, false
}
return 0, true
}
}
现在我得到了更合乎逻辑的结果:
BenchmarkIteratorMethods/generator-8 773698 1442 ns/op
BenchmarkIteratorMethods/callback-8 966046 1168 ns/op
BenchmarkIteratorMethods/channel-8 7710 145243 ns/op