2016-08-21 2 views
1

積分を同時に計算しようとしましたが、私のプログラムは通常のforループで積分を計算するよりも遅くなりました。私は間違って何をしていますか?Golangの同時積分計算

package main 

import (
    "fmt" 
    "math" 
    "sync" 
    "time" 
) 

type Result struct { 
    result float64 
    lock sync.RWMutex 
} 

var wg sync.WaitGroup 
var result Result 

func main() { 
    now := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 100000.0 
    deltax := (b - a)/n 
    wg.Add(int(n)) 
    for i := 0.0; i < n; i++ { 
     go f(a, deltax, i) 
    } 
    wg.Wait() 
    fmt.Println(deltax * result.result) 
    fmt.Println(time.Now().Sub(now)) 
} 

func f(a float64, deltax float64, i float64) { 
    fx := math.Sqrt(a + deltax * (i + 0.5)) 
    result.lock.Lock() 
    result.result += fx 
    result.lock.Unlock() 
    wg.Done() 
} 

答えて

2

ゴルーチンでの活動に要する時間は、コンテキストを切り替えるタスクを実行し、値を更新するために、ミューテックスを使用するために必要なよりも多くの時間を要する場合を除き、連続的にそれを行うには速いだろう。

少し修正したバージョンをご覧ください。私がやったことは、f()機能に1マイクロ秒の遅延を加えたことだけです。

遅延で
package main 

import (
    "fmt" 
    "math" 
    "sync" 
    "time" 
) 

type Result struct { 
    result float64 
    lock sync.RWMutex 
} 

var wg sync.WaitGroup 
var result Result 

func main() { 
    fmt.Println("concurrent") 
    concurrent() 
    result.result = 0 
    fmt.Println("serial") 
    serial() 
} 

func concurrent() { 
    now := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 100000.0 
    deltax := (b - a)/n 
    wg.Add(int(n)) 
    for i := 0.0; i < n; i++ { 
     go f(a, deltax, i, true) 
    } 
    wg.Wait() 
    fmt.Println(deltax * result.result) 
    fmt.Println(time.Now().Sub(now)) 
} 

func serial() { 
    now := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 100000.0 
    deltax := (b - a)/n 
    for i := 0.0; i < n; i++ { 
     f(a, deltax, i, false) 
    } 
    fmt.Println(deltax * result.result) 
    fmt.Println(time.Now().Sub(now)) 
} 

func f(a, deltax, i float64, concurrent bool) { 
    time.Sleep(1 * time.Microsecond) 
    fx := math.Sqrt(a + deltax*(i+0.5)) 
    if concurrent { 
     result.lock.Lock() 
     result.result += fx 
     result.lock.Unlock() 
     wg.Done() 
    } else { 
     result.result += fx 
    } 
} 

次のように、結果は(同時バージョンははるかに高速である)であった:遅延なし

concurrent 
0.6666666685900424 
624.914165ms 

serial 
0.6666666685900422 
5.609195767s 

concurrent 
0.6666666685900428 
50.771275ms 

serial 
0.6666666685900422 
749.166µs 

あなたが見ることができるように、長いですタスクを完了するのに要すると、可能な場合は、それを同時に行うことがより意味があります。パフォーマンスの向上のために

2

、3- 、あなたがlock sync.RWMutexを使用せずに、CPUコアごとにタスクを分割してもよい:

+30xチャネルを使用して最適化とruntime.NumCPU()を、これはあなたのサンプルコードながら、2ms 2のコアと993µs 8上のコアを取ります2つのコアと40ms 8のコアに61msを取ります

このワーキングサンプルコードと出力を参照してください。

package main 

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

func main() { 
    nCPU := runtime.NumCPU() 
    fmt.Println("nCPU =", nCPU) 
    ch := make(chan float64, nCPU) 
    startTime := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 100000.0 
    deltax := (b - a)/n 

    stepPerCPU := n/float64(nCPU) 
    for start := 0.0; start < n; { 
     stop := start + stepPerCPU 
     go f(start, stop, a, deltax, ch) 
     start = stop 
    } 

    integral := 0.0 
    for i := 0; i < nCPU; i++ { 
     integral += <-ch 
    } 

    fmt.Println(time.Now().Sub(startTime)) 
    fmt.Println(deltax * integral) 
} 

func f(start, stop, a, deltax float64, ch chan float64) { 
    result := 0.0 
    for i := start; i < stop; i++ { 
     result += math.Sqrt(a + deltax*(i+0.5)) 
    } 
    ch <- result 
} 

2つのコアの出力:8つのコアの

nCPU = 2 
2.0001ms 
0.6666666685900485 

出力:

nCPU = 8 
993µs 
0.6666666685900456 

あなたのサンプルコード、2つのコアの出力:

0.6666666685900424 
61.0035ms 

8上のあなたのサンプルコード、出力コア:

0.6666666685900415 
40.9964ms 
0あなたはここに2個のコア、これは2つのコアで 110msがかかりますが、これと同じCPU上 1を使用してを使用して見るように

:良いベンチマークの統計情報については


2 - は、多数のサンプル(ビッグN)を使用します

package main 

import (
    "fmt" 
    "math" 
    "time" 
) 

func main() { 
    now := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 10000000.0 
    deltax := (b - a)/n 
    result := 0.0 
    for i := 0.0; i < n; i++ { 
     result += math.Sqrt(a + deltax*(i+0.5)) 
    } 
    fmt.Println(time.Now().Sub(now)) 
    fmt.Println(deltax * result) 
} 
01:n := 10000000.0とシングルゴルーチンで

、このワーキングサンプルコードを参照してください:コアこれは215msn := 10000000.0となります

出力:n := 10000000.0と2ゴルーチンで

215.0123ms 
0.6666666666685884 

、このワーキングサンプルコードを参照してください。

package main 

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

func main() { 
    nCPU := runtime.NumCPU() 
    fmt.Println("nCPU =", nCPU) 
    ch := make(chan float64, nCPU) 
    startTime := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 10000000.0 
    deltax := (b - a)/n 

    stepPerCPU := n/float64(nCPU) 
    for start := 0.0; start < n; { 
     stop := start + stepPerCPU 
     go f(start, stop, a, deltax, ch) 
     start = stop 
    } 

    integral := 0.0 
    for i := 0; i < nCPU; i++ { 
     integral += <-ch 
    } 

    fmt.Println(time.Now().Sub(startTime)) 
    fmt.Println(deltax * integral) 
} 

func f(start, stop, a, deltax float64, ch chan float64) { 
    result := 0.0 
    for i := start; i < stop; i++ { 
     result += math.Sqrt(a + deltax*(i+0.5)) 
    } 
    ch <- result 
} 

出力:

nCPU = 2 
110.0063ms 
0.6666666666686073 

の1- ありゴルーチンの数の最適なポイントであり、この時点からゴルーチンの増加数は、プログラムの実行時間を短縮しません:2つのコアのCPUで

、次のコードを使用して、結果は次のとおりです。

nCPU: 1,   2,   4,   8,   16 
Time: 2.16s, 1.1220642s, 1.1060633s, 1.1140637s, 1.1380651s 

あなたはnCPU=1時間nCPU=2から見たような減少は十分な大きさであるが、この時点の後に、それはそうnCPU := runtime.NumCPU()が、ここで十分です使用して、そのnCPU=2 2コアCPU上で、このサンプル・コードの最適点があり、あまりないです。

package main 

import (
    "fmt" 
    "math" 
    "time" 
) 

func main() { 
    nCPU := 2 //[email protected] [email protected] [email protected] [email protected] [email protected] 
    fmt.Println("nCPU =", nCPU) 
    ch := make(chan float64, nCPU) 
    startTime := time.Now() 
    a := 0.0 
    b := 1.0 
    n := 100000000.0 
    deltax := (b - a)/n 

    stepPerCPU := n/float64(nCPU) 
    for start := 0.0; start < n; { 
     stop := start + stepPerCPU 
     go f(start, stop, a, deltax, ch) 
     start = stop 
    } 

    integral := 0.0 
    for i := 0; i < nCPU; i++ { 
     integral += <-ch 
    } 

    fmt.Println(time.Now().Sub(startTime)) 
    fmt.Println(deltax * integral) 
} 

func f(start, stop, a, deltax float64, ch chan float64) { 
    result := 0.0 
    for i := start; i < stop; i++ { 
     result += math.Sqrt(a + deltax*(i+0.5)) 
    } 
    ch <- result 
} 
+0

@ husain-al-marzooq私はこれが役立つことを望みます。 –