GoLang - 顺序与并发

时间:2016-09-02 19:44:38

标签: go concurrency

我有两个版本的阶乘。并发与序列。

程序都会计算10“1000000”次的阶乘。

因子并发处理

package main

import (
    "fmt"
    //"math/rand"
    "sync"
    "time"
    //"runtime"
)

func main() {
    start := time.Now()
    printFact(fact(gen(1000000)))
    fmt.Println("Current Time:", time.Now(), "Start Time:", start, "Elapsed Time:", time.Since(start))
    panic("Error Stack!")
}

func gen(n int) <-chan int {
    c := make(chan int)
    go func() {
        for i := 0; i < n; i++ {
            //c <- rand.Intn(10) + 1
            c <- 10
        }
        close(c)
    }()
    return c
}

func fact(in <-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    for n := range in {
        wg.Add(1)
        go func(n int) {
            //temp := 1
            //for i := n; i > 0; i-- {
            //  temp *= i
            //}
            temp := calcFact(n)
            out <- temp
            wg.Done()
        }(n)
    }
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

func printFact(in <-chan int) {
    //for n := range in {
    //  fmt.Println("The random Factorial is:", n)
    //}
    var i int
    for range in {
        i ++
    }
    fmt.Println("Count:" , i)
}

func calcFact(c int) int {
    if c == 0 {
        return 1
    } else {
        return calcFact(c-1) * c
    }
}

//###End of Factorial Concurrent 

因子序列处理

package main

import (
    "fmt"
    //"math/rand"
    "time"
    "runtime"
)

func main() {
    start := time.Now()
    //for _, n := range factorial(gen(10000)...) {
    //  fmt.Println("The random Factorial is:", n)
    //}
    var i int
    for range factorial(gen(1000000)...) {
        i++
    }
    fmt.Println("Count:" , i)
    fmt.Println("Current Time:", time.Now(), "Start Time:", start, "Elapsed Time:", time.Since(start))
}

func gen(n int) []int {
    var out []int
    for i := 0; i < n; i++ {
        //out = append(out, rand.Intn(10)+1)
        out = append(out, 10)
    }
    println(len(out))
    return out
}

func factorial(val ...int) []int {
    var out []int
    for _, n := range val {
        fa := calcFact(n)
        out = append(out, fa)
    }
    return out
}

func calcFact(c int) int {
    if c == 0 {
        return 1
    } else {
        return calcFact(c-1) * c
    }
}

//###End of Factorial sequential processing

我的假设是并发处理比顺序处理更快,但顺序执行的速度比我的Windows机器中的并发处理速度快。

我正在使用8核/ i7 / 32 GB RAM。

我不确定程序中是否有错误或我的基本理解是否正确。

P.S。 - 我是GoLang的新手。

2 个答案:

答案 0 :(得分:3)

与顺序版本相比,程序的并发版本总是很慢。然而,原因与您试图解决的问题的性质和行为有关。

您的程序是并发的但不是并行的。每个callFact都在其自己的goroutine中运行,但是没有需要完成的工作量的划分。每个goroutine必须执行相同的计算并输出相同的值。

这就像有一项任务要求将一些文本复制一百次。您只有一个CPU(暂时忽略核心)。

当您启动顺序进程时,将CPU指向原始文本一次,并要求它将其写下100次。 CPU必须管理单个任务。

使用goroutines,CPU被告知必须同时完成一百个任务。碰巧它们都是相同的任务。但是CPU并不够聪明,不知道。

所以它与上面做的一样。即使每个任务现在小100倍,仍然只有一个CPU。所以CPU的工作量仍然是相同的,除了一次管理100个不同的东西所带来的所有额外开销。因此,它失去了部分效率。

要看到性能的提升,您需要适当的并行性。一个简单的例子是将阶乘输入数大致分成中间并计算2个较小的阶乘。然后将它们组合在一起:

// not an ideal solution

func main() {
    ch := make(chan int)
    r := 10
    result := 1
    go fact(r, ch)
    for i := range ch {
        result *= i
    }
    fmt.Println(result)
}

func fact(n int, ch chan int) {
    p := n/2
    q := p + 1
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
        ch <- factPQ(1, p)
        wg.Done()
    }()
    go func() {
        ch <- factPQ(q, n)
        wg.Done()
    }()
    go func() {
        wg.Wait()
        close(ch)
    }()
}

func factPQ(p, q int) int {
    r := 1
    for i := p; i <= q; i++ {
        r *= i
    }
    return r
}

工作代码:https://play.golang.org/p/xLHAaoly8H

现在你有两个goroutine朝着同一个目标努力,而不仅仅是重复相同的计算。

关于CPU内核的注意事项:

在原始代码中,顺序版本的操作绝对是由运行时环境和操作系统分布在各种CPU内核中的。所以它仍然具有一定程度的并行性,你只是无法控制它。

在并发版本中也是如此,但如上所述,goroutine上下文切换的开销会降低性能。

答案 1 :(得分:0)

abhink给出了一个很好的答案。我还想提请注意Amdahl's Law,在尝试使用并行处理来提高计算的整体速度时,应始终牢记这一点。 &nbsp;&#34;&nbsp;&nbsp;&nbsp;&#34;

Go允许我们编写并发程序。这与尝试编写更快的并行程序有关,但这两个问题是分开的。有关详细信息,请参阅Rob Pike的Concurrency is not Parallelism