Quantcast
Channel: 19日に更新してた
Viewing all articles
Browse latest Browse all 58

【golang】math.Pow をその度に計算するのと計算結果を array に入れて参照するの、ループを goroutine 使って並列計算するしないの、どれくらい変わる?

$
0
0

そういえば以前、rust と pythonの比較をしたけれど、golangではあの計算をやっていなかったなと思い、ベタで計算するのと array に入れて参照するのと、ループを振り分けて goroutine を使って計算するのが、それぞれどれくらいになるかを見てみようかと、4つにわけて試してみました。
owiewowe.hatenablog.com

package main

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

func x5(i int) float64 {
	f := float64(i)
	return math.Pow(f, 5)
}

func CalcWithoutGoroutine() {
	start := time.Now()
L:
	for a := 1; a <= limit; a++ {
		for b := a; b <= limit; b++ {
			for c := b; c <= limit; c++ {
				for d := c; d <= limit; d++ {
					for e := d; e <= limit; e++ {
						if x5(a)+x5(b)+x5(c)+x5(d) == x5(e) {
							fmt.Println(a, b, c, d, e)
							fmt.Println("CalcWithoutGoroutine() ", time.Since(start))
							break L
						}
					}
				}
			}
		}
	}
}

func SilceWithoutGoroutine() {
	start := time.Now()
	s := [limit]float64{}
	for i := 0; i < limit; i++ {
		s[i] = x5(i + 1)
	}
L:
	for a := 0; a < limit; a++ {
		for b := a; b < limit; b++ {
			for c := b; c < limit; c++ {
				for d := c; d < limit; d++ {
					for e := d; e < limit; e++ {
						if s[a]+s[b]+s[c]+s[d] == s[e] {
							fmt.Println(a, b, c, d, e)
							fmt.Println("SilceWithoutGoroutine()", time.Since(start))
							break L
						}
					}
				}
			}
		}
	}
}

func CalcWithGoroutine() {
	start := time.Now()
	var wg sync.WaitGroup
	ch := make(chanstruct{})
LO:
	for a := 1; a <= limit; a++ {
		select {
		case<-ch:
			break LO
		default:
		}
		wg.Add(1)
		t := func(a int, ch chanstruct{}) {
		L:
			for b := a; b <= limit; b++ {
				for c := b; c <= limit; c++ {
					select {
					case<-ch:
						break L
					default:
					}
					for d := c; d <= limit; d++ {
						for e := d; e <= limit; e++ {
							if x5(a)+x5(b)+x5(c)+x5(d) == x5(e) {
								fmt.Println(a, b, c, d, e)
								fmt.Println("CalcWithGoroutine()", time.Since(start))
								close(ch)
								// ここでgoroutineの数を見ると結構な数が走っている// fmt.Println(runtime.NumGoroutine())break L
							}
						}
					}
				}
			}
			wg.Done()
		}
		go t(a, ch)
	}
	wg.Wait()
	// goroutine終了確認用// fmt.Println(time.Since(start))
}

func SliceWithGoroutine() {
	start := time.Now()
	s := [limit + 1]float64{}
	for i := 0; i < limit; i++ {
		s[i] = x5(i + 1)
	}
	var wg sync.WaitGroup
	ch := make(chanstruct{})
LO:
	for a := 0; a < limit; a++ {
		select {
		case<-ch:
			break LO
		default:
		}
		wg.Add(1)
		t := func(a int, ch chanstruct{}) {
		L:
			for b := a; b < limit; b++ {
				for c := b; c < limit; c++ {
					select {
					case<-ch:
						break L
					default:
					}
					for d := c; d < limit; d++ {
						for e := d; e < limit; e++ {
							if s[a]+s[b]+s[c]+s[d] == s[e] {
								fmt.Println(a, b, c, d, e)
								fmt.Println("SliceWithGoroutine()", time.Since(start))
								close(ch)
								// ここでgoroutineの数を見ると結構な数が走っている// fmt.Println(runtime.NumGoroutine())break L
							}
						}
					}
				}
			}
			wg.Done()
		}

		go t(a, ch)

	}
	wg.Wait()
	// goroutine終了確認用// fmt.Println(time.Since(start))
}


const limit = 200func main() {

	CalcWithoutGoroutine()

	SilceWithoutGoroutine()

	CalcWithGoroutine()

	SliceWithGoroutine()
}
2784110133144
CalcWithoutGoroutine()  2m22.8086113s
2784110133144
SilceWithoutGoroutine()1.0819965s
2784110133144
CalcWithGoroutine()43.4378386s
2784110133144
SliceWithGoroutine()370.383ms

見にくいですが、SliceWithGoroutine() < SilceWithoutGoroutine() < CalcWithGoroutine() < CalcWithoutGoroutine() となっておりますので、 math.Pow をいちいち計算せずに計算結果の入った array を参照すると 100 倍程度、goroutine を使うと 6 コア 12 スレッドで windows11 上で golangに丸投げしても 2 - 3 倍速くなりました。正確性を問うなら benchmark を使うべきなのでしょうけれど、ここでのネタ程度ですし、桁が違うので benchmark 使わなくてもいいかなと。

goroutine 使うと 2 - 3 倍速くなるのは意外でした、とんとんか良くても 1.5 倍くらいだと。

ただ、これ主題から外れるのですが、 x5 の関数を

func x5(i int) float64 {
	f := float64(i)
	return f * f * f * f * f
}

にすると、

2784110133144
CalcWithoutGoroutine()5.552252s
2784110133144
SilceWithoutGoroutine()1.0677033s
2784110133144
CalcWithGoroutine()1.9848244s
2784110133144
SliceWithGoroutine()365.7034ms

となったので array 参照は 5 倍強、goroutine を使うと 2倍強 というところでしょうか。こちらは正確に計測したいなら benchmark 使ったほうが良さそうです。

負荷掛けるのが目的でしたら、math.Pow が適切?

ここでの runtime.NumGoroutine() は一桁のこともあれば、200 近いこともあったので割と楽しめます。


Viewing all articles
Browse latest Browse all 58

Trending Articles