straceprof——とにかく簡単にビルドのプロファイルを取るためのソフトウェア

三行まとめ

とりあえず使ってみたい人向けのコマンド

$ sudo apt-get install strace
$ pip install straceprof
$ strace \
    --trace=execve,execveat,exit,exit_group \
    --follow-forks \
    --string-limit=1000 \
    --absolute-timestamps=format:unix,precision:us \
    --output=straceprof.log \
    <command to profile>
$ straceprof \
    --log=straceprof.log \
    --output=straceprof.png

ソフトウェアのビルドのプロファイル

仕事でもプライベートでも少し規模の大きいソフトウェアをビルドする機会がかなりの頻度であります。 例えば、ビルドしているのは、Linux カーネルJuliaPyTorchglibc といったソフトウェアです。 これらのソフトウェアは僕のPC1でフルビルドに5分から30分程度かかります。 差分ビルドであってもフルビルドほどではないにせよそれなりの時間がかかります。

このビルド時間は少し変更を入れて動作確認するたびにかかるので、作業効率を上げるためにはこれを縮めることが大変重要です。 ビルド時間を短縮するというのは一種のパフォーマンスチューニングです。 このため、ビルドプロセス全体のプロファイルがとり、ボトルネックを特定する必要があります。

ここで、ビルドプロセスのプロファイルをとるのが難しい場合が多い、という問題があります。 ソフトウェアのビルドに使われているツールは多様で、しかもそれらがシェルスクリプトや Dockerfile などで組み合わされていることがあります。 個々の CMakecargo といったツールにはプロファイラがあるかもしれませんが、結局知りたいのはビルドプロセス全体の所要時間なので全体をまとめてプロファイルできる必要があります。

さらに、ソフトウェアがビルドされる環境は必ずしもプロファイルしやすい場所ではありません。 よくあるのは CI でビルドしている場合でしょう。 Github Action 上などで perfperfetto などのリッチなプロファイラを設定するのはそれ自体が骨の折れる作業ですし、必要な権限がなくそもそも動かすのが不可能である可能性もあります。 また、ビルド環境を完全に固定するために Docker などのコンテナ環境を利用している場合は、プロファイラのセットアップがさらに面倒になります。

straceprof

straceprof はとにかくお手軽にビルドプロセス全体のプロファイルをとるためのツールです。 プロファイルをとるのに必要なのは strace コマンドだけです。 このコマンドはほとんどすべての Linux ディストリビューションで簡単にインストールすることができます。

まず、strace コマンドをつけてビルドしてプロファイルを取ります。

$ strace \
    --trace=execve,execveat,exit,exit_group \
    --follow-forks \
    --string-limit=1000 \
    --absolute-timestamps=format:unix,precision:us \
    --output=straceprof.log \
    <command to profile>

次に、この出力を straceprof コマンドに渡すとプロファイル結果が可視化されます。簡単ですね!

$ straceprof \
    --log=straceprof.log \
    --output=straceprof.png

仕組み

strace というコマンドがあります。 このコマンドを利用すると、プロセスが発行する全てのシステムコールを監視し、ファイルに記録することができます。

また、Linux では多くのプロセスが execve(2)システムコールで開始され、exit_group(2)で終了します2。 このため、straceで各プロセスのexecve(2)exit_group(2)の時間を記録すれば、そのプロセスの所要時間を割り出すことができます。

straceprofstrace の出力をパースすることで各プロセスの所要時間を割り出し、matplotlib を使って画像を出力します。 画像の縦方向には意味がなく、可能な限り画像サイズが小さくなる順番にプロセスが並んでいます。

使用例

Julia のビルド

Julia というプログラミング言語をフルビルドしたプロファイラ結果がこちらです。 ビルドを開始してから140秒から320秒の間、compiler.jlsys.jlの処理を行っており、これが全体のボトルネックです。 この処理を高速化できれば、ビルド時間を短縮することができます。

Linux カーネルのビルド

僕がよく使っている Linux カーネルをビルドするスクリプト のプロファイル結果がこちらです。 C言語のコンパイルが並列に行われており、明確なボトルネックはありません。

お願い

https://github.com/akawashiro/straceprof にスターをつけてください。

  1. CPU: AMD Ryzen 9 5950X、メモリ: 64 GiByte 

  2. そうでない場合もありますが、ビルド中に起動されるほとんどのプロセスはコンパイラやリンカであり、このように考えて問題ないと考えています。