Go-lang并行段比串行段慢

时间:2013-12-18 22:57:33

标签: go concurrency parallel-processing

我建立了一个流行数学模型,在Go中计算量很大。我现在正在尝试构建一组系统来测试我的模型,在那里我改变输入并期望输出不同。我建立了一个系列版本,以缓慢增加艾滋病毒的流行率,并看到对艾滋病毒死亡的影响。运行需要大约200毫秒。

for q = 0.0; q < 1000; q++ {

    inputs.CountryProfile.HivPrevalenceAdultsByGroup[0] = inputs.CountryProfile.HivPrevalenceAdultsByGroup[0] * float32(math.Pow(1.00001, q))
    results := costAnalysisHandler(inputs)
    fmt.Println(results.HivDeaths[20])

}

然后我使用频道创建了一个“并行”版本,它需要更长,大​​约400毫秒才能运行。这些小的变化非常重要,因为我们将使用不同的输入运行数百万次运行,因此希望尽可能提高效率。这是并行版本:

ch := make(chan ChData)
var q float64
for q = 0.0; q < 1000; q++ {
    go func(q float64, inputs *costanalysis.Inputs, ch chan ChData) {
        inputs.CountryProfile.HivPrevalenceAdultsByGroup[0] = inputs.CountryProfile.HivPrevalenceAdultsByGroup[0] * float32(math.Pow(1.00001, q))
        results := costAnalysisHandler(inputs)
        fmt.Println(results.HivDeaths[20])
        ch <- ChData{int(q), results.HivDeaths[20]}
    }(q, inputs, ch)
}
for q = 0.0; q < 1000; q++ {
    theResults := <-ch
    fmt.Println(theResults)
}

任何想法非常赞赏。

3 个答案:

答案 0 :(得分:4)

启动和与后台任务通信会产生开销。花费在成本分析上的时间可能相形见绌等于通信成本,如果程序耗时200ms,但如果协调成本 杀死你的应用程序,一个常见的方法是一次交出大块的工作 - 例如,让每个goroutine分析10 q个值,而不只是一个。{ (编辑:正如@Innominate所说,制作一个处理作业对象队列的goroutine的“工作池”是另一种常见方法。)

此外,您粘贴的代码具有竞争条件。每次生成goroutine时,都不会复制Inputs结构的内容,因为您正在向函数传递指针。因此,并行运行的goroutines将读取和写入同一个Inputs实例。

为每个分析创建一个全新的Inputs实例,使用自己的数组等,可以避免竞争。如果最终浪费了大量内存或导致大量冗余副本,您可以1)recycle Inputs instances,2)分离出可以安全共享的只读数据(可能是已修复的国家/地区数据,dunno),或者3)将一些相对较大的数组更改为costAnalysisHandler内的局部变量,而不是需要传递的东西(也许它可能只需要初始HIV流行并在t = 20时返回HIV死亡,其他一切都是本地和堆栈。)

这不适用于Go今天,但是在最初发布问题时没有:除非您使用所需的并发级别调用runtime.GOMAXPROCS(),否则没有任何内容真正并行运行,例如runtime.GOMAXPROCS(runtime.NumCPU())。< / p>

最后,如果您正在做一些更大的分析并且实际上存在性能问题,那么您应该只担心所有这些问题;如果.2秒的等待是所有表演工作可以在这里拯救你,那就不值得了。

答案 1 :(得分:1)

并行化计算密集型计算需要并行计算实际上可以在您的计算机上并行运行。如果他们不这样做,那么创建goroutine,频道和读取频道的额外开销将使程序运行得更慢。

我猜这是问题所在。

尝试将GOMAXPROCS环境变量设置为运行代码之前的CPU数量。或者在开始parallell计算之前调用runtime.GOMAXRPROCS(runtime.NumCPU())。

答案 2 :(得分:1)

我看到两个与并行性能相关的问题,

第一个也是最明显的一个是你必须设置GOMAXPROCS才能让Go运行时使用多个cpu / core。通常会根据机器中的处理器数量设置它,但理想的设置可能会有所不同。

第二个问题有点棘手,那就是你的代码似乎没有很好的并行化。简单地开始一千个goroutines并假设他们将会解决它并不会给出好的结果。您可能应该使用某种工作池,运行有限数量的同时计算(一个好的起始编号是将其设置为与GOMAXPROCS相同),而不是尝试一次执行1000次。

请参阅:http://golang.org/doc/faq#Why_no_multi_CPU