Last update: 2025/1/5
今回はgprofでプログラムを解析するときの手順をメモしておく。解析するプログラムの題材として以前つくったモンテカルロ法で円周率を求めるC++のコードを使う。
まずは、-pgオプションをつけてソースコードをコンパイルする。※-pgオプションをつけることでパフォーマンスが低下することがあるので、オプションをつけるのは解析するときだけにする
$ g++ -pg pi.cpp
この状態でコンパイルしたプログラムを実行する。
$ ./a.out 10000000
すると、以下のようにgmon.outという解析用のバイナリファイルが出力される。
$ ls
a.out gmon.out pi.cpp
さきほど出力されたgmon.outを使い、以下のようにコマンドを打つ。デフォルトでは結果がコンソール画面に表示されるので、以下のようにtxtファイルに出力したりlessなどとパイプすると確認しやすい。
$ gprof ./a.out gmon.out > gmon.txt
実行結果として大きく分けて2種類の結果が表示される。まず1つめは以下のFlat profile。
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
53.64 0.29 0.29 64103 0.00 0.00 std::mersenne_twister_engine::_M_gen_rand()
19.09 0.40 0.10 20000000 0.00 0.00 double std::generate_canonical >(std::mersenne_twister_engine&)
9.09 0.45 0.05 40000000 0.00 0.00 std::mersenne_twister_engine::operator()()
3.64 0.47 0.02 20000000 0.00 0.00 std::uniform_real_distribution::param_type::b() const
3.64 0.49 0.02 20000000 0.00 0.00 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&)
3.64 0.51 0.02 20000000 0.00 0.00 std::__detail::_Adaptor, double>::_Adaptor(std::mersenne_twister_engine&)
2.73 0.53 0.01 20000000 0.00 0.00 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&, std::uniform_real_distribution::param_type const&)
1.82 0.54 0.01 40000000 0.00 0.00 std::uniform_real_distribution::param_type::a() const
1.82 0.55 0.01 _init
0.91 0.55 0.01 2 2.50 2.50 bool __gnu_cxx::__is_null_pointer(char const*)
…以下略…
各項目の意味も出力されるので説明は省くが、この結果からプログラムの実行の時間を占めている関数がわかり、プログラムのボトルネックを調べることができる。今回のプログラムだと擬似乱数生成エンジンの一部であるstd::mersenne_twister_engine<...>::_M_gen_rand()が実行時間の半分以上を占めていることがわかる。
また、以下のようなコールグラフも出力される。
granularity: each sample hit covers 4 byte(s) for 1.82% of 0.55 seconds
index % time self children called name
[1] 98.2 0.00 0.54 main [1]
0.02 0.52 20000000/20000000 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&) [2]
0.00 0.00 1/2 std::__cxx11::basic_string, std::allocator >::basic_string >(char const*, std::allocator const&) [14]
0.00 0.00 1/1 std::random_device::random_device() [15]
0.00 0.00 1/1 std::__cxx11::stoi(std::__cxx11::basic_string, std::allocator > const&, unsigned long*, int) [39]
0.00 0.00 1/1 std::random_device::operator()() [34]
0.00 0.00 1/1 std::mersenne_twister_engine::mersenne_twister_engine(unsigned long) [36]
0.00 0.00 1/1 std::uniform_real_distribution::uniform_real_distribution(double, double) [38]
0.00 0.00 1/1 std::random_device::~random_device() [33]
-----------------------------------------------
0.02 0.52 20000000/20000000 main [1]
[2] 97.3 0.02 0.52 20000000 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&) [2]
0.01 0.50 20000000/20000000 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&, std::uniform_real_distribution::param_type const&) [3]
-----------------------------------------------
0.01 0.50 20000000/20000000 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&) [2]
[3] 93.6 0.01 0.50 20000000 double std::uniform_real_distribution::operator() >(std::mersenne_twister_engine&, std::uniform_real_distribution::param_type const&) [3]
0.00 0.45 20000000/20000000 std::__detail::_Adaptor, double>::operator()() [4]
0.02 0.00 20000000/20000000 std::__detail::_Adaptor, double>::_Adaptor(std::mersenne_twister_engine&) [9]
0.02 0.00 20000000/20000000 std::uniform_real_distribution::param_type::b() const [8]
0.01 0.00 40000000/40000000 std::uniform_real_distribution::param_type::a() const [10]
-----------------------------------------------
…以下略…
Call Graphを可視化するツールもあるようなので、また別の機会に試してみたいと思う。
セミナー資料 公開ページ | HPCI:チューニング技法入門: Part 1 チューニングの基礎
タダでソフト開発の生産性と品質を上げる方法(10):プログラムの実行速度を瞬時に測定する「gprof」:山浦恒央の“くみこみ”な話(100)
Copyright (c) 2024 kd