ISSのボトルネックになっている部分を調査すべく、Google Perftools を導入した。
どうやら、gperftoolsを使うと、
- メモリリークの検出
- ヒープの使用量
- CPUの使用量
ができるらしい。とりあえずは、CPUの使用量と、ヒープの使用量をチェックしてみようか。
CMakeLists.txt への埋め込み
まずは、CMakeLists.txtから制御できるように変更しよう。USE_PERFマクロを定義して、gperftoolsを使うかどうかを制御する。
+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
まー確かに、メモリを扱っている部分が一番メモリを食うよなあ。あとは、関数テーブルと、グローバル変数テーブルもそれなりに消費している。