Island Life

< 修正14条と在住外国人の権利 | せりふを覚える >

2017/02/21

GC切って性能向上

Instagramで、PythonのGCをdisableすることにより10%性能向上させた、という話が非常におもしろかった。一般的にGCに時間を取られていたからということではなくて、Pythonの設計とInstagramの運用の両方に関わる特殊な事情によるものなんだけど、言語ランタイム設計の際の参考になる。

要点。

  • サーバはサブプロセスをたくさんpreforkしている。forkしても書き込まなければメモリは親プロセスと共有されるはず。ところがfork直後にがくんと共有ページが減っている。
    • オブジェクトはヘッダのリンクトリストで繋がれてるが、GCがそのヘッダに触るために、copy on writeされてページが共有されなくなってた
    • GCを切ることで共有されるページがずっと増えてメモリに余裕ができ、より多くのワーカープロセスをpreforkできるようになった
  • ところがサーバのリスタートがむちゃくちゃ遅くなった
    • Pythonは終了時のファイナライズでGCが走る (グローバルGCはdisableしてるはずだから、それとは別にファイナライズのためにlive objectをなめるってことかな?)。そこでオブジェクトに触りまくるのでまたcopy on writeしまくり、キャッシュバッファがフラッシュされてしまっていた。
    • 後始末はatexitで明示的に指示したものだけやってくれればいいんで、atexitハンドラの最後にos._exit呼んでPython自身の後始末部分をスキップ
  • 「GCを切る」と言ってもグローバルのmark-and-sweepを切るだけでリファレンスカウンティングによる回収は行われるので、彼らの運用ではそれで十分

感想。

  • ワーカープロセス間のページ共有という観点は今まで意識してなかった。ただ、不要なCoW回避については、リファレンスカウンタやリンクトリストをオブジェクト本体とは別ページで管理するって手もあるんではないか。Boehm GCではmark bitをオブジェクトとは別に管理して、性能的にも有利と出ていたはず。ただ、リファレンスカウンタは頻繁に触るので、分けてしまうと触る場所が増えて却って良くないかも。
  • Pythonインタプリタ終了時にGCが走る、というのがちょっと意外だった。ファイナライザの実行を保証するため? 自分の感覚では、ファイナライザは他のリソースクリーンアップ手段から漏れた場合を捕捉してリソースリークを防ぐもので、プロセス終了時に必要なクリーンアップを任せるものじゃないって認識だなあ。Gaucheでも終了時にファイナライザが走ることは保証してない。

Tag: Programming

Past comment(s)

IKeJI (2017/02/22 05:52:02):

Rubyも2.0からGC用のマークを別に持つようにしてました。 http://magazine.rubyist.net/?0041-200Special-gc

shiro (2017/02/22 16:24:51):

GC時しか必要としないmarkを分けるのは合理的だと思います。元記事で触れられている、PythonでGC時にシャッフルされるリンク構造というのも、GC時にしか使わないのなら別ページで管理すればいいんじゃないかなと思いますが、他にも使われるのかもしれません。ただ、リファレンスカウンタは頻繁に書き換えるのでページを分けるメリットは出るでしょうか。

Post a comment

Name: