藻ログ

都会でOLをしています

profiling with gperftools

https://3.bp.blogspot.com/-lnApc972VIY/WdyDRkOtDCI/AAAAAAABHbE/YdR9Y9xZ8uw0DJB59-zKVXEmqjuoeUZfACLcBGAs/s800/character_program_fast.png

速いプログラムを書くためにはどこがボトルネックになっているのか,プロファイリングをすることが重要です.
C/C++ コードのプロファイラには様々な選択肢*1があります,その中で gperftools (google performance tools) について紹介します
https://patricklam.ca/p4p/2014/notes/pdf/L16-slides.pdf

TL;DR

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

KCachegrind

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

*1:perf, gprof, Vtune※課金

*2:拡張ライブラリで CUDAを呼び出している場合も同じで,python スクリプトごと nvprof にかけられる