LoginSignup
6
2

More than 5 years have passed since last update.

goroutineでmath/randを利用する時のマルチコアにおける注意点

Last updated at Posted at 2016-12-24

この記事ではAWS ec2上のCentOS7にインストールしたGo1.7を使っています。

とあるgoroutineを使ったGo言語のプログラムを作成して、マルチコア環境で実行してみたところ、シングルコアよりも性能が落ちてしまう、という不思議な現象に遭遇しました。

いろいろ切り分けてみると、math/randライブラリで、グローバルrandを使っていると、この現象に陥ることがわかりました。ローカルrandに書き換えると、マルチコアで順当に性能が出ます。マルチスレッドでグローバル関数を利用したために競合が起きているように思います。
シングルコアの時にgoroutineでスレッドを増やしてもこの現象は発生せず、マルチコアになると発生します。

そのものズバリを解説している記事を見つけてしまいました。
Goroutine と math/rand とベンチマークの罠

二番煎じですが、役に立つ情報かもしれませんので、ベンチマークのコードと結果を示します。

rand_test.go
package main

import (
  "testing"
  "math/rand"
)

func BenchmarkGlobal(b *testing.B) {
  c := make(chan bool, 10000)
  for i := 0; i < b.N; i++ {
    c <- true
    go func() {
      defer func() { <-c }()
      for j := 0; j < 100; j++ {
        _ = rand.Intn(100)  // use global rand
      }
    }()
  }
}

func BenchmarkLocal(b *testing.B) {
  c := make(chan bool, 10000)
  for i := 0; i < b.N; i++ {
    c <- true
    go func() {
      defer func() { <-c }()
      r := rand.New(rand.NewSource(1)) // create local rand
      for j := 0; j < 100; j++ {
        _ = r.Intn(100)   // use local rand
      }
    }()
  }
}

go test -bench . -benchmemでベンチマークを実行します。

以下に結果を表にまとめます。
AWS ec2のインスタンスタイプを変えて、ベンチマーク結果の変化を比較しています。

BenchmarkGlobal

インスタンスタイプ コア数 ns/op B/op allocs/op
t2.nano 1 6082 4 0
t2.small 1 6365 4 0
t2.medium 2 24269 28 0
t2.xlarge 4 30654 56 0
t2.2xlarge 8 22361 6 0

BenchmarkLocal

インスタンスタイプ コア数 ns/op B/op allocs/op
t2.nano 1 20738 5387 1
t2.small 1 21593 5399 1
t2.medium 2 11624 5359 1
t2.xlarge 4 6038 5410 1
t2.2xlarge 8 3112 5405 1

BenchmarkGlobalだとコア数が増えてもむしろ性能悪化してしまいます。一方、BenchmarkLocalはコア数に対してほぼリニアに性能向上しています。

参考リンク:
Goroutineの最大数を制御する方法
go言語でベンチマーク
Go言語でランダムな文字列を生成する方法の比較

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2