【发布时间】:2018-04-09 01:12:27
【问题描述】:
我正在学习golang和goroutine的使用。
使用此示例代码,在我的具有 4 个逻辑 CPU 的 PC 上,我无法理解为什么我没有任何性能提升。
如果我添加并发,它所花费的时间往往会比原来没有并发的慢。
2017/10/27 13:57:17 Starting 1 threads
2017/10/27 13:57:27 Id routine: 0 rate: 232.558140
2017/10/27 13:57:27 Current total rate was 232.56 K/s
2017/10/27 13:57:27 Starting 1 threads
2017/10/27 13:57:37 Id routine: 0 rate: 357.142857
2017/10/27 13:57:37 Current total rate was 357.14 K/s
2017/10/27 13:58:04 Starting 2 threads
2017/10/27 13:58:14 Id routine: 0 rate: 131.578947
2017/10/27 13:58:14 Id routine: 1 rate: 129.870130
2017/10/27 13:58:14 Current total rate was 261.45 K/s
2017/10/27 13:58:14 Starting 2 threads
2017/10/27 13:58:24 Id routine: 1 rate: 144.927536
2017/10/27 13:58:24 Id routine: 0 rate: 151.515152
2017/10/27 13:58:24 Current total rate was 296.44 K/s
2017/10/27 13:59:15 Starting 3 threads
2017/10/27 13:59:25 Id routine: 2 rate: 94.339623
2017/10/27 13:59:25 Id routine: 0 rate: 92.592593
2017/10/27 13:59:25 Id routine: 1 rate: 90.090090
2017/10/27 13:59:25 Current total rate was 277.02 K/s
2017/10/27 13:59:25 Starting 3 threads
2017/10/27 13:59:35 Id routine: 2 rate: 92.592593
2017/10/27 13:59:35 Id routine: 0 rate: 90.090090
2017/10/27 13:59:35 Id routine: 1 rate: 101.010101
2017/10/27 13:59:35 Current total rate was 283.69 K/s
2017/10/27 14:00:12 Starting 4 threads
2017/10/27 14:00:22 Id routine: 1 rate: 56.818182
2017/10/27 14:00:22 Id routine: 3 rate: 56.818182
2017/10/27 14:00:22 Id routine: 0 rate: 61.349693
2017/10/27 14:00:22 Id routine: 2 rate: 62.500000
2017/10/27 14:00:22 Current total rate was 237.49 K/s
2017/10/27 14:00:22 Starting 4 threads
2017/10/27 14:00:32 Id routine: 1 rate: 67.114094
2017/10/27 14:00:32 Id routine: 3 rate: 61.349693
2017/10/27 14:00:32 Id routine: 0 rate: 57.803468
2017/10/27 14:00:32 Id routine: 2 rate: 65.789474
2017/10/27 14:00:32 Current total rate was 252.06 K/s
还有,这里是 goroutine 的并发代码:
package main
import (
"fmt"
"log"
"sync"
"time"
)
var (
tryArr []string
goroutineQuit = make(chan struct{})
concurrencyMapHQ = struct {
sync.RWMutex
HQ map[int]float64
}{HQ: make(map[int]float64)}
)
func rateStats() {
var totalRate float64
for i, rate := range concurrencyMapHQ.HQ {
log.Printf("Id routine: %d rate: %f", i, rate)
totalRate += rate
}
log.Printf("Current total rate was %.2f K/s", totalRate)
}
func stopThreads(threads int) {
for i := 0; i < threads; i++ {
goroutineQuit <- struct{}{}
}
rateStats()
}
func runThreads(threads int, steps int) {
log.Printf("Starting %d threads", threads)
for i := 0; i < threads; i++ {
i := i
go func(id int, steps int) {
calculate(i, steps)
}(i, steps)
}
}
func calculate(idRoutine int, steps int) {
var (
possibles []string
numBatchs = 0
t1 = time.Now()
)
for {
select {
case <-goroutineQuit:
return
default:
possibles = nil
for _, nonce := range tryArr {
possibles = append(possibles, nonce)
}
numBatchs++
if numBatchs == 99 {
t2 := time.Now()
timeExecution := t2.Sub(t1) / time.Millisecond
concurrencyMapHQ.Lock()
concurrencyMapHQ.HQ[idRoutine] = float64(steps) / float64(timeExecution)
concurrencyMapHQ.Unlock()
numBatchs = 0
t1 = t2
}
}
}
}
func main() {
threads := 4
steps := 10000
tryArr = make([]string, steps)
for i := 0; i < steps; i++ {
tryArr[i] = fmt.Sprintf("indice: %d", i)
}
for {
runThreads(threads, steps)
time.Sleep(10 * time.Second)
stopThreads(threads)
runThreads(threads, steps)
time.Sleep(10 * time.Second)
stopThreads(threads)
}
}
【问题讨论】:
-
您是否分析过您的应用程序以查看时间花费在哪里?如果您从使用测试包的真正基准开始,这会更容易。
-
正如 JimB 提到的,pprof 是调试这类问题的好工具。我还推荐 trace 捕获执行事件,如 goroutine 创建/阻塞/解除阻塞。 youtube 频道 justforfunc 最近做了一个关于使用该工具提高围棋程序性能的教程视频。
-
按照 JimB 的建议,使用
golang内置的 test/benchmark 框架,而不是构建自己的框架。它甚至涵盖了并行基准。 -
并非所有事情都可以通过并发得到改善。让事情快速发展并不是万能的灵丹妙药。你的 goroutine 运行非常紧密的循环,中间有一个互斥锁;我不知道你可以从并发实现多少性能提升。
-
本次讲座也会对你有所帮助blog.golang.org/concurrency-is-not-parallelism
标签: performance go concurrency goroutine