FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Google Perftoolsを導入する

ISSボトルネックになっている部分を調査すべく、Google Perftools を導入した。

gperftools - Fast, multi-threaded malloc() and nifty performance analysis tools - Google Project Hosting

どうやら、gperftoolsを使うと、

ができるらしい。とりあえずは、CPUの使用量と、ヒープの使用量をチェックしてみようか。

CMakeLists.txt への埋め込み

まずは、CMakeLists.txtから制御できるように変更しよう。USE_PERFマクロを定義して、gperftoolsを使うかどうかを制御する。

github.com

+option (USE_PERF "Use Google Profiler" OFF)
+
 set(CMAKE_VERBOSE_MAKEFILE true)

 set (VERSION ${CMAKE_VERSION})
@@ -56,3 +58,12 @@ target_link_libraries (swimmer_mips lua5.2)
 target_link_libraries (swimmer_mips bfd)
 target_link_libraries (swimmer_mips gflags)
 target_link_libraries (swimmer_mips ${CMAKE_THREAD_LIBS_INIT})
+
+# Google Performance Analyzer
+if (USE_PERF)
+   target_link_libraries (swimmer_mips tcmalloc)
+   target_link_libraries (swimmer_mips profiler)
+endif (USE_PERF)
+
+message (STATUS "Generate config.h ...")
+message (STATUS "USE_PERF=${USE_PERF}")

cmakeでは、optionでこのような変数を制御するらしい。

cmake -DUSE_PERF=ON .

もしくは、

cmake -DUSE_PERF=OFF .

とする。ここではデフォルトがOFFなので、指定しない場合はOFFだ。

そして、config.h.inにデフォルト変数の記載をしている。

diff --git a/build_riscv/config.h.in b/build_riscv/config.h.in
index e9e8057..3879b7b 100644
--- a/build_riscv/config.h.in
+++ b/build_riscv/config.h.in
@@ -30,3 +30,5 @@

 #define VERSION "@VERSION@"
 #define REVISION "@REVISION@"
+
+#cmakedefine USE_PERF

CPUプロファイルの記述は、プログラムの中に記述している。下記のように記述すると、swimmer.profにプロファイリング結果が出力される仕組みだ。

diff --git a/src/swimmer_main.cpp b/src/swimmer_main.cpp
index 30be9ab..ab204c8 100644
--- a/src/swimmer_main.cpp
+++ b/src/swimmer_main.cpp
@@ -44,6 +44,8 @@
 #include <lua5.2/lualib.h>
 #include <lua5.2/lauxlib.h>

+#include <gperftools/profiler.h>
+
 // FLAGs definition
 DEFINE_string (hexfile, "", "Hex file to simulate.");
 DEFINE_string (binfile, "", "Binary file to simulate.");
@@ -74,6 +76,10 @@ std::string g_input_filename;

 int main (int argc, char *argv[])
 {
+#ifdef USE_PERF
+    ProfilerStart ("swimmer.prof");
+#endif // USE_PERF
+
     std::string debug_filename;

     bool is_cmd_binfile = false;
@@ -169,6 +175,10 @@ int main (int argc, char *argv[])
         env->StepSimulation (env->GetMaxCycle());
     }

+#ifdef USE_PERF
+    ProfilerStop ();
+#endif // USE_PERF
+
     return 0;
 }

USE_PERF=ONでコンパイルしたときには、make時にprofilerとtcmallocがリンクされていることが分かる。

実行してプロファイリングをする

/usr/local/bin/cmake -E cmake_link_script CMakeFiles/swimmer_mips.dir/link.txt --verbose=1
/usr/bin/c++    -O0 -g -Wall -fdiagnostics-color -g   CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/swimmer_main.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/mips_env.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/env.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/trace.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_print.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_mnemonic.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_mips_init.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_decoder.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_mips.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/inst_operand.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/dec_utils.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/memory_block.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/memory.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/lua_env.cpp.o CMakeFiles/swimmer_mips.dir/home/vagrant/swimmer_riscv/src/bfd_env.cpp.o  -o swimmer_mips  -L../vendor/gflags/lib -rdynamic -llua5.2 -lbfd -lgflags -lpthread -ltcmalloc -lprofiler -Wl,-rpath,../vendor/gflags/lib
make[2]: Leaving directory '/home/vagrant/swimmer_riscv/build_mips'
[100%] Built target swimmer_mips
make[1]: Leaving directory '/home/vagrant/swimmer_riscv/build_mips'
/usr/local/bin/cmake -E cmake_progress_start /home/vagrant/swimmer_riscv/build_mips/CMakeFiles 0

ヒーププロファイルを測定する場合は、 HEAPPROFILE=xxxを設定する。




これで、CPUプロファリングをしたswimmer.profと、ヒーププロファイリングをした/tmp/heapprofile.0001.heapが出来上がった。

CPUプロファイリングの結果を参照

プロファイリングの結果参照には、pprofを利用する。

$ pprof swimmer_mips swimmer.prof
Using local file swimmer_mips.
Using local file swimmer.prof.
Welcome to pprof!  For help, type 'help'.
(pprof) top
Total: 9 samples
       2  22.2%  22.2%        2  22.2% ExtractBitField
       2  22.2%  44.4%        2  22.2% Memory::SearchMemTable
       1  11.1%  55.6%        1  11.1% EnvBase::GetPC
       1  11.1%  66.7%        3  33.3% Memory::LoadMemWord
       1  11.1%  77.8%        1  11.1% MemoryBlock::WriteByte
       1  11.1%  88.9%        1  11.1% _IO_new_file_xsputn
       1  11.1% 100.0%        1  11.1% std::set::find
       0   0.0% 100.0%        3  33.3% EnvBase::FetchMemory
       0   0.0% 100.0%        1  11.1% EnvBase::FindPCBreak
       0   0.0% 100.0%        2  22.2% EnvBase::LoadBinary

デバッグ機能を有効にしていないから当然かもしれないが、ExtractBitFieldが一番重たいのか。SearchMetTableも、線形サーチしているから、当然かもしれない。

ヒーププロファイリングの結果を参照する。

プロファイリングの結果参照には、pprofを利用する。

$ pprof swimmer_mips /tmp/heapprofile.0001.heap
Using local file swimmer_mips.
Using local file /tmp/heapprofile.0001.heap.
Welcome to pprof!  For help, type 'help'.
(pprof) top
Total: 0.1 MB
     0.1  46.5%  46.5%      0.1  46.5% MemoryBlock::MemoryBlock
     0.0  35.4%  81.8%      0.0  35.4% _objalloc_alloc
     0.0   7.1%  89.0%      0.0   7.1% objalloc_create
     0.0   3.9%  92.8%      0.0   4.0% google::FlagRegisterer::FlagRegisterer
     0.0   1.8%  94.7%      0.0   1.8% std::string::_Rep::_S_create
     0.0   1.3%  95.9%      0.0  12.1% EnvBase::LoadFunctionTable
     0.0   1.1%  97.0%      0.0   1.1% EnvBase::EnvBase
     0.0   0.8%  97.9%      0.0   9.3% EnvBase::LoadGVariableTable
     0.0   0.7%  98.5%      0.0   0.7% __gnu_cxx::new_allocator::allocate
     0.0   0.5%  99.0%      0.0   0.5% __fopen_internal

まー確かに、メモリを扱っている部分が一番メモリを食うよなあ。あとは、関数テーブルと、グローバル変数テーブルもそれなりに消費している。