Go Conference 2014 Autumnレポート

Go Conference 2014 Autumn,その他のセッションのまとめ

この記事を読むのに必要な時間:およそ 5 分

前回の記事では,3つのセッションについてレポートを記しました。この記事では,残りのセッションについてまとめたいと思います。

Why my Go program is slow?@methane氏

このセッションでは,@methane氏からpprofというCPUプロファイラとGo言語におけるパフォーマンスチューニングについての話がされました。なお,このセッションの発表資料は,以下のURLから閲覧することができます。

写真1 @methane氏の発表の様子

写真1 @methane氏の発表の様子

サンプリングプロファイラ

pprofサンプリングプロファイラで,一定時間ごとにCPUがどこを処理しているかというサンプルを取ることでプロファイリングを行うそうです。そのため,多くのサンプルが取れた関数は時間がかかっているということがわかります。同氏は,サンプリングプロファイラは,モンテカルロ法に似ていると述べていました。サンプリングプロファイラに対して,決定論的なプロファイラというものが存在するらしいのですが,そちらは関数の始まると終わりにタイマーを仕掛けておき,それを集計することでプロファイリングを行うそうです。そのため,精度は高いそうですが,プロファイリングの処理自体が重たいため,計測対象のプログラムの実行速度に影響を与えてしまうそうです。一方,サンプリングプロファイラは,実行速度への影響は少なく,プロダクト版のプログラムに導入しても問題ないと同氏は述べていました。

用途に適したパッケージを使う

同氏は,デーモン型のプログラムや長時間動くプログラムでは,runtime/pprofを直接使用するのではなく,net/http/pprofを使用すると良いと主張していました。なお,net/http/pprofは以下のようにimportするだけで使えるそうです。

import (
    "net/http"
    _ "net/http/pprof"
)

func main() {
    go http.ListenAndServe(":5000", nil)
}

また同氏は,処理時間の短いコマンドラインツールなどは,davecheney/profileを使うと良いと述べていました。

Macでサンプリングプロファイラを使用するためには

pprofSIGPROFというシグナルを送る機能を利用しているそうですが,Macではマルチスレッドプログラムでその機能がうまく動かないため,pprofを利用できないそうです。この件の詳細は,Go言語チームのRuss Cox氏の記事を見ると良いそうです。Russ Cox氏の記事では,Macのカーネルにパッチを当ててpprofできるようにしているそうです。なお,カーネルにパッチを当てるのは大変危険なので,元に戻せる環境が無い方は試さないほうが良さそうです。

methane氏は,Mac上でサンプリングプロファイラを利用したい場合は,アクティビティモニタやInstrumentsを利用すると良いと述べていました。

go tool pprof

runtime/pprofおよびnet/http/pprofでプロファイリングした結果は,go tool pprofを使って解析することができるそうです。元々このツールは,Googleが開発しているC/C++向けのperftoolsを利用していたそうですが,Go1.4からはGo言語で書き直されているそうです。Go1.3までの使い方と多少異なるようですが,動作も安定してきているそうなのでGo1.4のgo tool pprofを使うことを同氏は推奨していました。このセッションでは,同氏が実際にpprofを使ってプロファイリングをするデモが行われました。pprofの使い方については,以下の記事が参考になります。

Go言語で書かれたプログラムが遅くなる理由

同氏は,Go言語で書かれたプログラムが遅くなる原因として,低レイヤーの話では以下の3つが多いと述べていました。

  • ガベージコレクション(GC)
  • メモリコピー
  • 関数呼び出し
ガベージコレクション(GC)

GCが重い場合は,GODEBUG=gctrace=1というデバッグフラグを付けるとGCの情報が見られるそうです。また,pprofのヒープのプロファイラを使うと,どこでどれだけメモリ確保しているかが分かるため,メモリを多く確保しているところを見つけることができるそうです。そしてその部分で,Go1.3で入ったsync.Poolを使ってオブジェクトをプールしておき,メモリを確保する回数を減らすと良いそうです。

また,作成するスライスやマップの大きさが分かっている場合は,十分なキャパシティをmake関数で指定すると,要素を追加したときにメモリ確保が発生しないので速いとのことでした。さらに,GOGCという環境変数の値を大きくすることで,GCの負荷を減らし,早めにヒープを増やすと言うISUCON向けのチューニング方法も説明されていました。

なお,同氏の記事で,ISUCON4の予選のチューニング方法についてくわしく解説されています。

メモリコピー

string型で持っている値を実際に使う際に頻繁に[]byte型に変換
する場合,その度にメモリコピーが発生してしまうので注意したほうが良いと同氏は指摘していました。そのためそのような場合には,はじめから[]byte型で値を持つと良いと述べていました。また,net/textprotoパッケージのReader.ReadMIMEHeaderで行われているテクニックについての説明がされていました。

なお,Reader.ReadMIMEHeaderについては同氏の記事で詳しく解説されています。

関数呼び出し

同氏は以下の理由により,Go言語の関数呼び出しはC言語の関数呼び出しとくらべて遅いと主張していました。

  • 引数や戻り値がレジスタではなくスタック渡しであるため
  • 呼び出す側が使用しているすべてのレジスタを退避させる必要があるため
  • 関数呼び出し時にゴルーチンの切り替えやGCのストップザワールドのために起こるランタイムのフックがあるため

一方で,Go言語では十分に小さい,葉の関数はインライン展開されると同氏は述べていました。

著者プロフィール

上田拓也(うえだたくや)

KLab(株)所属。Go Conference 2014 Autumnスタッフ。

業務ではJavaScript,Luaなどを扱っている。

大学時代にGo言語に出会い,それ以来Go言語にのめり込む。

時間があると,Go言語の勉強会に参加している。

Go言語のマスコットのGopherの絵を描くのも好き。

Twitter:@tenntenn

コメント

コメントの記入