例子取自《100 Go Mistakes》一书
我不明白为什么使用通道比简单地添加数组更有利可图?他们不仅要承担创作成本,还要承担经营活动的成本,因为它们的背后是结构。
小澄清:
Count
并Count3
使用类似的结构Result
Count2
使用类型结构Result2
type Input struct {
a int64
b int64
}
type Result struct {
sumA int64
sumB int64
}
//[56]byte используется для расположение sumA и SumB в разных сегментах кеша
type Result2 struct {
sumA int64
_ [56]byte
sumB int64
}
// 1048576 - 2^20 для наглядности
func CreateInput() []Input {
input := make([]Input, 0, 1048576)
for i := int64(0); i < 1048576; i++ {
input = append(input, Input{
a: i,
b: i,
})
}
return input
}
func Count(inputs []Input) Result {
wg := sync.WaitGroup{}
wg.Add(2)
result := Result{}
go func() {
for i := 0; i < len(inputs); i++ {
result.sumA += inputs[i].a
}
wg.Done()
}()
go func() {
for i := 0; i < len(inputs); i++ {
result.sumB += inputs[i].b
}
wg.Done()
}()
wg.Wait()
return result
}
func Count2(inputs []Input) Result2 {
wg := sync.WaitGroup{}
wg.Add(2)
result := Result2{}
go func() {
for i := 0; i < len(inputs); i++ {
result.sumA += inputs[i].a
}
wg.Done()
}()
go func() {
for i := 0; i < len(inputs); i++ {
result.sumB += inputs[i].b
}
wg.Done()
}()
wg.Wait()
return result
}
func Count3(inputs []Input) Result {
var result Result
sumAChan := make(chan int64)
sumBChan := make(chan int64)
go func() {
var sumA int64
for i := 0; i < len(inputs); i++ {
sumA += inputs[i].a
}
sumAChan <- sumA
}()
go func() {
var sumB int64
for i := 0; i < len(inputs); i++ {
sumB += inputs[i].b
}
sumBChan <- sumB
}()
result.sumA = <-sumAChan
result.sumB = <-sumBChan
return result
}
更新:结果在很大程度上取决于参数-coverprofile
Count2
和Count3
非常接近,但为什么-coverprofile
它只在 上产生如此大的差异Count2
?如果没有它,平均结果是:
│ stats.txt │
│ sec/op │
Count-12 18.24m ± 2%
Count2-12 677.0µ ± 7%
Count3-12 689.8µ ± 12%
geomean 2.042m
│ stats.txt │
│ B/op │
Count-12 162.5 ± 19%
Count2-12 192.0 ± 6%
Count3-12 288.0 ± 0%
geomean 207.9
│ stats.txt │
│ allocs/op │
Count-12 4.000 ± 0%
Count2-12 4.000 ± 0%
Count3-12 4.000 ± 0%
geomean 4.000
以下是包含 1,048,576 个元素的基准测试结果c -coverprofile
cpu: AMD Ryzen 5 5600G with Radeon Graphics
BenchmarkCount-12 49 25359128 ns/op 237 B/op 4 allocs/op
BenchmarkCount2-12 295 3661261 ns/op 300 B/op 4 allocs/op
BenchmarkCount3-12 1946 644967 ns/op 288 B/op 4 allocs/op
以下是基准测试结果:без -coverprofile
BenchmarkCount-12 67 18467245 ns/op 209 B/op 4 allocs/op
BenchmarkCount2-12 1833 597416 ns/op 211 B/op 4 allocs/op
BenchmarkCount3-12 1938 734245 ns/op 288 B/op 4 allocs/op
基准代码
var res test.Result
var res2 test.Result2
var res3 test.Result
// BenchmarkResult placeholder.
func BenchmarkCount(b *testing.B) {
var sum test.Result
b.StopTimer()
input := test.CreateInput()
b.StartTimer()
for i := 0; i < b.N; i++ {
sum = test.Count(input)
}
res = sum
}
func BenchmarkCount2(b *testing.B) {
var sum test.Result2
b.StopTimer()
input := test.CreateInput()
b.StartTimer()
for i := 0; i < b.N; i++ {
sum = test.Count2(input)
}
res2 = sum
}
func BenchmarkCount3(b *testing.B) {
var sum test.Result
b.StopTimer()
input := test.CreateInput()
b.StartTimer()
for i := 0; i < b.N; i++ {
sum = test.Count3(input)
}
res3 = sum
}
运行基准测试的命令,通过 vscode 中的图形界面启动
go test -benchmem -run=^$ -coverprofile=/tmp/vscode-govayH6A/go-code-cover -bench . alice/effective/internal/test