profiling with gperftools
速いプログラムを書くためにはどこがボトルネックになっているのか,プロファイリングをすることが重要です.
C/C++ コードのプロファイラには様々な選択肢*1があります,その中で gperftools (google performance tools) について紹介します
https://patricklam.ca/p4p/2014/notes/pdf/L16-slides.pdf
TL;DR
- gperftools では cpu と heap のプロファイルが取れる
- python 拡張ライブラリのプロファイルは yep で取る
- その他プロファイラ
gperftools によるプロファイリング
google-perftools には以下の機能がついており,コールグラフやヒープの使用量を解析できます.
- CPU profiler
- heap profiler
- heap checker
installation
$ sudo apt-get install google-perftools libgoogle-perftools-dev
(1) コードに埋め込んで使う
profiler 関数を呼び出しするヘッダをインクルードし,
#include <gperftools/profiler.h>
ProfilerStart()
でプロファイリングしたい処理を囲みます
ProfilerStart("sample.prof"); // write something ProfilerStop();
cpu_profile は libprofiler.so
を,heap_profile は libtcmalloc.so
をリンクしてやります.
どこにいるのか分からない場合は $ dpkg -L libgoogle-perftools-dev
とかして探して下さい.
$ g++ test.cc -o test -lprofiler -ltcmalloc
バイナリを実行するとsample.prof
が生成されます.
heap を調べる場合は環境変数で HEAPPROFILE
を指定すると sample.0001.heap
が生成されます
$ HEAPPROFILE=sample ./test # sample.prof # sample.0001.heap
(2) 環境変数で指定する
ProfilerStart
を埋め込まなくても,CPUPROFILE=
環境変数を指定すれば.prof
を生成できます.
CPUPROFILE=sample.prof ./test
しかし普通にリンクすると,--as-need
フラグのために libprofiler
がリンクされない(必要なライブラリと認識されない)= プロファイル結果が吐けない問題が...
$ g++ test.cc -lprofiler $ ldd a.out linux-vdso.so.1 => (0x00007ffdb718c000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f60191a4000) /lib64/ld-linux-x86-64.so.2 (0x00007f601956e000)
そこで,--as-needed
を一旦折ってからまた立てるという方法で回避します(しんどい)
$ g++ test.cc -Wl,--no-as-needed -lprofiler -ltcmalloc -Wl,--as-needed
後は同じ要領で heap と cpu のプロファイルを dumpします
HEAPPROFILE=test CPUPROILE=test.prof ./test
結果を見る
実行バイナリとプロファイルを,pprof
に渡します.text
と入れるとテキストベースでサンプル結果が,callgrind
と入れると kcahcegrind でコールグラフが見れます
$ google-pprof test test.prof Using local file a.out. Using local file test.prof. Removing __libc_start_main from all stack traces. Removing _start from all stack traces. Removing 0xffffffffffffffff from all stack traces. Welcome to pprof! For help, type 'help'. (pprof) text (pprof) callgrind
heap の使用量も同様にチェックできます
$ google-pprof test test.0001.heap
サンプリング間隔の調整
プロファイルは実行時に一定周期でサンプリングして取っています.間隔が遅すぎるとか速すぎるとかがあれば環境変数で調整することができます.
CPUPROFILE_FREQUENCY=100
Python 拡張ライブラリ(C++)のプロファイリング
以上では一般的な C++ プログラムのプロファイルの取り方を紹介しました.では,C++で記述された python 拡張ライブラリのプロファイリングはどうでしょうか.
基本は同じで,C++で作った pythonlib に libprofiler
をリンクしておけばプロファイルが取れます *2
pybind11 の例です.python 用の so ライブラリに libprofiler と libtcmalloc をリンクします
add_subdirectory(pybind11) pybind11_add_module(mylib src/python/_mylib.cc) target_link_libraries(mylib PRIVATE ${OpenCV_LIBS} profiler)
ちゃんとリンクできてることを確認しましょう.
$ ldd cvlib.cpython-35m-x86_64-linux-gnu.so linux-vdso.so.1 => (0x00007ffc571d4000) libprofiler.so.0 => /usr/lib/libprofiler.so.0 (0x00007fc72790e000)
↑の拡張ライブラリを import し,関数呼び出しをする python script を実行すると hoge.prof や hoge.0001.heap が同様に得られます
python3 test.py # sample.prof (output)
後は pprof に拡張ライブラリを渡してやれば良いです.
$ google-pprof mylib.cpython-35m-x86_64-linux-gnu.so sample.prof
yep を使う
バックエンドが google-pprof である,python bindings プロファイル用の utility ライブラリがあります.
↑で述べたことがもっと簡単にできる && python bindings 部分のプロファイルも取れる(Pyxxxx_... みたいな関数とか)のでこちらを使うのが良いでしょう.
github.com
この解説がわかりやすいのでこっちを見て下さい【完】
Profiling with yep · GitHub
プロファイルを取る前に
これを読みましょう.
Profiling Tips