Go并发:Chudnovky的算法,慢于同步 [英] Go Concurrency: Chudnovky's algorithm, slower than sync
问题描述
我刚刚开始按照朋友的建议开始学习。到目前为止,我喜欢它,但我写了(我想是)轻量级并发的力量的完美例子,并得到了一个令人惊讶的结果...所以我怀疑我做错了,或者我误解了goroutines是多么昂贵。我希望这里的一些gophers可以提供洞察。
I just recently started learning go at the recommendation of a friend. So far, I am loving it, but I wrote (what I thought would be) the perfect example of the power of lightweight concurrency, and got a surprising result... so I suspect I'm doing something wrong, or I'm misunderstanding how expensive goroutines are. I'm hoping some gophers here can provide insight.
我在Go使用goroutines和简单的同步执行写了Chudnovsky的算法。我假设,每个计算独立于其他计算,它至少会有更快的并行运行。
I wrote Chudnovsky's algorithm in Go using both goroutines and simple synchronous execution. I assumed, with each calculation being independent of the others, it'd be at least a little faster running concurrently.
:我在第5代i7上运行这个程序,所以如果goroutines被复用到线程上,并发和并行。
note: I am running this on a 5th gen i7, so if goroutines are multiplexed onto threads as I was told, this should be both concurrent and parallel.
package main
import (
"fmt"
"math"
"strconv"
"time"
)
func main() {
var input string
var sum float64
var pi float64
c := make(chan float64)
fmt.Print("How many iterations? ")
fmt.Scanln(&input)
max,err := strconv.Atoi(input)
if err != nil {
panic("You did not enter a valid integer")
}
start := time.Now() //start timing execution of concurrent routine
for i := 0; i < max; i++ {
go chudnovskyConcurrent(i,c)
}
for i := 0; i < max; i++ {
sum += <-c
}
end := time.Now() //end of concurrent routine
fmt.Println("Duration of concurrent calculation: ",end.Sub(start))
pi = 1/(12*sum)
fmt.Println(pi)
start = time.Now() //start timing execution of syncronous routine
sum = 0
for i := 0; i < max; i++ {
sum += chudnovskySync(i)
}
end = time.Now() //end of syncronous routine
fmt.Println("Duration of synchronous calculation: ",end.Sub(start))
pi = 1/(12*sum)
fmt.Println(pi)
}
func chudnovskyConcurrent(i int, c chan<- float64) {
var numerator float64
var denominator float64
ifloat := float64(i)
iun := uint64(i)
numerator = math.Pow(-1, ifloat) * float64(factorial(6*iun)) * (545140134*ifloat+13591409)
denominator = float64(factorial(3*iun)) * math.Pow(float64(factorial(iun)),3) * math.Pow(math.Pow(640320,3),ifloat+0.5)
c <- numerator/denominator
}
func chudnovskySync(i int) (r float64) {
var numerator float64
var denominator float64
ifloat := float64(i)
iun := uint64(i)
numerator = math.Pow(-1, ifloat) * float64(factorial(6*iun)) * (545140134*ifloat+13591409)
denominator = float64(factorial(3*iun)) * math.Pow(float64(factorial(iun)),3) * math.Pow(math.Pow(640320,3),ifloat+0.5)
r = numerator/denominator
return
}
func factorial(n uint64) (res uint64) {
if ( n > 0 ) {
res = n * factorial(n-1)
return res
}
return 1
}
这里是我的结果:
How many iterations? 20
Duration of concurrent calculation: 573.944µs
3.1415926535897936
Duration of synchronous calculation: 63.056µs
3.1415926535897936
推荐答案
您所做的计算太简单,无法在一个单独的goroutine中执行它们。你在运行时(创建goroutines,复用,调度等)比在实际计算上放弃更多的时间。你想做的是更适合GPU,例如,你有大量的并行执行单元,可以做这个简单的计算,在一瞬间。但你需要其他语言和API来完成。
The calculations you're doing are too simple to do each one of them in a separate goroutine. You're loosing more time in the runtime (creating goroutines, multiplexing, scheduling etc) than on actual calculations. What you're trying to do is more suited for GPU, for example, where you have massive number of parallel execution units that can do this simple calculations all at ones in an instant. But you would need other languages and APIs to do that.
你可以做的是为每个硬件执行线程创建软件执行线程。您要将 max
变量拆分为大块并并行执行。这里非常简单的举例说明这个想法:
What you can do is to create software thread of execution for every hardware thread of execution. You want to split your max
variable into big chunks and execute them in parallel. Here's very simple take on it just to illustrate the idea:
package main
import (
"fmt"
"math"
"strconv"
"time"
"runtime"
)
func main() {
var input string
var sum float64
var pi float64
c := make(chan float64, runtime.GOMAXPROCS(-1))
fmt.Print("How many iterations? ")
fmt.Scanln(&input)
max,err := strconv.Atoi(input)
if err != nil {
panic("You did not enter a valid integer")
}
start := time.Now() //start timing execution of concurrent routine
for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
go func(i int){
var sum float64
for j := 0; j < max/runtime.GOMAXPROCS(-1); j++ {
sum += chudnovskySync(j + i*max/runtime.GOMAXPROCS(-1))
}
c <- sum
}(i)
}
for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
sum += <-c
}
end := time.Now() //end of concurrent routine
fmt.Println("Duration of concurrent calculation: ",end.Sub(start))
pi = 1/(12*sum)
fmt.Println(pi)
start = time.Now() //start timing execution of syncronous routine
sum = 0
for i := 0; i < max; i++ {
sum += chudnovskySync(i)
}
end = time.Now() //end of syncronous routine
fmt.Println("Duration of synchronous calculation: ",end.Sub(start))
pi = 1/(12*sum)
fmt.Println(pi)
}
func chudnovskySync(i int) (r float64) {
var numerator float64
var denominator float64
ifloat := float64(i)
iun := uint64(i)
numerator = math.Pow(-1, ifloat) * float64(factorial(6*iun)) * (545140134*ifloat+13591409)
denominator = float64(factorial(3*iun)) * math.Pow(float64(factorial(iun)),3) * math.Pow(math.Pow(640320,3),ifloat+0.5)
r = numerator/denominator
return
}
func factorial(n uint64) (res uint64) {
if ( n > 0 ) {
res = n * factorial(n-1)
return res
}
return 1
}
这里是结果
$ go version
go version go1.5.2 windows/amd64
$ go run main.go
GOMAXPROCS = 4
How many iterations? 10000
Duration of concurrent calculation: 932.8916ms
NaN
Duration of synchronous calculation: 2.0639744s
NaN
这篇关于Go并发:Chudnovky的算法,慢于同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!