2月にリリースが予定されているGo 1.6。1/28にRC1が出て、秒読み状態となっている。幾つか新しい機能や拡張・改善が入っているようなので少し見てみた。なお、ここでの内容はGo 1.6 Release Notes DRAFTを参考にしている。まだドラフトなのでこれから変わる可能性も当然あるが、RCまで来ているのでそれほど大きな変更はないと期待し、そこから気になる項目を幾つかピックアップして自分なりに解釈してみる。
なお、動作の確認にはgobrewを使った。導入とセットアップはそのGithubページにかかれているが、日本語で読みたいという方はこのQiitaの記事も合わせてどうぞ。
##はじめに
Go 1.5が出たのが2015年8月なので約6ヶ月ぶり。1.4から1.5が8ヶ月だったのが元のペースに戻ったということか。言語仕様の変更はなく、以前のプログラムも正しく動くように互換性が保たれている。それはここでお約束されいてる通り。
サポートプラットフォームとして MIPS64とか32bitのAndroidとかExperimental(試験的)として追加されているが使う予定はないなぁ。MIPSは思い入れのあるアーキテクチャだけど手短なプロダクトは無くなってしまったし。逆に誰がどんな目的でポーティングしているのか少し気になるところ。
##ツール
###Cgo
大きな変更として「GoのポインタをCのコードで使えるようにルールを決めた」とある。GoはGarbage Collectionな言語なので、C側で使っているのにGoのGCが勝手にゴミ集めして、結果的に予測不能な挙動が起きることがある。実際、以前CのライブラリをGoから使っていてそんなバグに悩まされたことがあった。で、ここでのルールは「GoからCへ渡されるメモリ領域に、Goのポインタを持たない」と「渡されたポインタをC側で保持しない」。そして単にルールを決めただけでなく、それをチェックする機構をランタイムに組み込んであって、違反するとメッセージを出してクラッシュするという! 面白そうだったので、ちょっと試してみたがうまくそのチェック機構を働かせることが出来なかった。
###コンパイラツールチェイン
ほとんど変更なしだが、パーサをYaccから生成されたモノからお手製のモノに置き換えたらしい。パフォーマンスの向上が期待できそう。あとは、Clangのメモリサニタイザー(メモリリークの動的チェック)をONにするオプションとか。
###Gccgo
gccのGo言語フロントエンドであるgccgoだが、本家のアップデート速度についていけていないらしい。現状最新のgcc 5.xでGo1.4相当。これが次期リリースのgcc 6でGo 1.5。Go 1.6になるのはgcc 7の頃と言っているが、それっていつのことになるやら。
###Goコマンド
Go1.5で試験的に導入されたvendoringの機能が、環境変数(GO15VENDOREXPERIMENT)を設定せずとも使えるようになったとのこと。Venderingはソースツリーの下に置いた外部ライブラリをあたかも通常のライブラリの用に使える(そしてそちらを優先する)という仕組みらしい。
###その他ツール関係
go docでのパッケージ探索が辞書順じゃなくインポートパスの順になったり、go vetでのチェックの強化などが行われたり。Printfの引数でf()と書く所をfと書いてしまった場合などを見つけてくれる。
##性能改善
プログラムによって速くなったり遅くなったりだけど、平均でGo1.5から数%の改善。そして、ガベージコレクションによる一時停止が1.5よりもさらに短くなったとのこと。実際に1.6rc1を試してみた人のツイートをみると確かに改善している!
##コアライブラリ
###HTTP/2
HTTP/2プロトコルのサポートがnet/httpに組み込まれ、1と2のプロトコルの差を意識すること無く使える。相手がHTTP/2を話せればそれで、ダメならHTTP/1.1でという動きをするらしい。ほとんどの場合でgolang.org/x/net/http2を使う必要はなくなると思うが、HTTP/2特有のパラメータ調整をする場合はhttp2をインポートしてという段取りになる。
これは少し試してみた。サンプルプログラムはこちら。
package main
import "net/http"
import "fmt"
func main() {
resp, err := http.Get("https://nghttp2.org/")
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
defer resp.Body.Close()
fmt.Printf("Proto: %s\n", resp.Proto)
}
そしてこれをGo1.5.3とGo1.6rc1で実行してみる。
$ gobrew use 1.5.3
$ go run main.go
Proto: HTTP/1.1
$ gobrew use 1.6rc1
$ go run main.go
Proto: HTTP/2.0
ほほう、同じコードでも1.6はHTTP/2での接続をしてくれているようだ。でも、これって非互換な変更じゃないのかな。。。
###ランタイム
マップへの同時アクセスを検知する機能が追加された。一つのgoroutineが書込している最中に別のgoroutineが同時に読み出しあるいは書き込みを行った場合にはエラーを出してクラッシュするらしい。ただし、このチェックは軽量でBest-Effort(できるだけ頑張るけど完璧じゃない)なので、より詳細にチェックしたい場合にはrace detectorを使ってね、と書いてある。
これも少し試してみた。mapに同時アクセスするこんなプログラム。
package main
import "fmt"
func main() {
amap := make(map[int]int)
go func() {
for i := 0; i < 1000; i++ {
amap[i] = i + 1
}
}()
for i := 0; i < 1000; i++ {
fmt.Printf("%d\n", amap[i])
}
}
これを実行すると、1.5.3では何もでないのに、1.6rc1では "fatal error: concurrent map read and map write" というのが出る。ふむふむ。
その他では以下の様な項目が挙げられている
- クラッシュした時のスタックトレースを実行中のgoroutineのモノだけにした。これで視認性が高まる。これまでの様に他のgoroutineのも出したければ環境変数GOTRACEBACKをallにするか、debug.SetTraceback("all")しておく。
- Windowsでタイマーのresolutionを1msにするのを止めました
- Cのライブラリとしてコンパイルした時のシグナルの扱い方を変更
###Reflect
gcとgccgoの間で長い間、非互換だった問題を解決。
###ソート
書きなおされて、InterfaceのLessとSwapメソッドの呼び出し回数を10%程度減らした。
###テンプレート
- テンプレートの周りの余白を削除する機能を追加 → テンプレートの見やすさ向上
- テンプレートの一部を上書きして再定義できる機能を追加
###その他ライブラリの変更
たくさんあり過ぎて追えないのでこちらを眺めて自分の使っているライブラリがあるかどうかをご確認あれ。
##終わりに
ということで、Go1.6の新機能に関してざっと眺めてみました。Go言語を使っているプロジェクトはここにまとめられてますが、なんだかんだで増えてきています。そして数の問題ではなく、例えば Docker みたいな注目のプロジェクトでも使われ始めていますが、今回の1.6も正常進化が進んだかなという印象でした。