gpt4 book ai didi

GoLang - 顺序与并发

转载 作者:IT王子 更新时间:2023-10-29 01:58:34 30 4
gpt4 key购买 nike

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

这两个程序都会计算 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 内存。

我不确定是程序有问题还是我的基本理解是正确的。

附注- 我是 GoLang 的新手。

最佳答案

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

您的程序是并发的,但不是并行的。每个 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 上下文切换的开销导致性能下降。

关于GoLang - 顺序与并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39299711/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com