我有两个版本的阶乘。并发与序列。
程序都会计算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的新手。
答案 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)
Go允许我们编写并发程序。这与尝试编写更快的并行程序有关,但这两个问题是分开的。有关详细信息,请参阅Rob Pike的Concurrency is not Parallelism。