LoginSignup
6
0

More than 1 year has passed since last update.

Goの乱数生成について調べてみた(ベンチマーク)

Last updated at Posted at 2021-12-11

初めに

オークファンの開発部に2021年に新卒入社した@isodaです。
業務でGoの乱数生成を使用する機会があったので、いろいろ調べてみました。

Goの乱数生成について

GOの乱数生成には標準パッケージで、math/randとcrypto/randの2通りの方法が存在しています。

math/rand

  • 特徴
    • 乱数の元になる、シード値が必要
    • セキュリティ性が低い

crypto/rand

  • 特徴
    • 乱数の元になる、シード値が必要ない
    • 暗号的に安全な乱数ジェネレーター
    • LinuxおよびFreeBSDでは、Readerは利用可能な場合はgetrandomを使用し、それ以外の場合は/dev/urandomを使用します

ベンチマーク用のプログラム作成

Goでは標準パッケージでベンチマーク用の機能があるらしく、そちらを使用しました

今回はmath/randとcrypto/randでそれぞれ、0~100の乱数と0~9223372036854775807(Int64の最大値)で乱数を作成する関数を作成しました

func BenchmarkAppend_MathRand100(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        rand.Seed(time.Now().UnixNano())
        rand.Intn(100)
    }
}

func BenchmarkAppend_MathRandMax(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        rand.Seed(time.Now().UnixNano())
        rand.Intn(9223372036854775807)
    }
}

func BenchmarkAppend_CryptoRand_100(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        rand.Int(rand.Reader, big.NewInt(100))
    }

}

func BenchmarkAppend_CryptoRand_Max(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        rand.Int(rand.Reader, big.NewInt(9223372036854775807))
    }
}

ローカル環境で測定

ローカルマシンについて

  • MacBook Pro
    • バージョン:10.15.7
    • CPU:2.8GHz クアッドコアIntel Core i7
    • メモリ:16GB 2133MHz
go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: rand
BenchmarkAppend_CryptoRand_100-8     7543069           149 ns/op          56 B/op          4 allocs/op
BenchmarkAppend_CryptoRand_Max-8     8180403           143 ns/op          56 B/op          4 allocs/op
BenchmarkAppend_MathRand100-8         161563          7044 ns/op           0 B/op          0 allocs/op
BenchmarkAppend_MathRandMax-8         163651          6986 ns/op           0 B/op          0 allocs/op
PASS
ok      rand    5.328s

各値について

この値の見方としては、左から
関数名
実行回数
1回あたりの時間
1回あたりのアロケーションで確保した容量
1回あたりのアロケーション回数

思ったこと

  1. 0~100までの乱数と0~Int64の最大値の乱数では、実行速度にあまり差が見られませんでした。 値の大きい乱数を作る方が遅くなるのかなと思っていたので、意外でした。
  2. math/randの方が早いかなと思っていたのですが、crypto/randの方が5倍くらい早く処理が終わっています。
  3. math/randはメモリアロケーションをおこなわずに、crypto/randはメモリアロケーションを行っている様です、なのでcrypto/randの実行速度はメモリの使用状況や性能に大きく左右されそうです。

EC2上で測定

今回検証したEC2の構成

  • EC2
    • AMI:Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type
    • インスタンスタイプ:t3.micro
      • vCPU:2
      • メモリ:1GB
    • クレジット仕様
      • unlimited
go test -bench . -benchmem
goos: linux
goarch: amd64
BenchmarkAppend_CryptoRand_100-2      825255          1426 ns/op          56 B/op          4 allocs/op
BenchmarkAppend_CryptoRand_Max-2     1000000          1125 ns/op          56 B/op          4 allocs/op
BenchmarkAppend_MathRand100-2         117762         10070 ns/op           0 B/op          0 allocs/op
BenchmarkAppend_MathRandMax-2         119493         10047 ns/op           0 B/op          0 allocs/op
PASS
ok      _/home/ec2-user/rand    4.927s

思ったこと

  1. ローカルで実行した際よりも、crypto/randの時間が10倍ほど遅くなっています、やはりメモリのスペックに大きく左右される様です
  2. math/randはローカルと比べて、1.3倍ほど遅くなっている様です、crypto/randほどの差は出ない様です
  3. math/randとcrypto/randで比較すると、やはりcrypto/randの方が8~9倍近く早く処理が完了している様です

まとめ

より安全な乱数を作成できるcrypto/randの方が今回の場合では処理が早いことがわかりました。
しかしcrypto/randはメモリアロケーションを行う為、動作させる環境などによって考慮が必要です。

6
0
1

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
0