技術備忘記

日々調べたこととかとりとめなく

Go1.6 パフォーマンスについて

Go1.6リリース!

去る2016/02/17にGoの1.6がリリースされました。 という訳で色々試してみようと思ったのですが、やりたかったことのほぼ100%を すでにRC版で実施されている方がいましたので有難く内容拝見させて頂きました。 良記事オススメです。 qiita.com

Go1.6 パフォーマンス周りの変更点

上の記事を読んで終わりにしてもアレなので、パフォーマンスに関して少し詳しく触れてみます。

変更点

As always, the changes are so general and varied that precise statements about performance are difficult to make. 
Some programs may run faster, some slower. On average the programs in the Go 1 benchmark suite 
run a few percent faster in Go 1.6 than they did in Go 1.5. 
The garbage collector's pauses are even lower than in Go 1.5, especially for programs using a large amount of memory.

プログラムの作りによって速くなったり遅くなったりだが、1.5に比べて平均数%速度改善し、 GCによるプログラムの停止時間が1.4 => 1.5からさらに短縮されたよ!とのことです。ふむふむ。

There have been significant optimizations bringing more than 10% improvements to implementations of the 
compress/bzip2, compress/gzip, crypto/aes, crypto/elliptic, crypto/ecdsa, and sort packages.

compress/bzip2 compress/gzip crypto/aes sort あたりに10%以上の速度改善を行ったとのこと。 特に sort は日頃から使う機会が多いので気になるところ。ということでベンチを取ってみます。

尚、動作検証時のバージョン切り替えにはgvmを使用しました。

sortパッケージの速度比較

package bench_test

import (
    "math/rand"
    "sort"
    "testing"
    "time"
)

func BenchmarkSort(b *testing.B) {
    n := 1000
    l := make([]int, n)
    for i := 0; i < n; i++ {
        rand.Seed(time.Now().UnixNano() + int64(i))
        l[i] = rand.Intn(n)
    }
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        ll := make([]int, n)
        copy(ll, l)
        sort.Sort(sort.IntSlice(ll))
    }
}

1.5.3

BenchmarkSort-5     10000        122986 ns/op        8224 B/op          2 allocs/op

1.6

BenchmarkSort-5     10000        109763 ns/op        8224 B/op          2 allocs/op

確かに10%程改善しています。

Go1.6 におけるパフォーマンスチューニング

Goのパフォーマンスチューニングと言えば、超良記事であるこちら。 qiita.com

こちらの内容が1.6になったことで何か変化があるのかついでに確認してみました。 (テストコード拝借させていただきました)

1.5.3

BenchmarkMemAllocOndemand-5           2000000           650 ns/op         496 B/op          5 allocs/op
BenchmarkMemAllocAllBeforeUsing-5   10000000           154 ns/op         160 B/op          1 allocs/op

BenchmarkFillSliceByAppend-5         5000000           342 ns/op         896 B/op          1 allocs/op
BenchmarkFillSliceByIndex-5          5000000           273 ns/op         896 B/op          1 allocs/op

BenchmarkExclusiveWithChannel-5     20000000            69.9 ns/op         0 B/op          0 allocs/op
BenchmarkExclusiveWithMutex-5       100000000           20.1 ns/op         0 B/op          0 allocs/op

BenchmarkSyncWithChannel-5            300000          4132 ns/op           0 B/op          0 allocs/op
BenchmarkSyncWithWaitGroup-5         1000000          2019 ns/op           0 B/op          0 allocs/op

BenchmarkStringMatchWithRegexp-5     3000000           430 ns/op           0 B/op          0 allocs/op
BenchmarkStringMatchWithoutRegexp-5 50000000            38.5 ns/op         0 B/op          0 allocs/op

BenchmarkGoroutine-5                 1000000          2015 ns/op           0 B/op          0 allocs/op
BenchmarkSequential-5               10000000           187 ns/op           0 B/op          0 allocs/op

BenchmarkLenDirect-5                 2000000           926 ns/op           0 B/op          0 allocs/op
BenchmarkLenCached-5                 2000000           919 ns/op           0 B/op          0 allocs/op

1.6

BenchmarkMemAllocOndemand-5           2000000           672 ns/op         496 B/op          5 allocs/op
BenchmarkMemAllocAllBeforeUsing-5   10000000           156 ns/op         160 B/op          1 allocs/op

BenchmarkFillSliceByAppend-5         5000000           360 ns/op         896 B/op          1 allocs/op
BenchmarkFillSliceByIndex-5          5000000           333 ns/op         896 B/op          1 allocs/op

BenchmarkExclusiveWithChannel-5     20000000            67.9 ns/op         0 B/op          0 allocs/op
BenchmarkExclusiveWithMutex-5       100000000           20.0 ns/op         0 B/op          0 allocs/op

BenchmarkSyncWithChannel-5            500000          3857 ns/op           0 B/op          0 allocs/op
BenchmarkSyncWithWaitGroup-5         1000000          1994 ns/op           0 B/op          0 allocs/op

BenchmarkStringMatchWithRegexp-5     3000000           409 ns/op           0 B/op          0 allocs/op
BenchmarkStringMatchWithoutRegexp-5 50000000            39.0 ns/op         0 B/op          0 allocs/op

BenchmarkGoroutine-5                 1000000          2008 ns/op           0 B/op          0 allocs/op
BenchmarkSequential-5               10000000           185 ns/op           0 B/op          0 allocs/op

BenchmarkLenDirect-5                 2000000           918 ns/op           0 B/op          0 allocs/op
BenchmarkLenCached-5                 2000000           937 ns/op           0 B/op          0 allocs/op

大きな差異は見られません。触れ込み通り速くなったり遅くなったりって感じですね。 ただし、チューニングの効果に関してはどちらのバージョンも一緒のようです。

というわけでこれまで通りの考え方で良さそうですね。

 まとめ

上記以外にも何本か既存の1.5系で書いたプログラムのテストを走らせたりしてみましたが、 特に問題は発生しませんでした。1.4 => 1.5の時もそうでしたが、今回も割とカジュアルに移行できそうな印象です。

ただし、今回は触れませんでしたがmapへの読み書きをgoroutine safeに扱ってなかったりするとpanicするという 結構ドキドキな変更が入っていたりもしますので、、移行前に一度-race を付けてテストを実施することをお勧めします (^^;)