Go并发:Chudnovky的算法,慢于同步 [英] Go Concurrency: Chudnovky's algorithm, slower than sync

查看:160
本文介绍了Go并发:Chudnovky的算法,慢于同步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始按照朋友的建议开始学习。到目前为止,我喜欢它,但我写了(我想是)轻量级并发的力量的完美例子,并得到了一个令人惊讶的结果...所以我怀疑我做错了,或者我误解了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆