Memoエモさ駆動開発2018-09-30Golangのメモリ周りのメモ以前、このレポジトリがバズっていた。limitations.bytebufが小さなサイズのバッファ用にあらかじめ用意しているbootstrapというバイト列が、エスケープ解析の際に必ずヒープ上に確保されてしまうため、buffer構造体も本来はスタック上に確保できるはずであるが、ヒープ上にエスケープされてしまうという問題があった。64byte以下の小さなバッファであっても必ずヒープ上にアロケーションが走ってしまい、パフォーマンスが落ちていた。この件は既にパッチが当たっており、正しくエスケープがされるよう修正されているのだが、これをきっかけに「なんでヒープに確保することが重たいんだっけ」「どういうときにエスケープされるんだっけ」などのメモリ周りを調べたメモ。なんでヒープ確保が重たいのスタックの確保はとてもシンプルで、関数呼び出し時にスタックを伸ばし、関数を抜けた時点でスタックを縮める。比較して、ヒープ上へ確保する場合は、確保先の探索、GCなど様々な処理が走り、この差がパフォーマンスに効いてくる。他の関数で参照される、参照を取得する必要があるといった場合を除き、極力エスケープしないようなコードが高速に動作する。関数内のみで参照される値は、スタック上に確保されるそうでないものは、ヒープ上へ確保されるのだが、コンパイラが途中で解析を止め、エスケープする条件が複数ある。この条件は日々変更が入ったり、今回のような特定の条件下で異なる動作をする場合があるため、網羅することはできないが(本来プログラマが意識するべきではないが)、2015年の時点で以下のリンクなどにまとまっている。ドキュメントGoの公式ドキュメントを読んでも「すべての条件を説明するのは複雑すぎるので、コンパイルフラグで実際にエスケープされるか確認して」と書いてある。以下のコマンドで確認することが出来る。関係はないが、エスケープ解析に効いてくる最適化の一つに、関数のインライン化の解析がある。Goのコンパイラは、関数が以下の条件に当てはまるとき、関数を直接、呼び出し元に展開する最適化を行っている。GitHubこのインライン化がどうメモリ周りに効いてくるかというと、インライン化が無い場合、関数の引数に値を渡した時点でその値はヒープ上に確保されてしまう。インライン化された関数であれば、関数内での値の使用となるため、スタック上の確保で済む。別の記事で、この最適化を意識したコードの例を考えてみたいと思う。<p>wait0000さんは、はてなブログを使っています。<p>引用をストックできませんでした。<p>限定公開記事のため引用できません。