SpECTRE Documentation Coverage Report
Current view: top level - __w/spectre/spectre/docs/DevGuide - Profiling.md Hit Total Coverage
Commit: 4d43624d64e749c1edac1bf2b22ce14141d2836f Lines: 0 1 0.0 %
Date: 2026-08-12 22:55:12
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : \cond NEVER
       2             : Distributed under the MIT License.
       3             : See LICENSE.txt for details.
       4             : \endcond
       5             : # Profiling {#profiling}
       6             : 
       7             : \tableofcontents
       8             : 
       9             : There are a number of tools available for profiling, each with their own
      10             : strengths and weaknesses. This makes it difficult to recommend one "right" way
      11             : of analyzing performance using profilers. Instead, one should use a combination
      12             : of the tools to discover and eliminate performance bottle necks. Common
      13             : profilers are Charm++ Projections (tracing-based), HPCToolkit (sampling-based,
      14             : very versatile), Linux perf (sampling-based, command line only), Intel VTune
      15             : (sampling-based, works well on Intel hardware), AMD uProf (similar to Intel
      16             : VTune), and Score-P (sampling and tracing, with MPI and pthread interfacing).
      17             : 
      18             : ## Profiling with HPCToolkit {#profiling_with_hpctoolkit}
      19             : 
      20             : Follow the HPCToolkit installation instructions at
      21             : [hpctoolkit.org](http://hpctoolkit.org). The Spack
      22             : installation seems to work well. Once installed, compile your executable in
      23             : Release mode with `-D ENABLE_PROFILING=ON -D DEBUG_SYMBOLS=ON` since otherwise
      24             : you won't be able to get call stacks and source analysis. Using `-D
      25             : BUILD_SHARED_LIBS=ON` is recommended since it makes HPCToolkit a lot easier to
      26             : use. You must also use the system allocator, `-D MEMORY_ALLOCATOR=SYSTEM`. We
      27             : will work from the build directory and perform all runs and performance analysis
      28             : there.
      29             : 
      30             : First run HPCToolkit as:
      31             : ```
      32             : hpcrun -t --event CYCLES@f200 ./bin/EXEC --input-file ./Input.yaml +p1
      33             : ```
      34             : We will profile on one core, but you can profile on multiple cores as well as
      35             : multiple nodes if using MPI as the Charm++ backend. This will generate a
      36             : `hpctoolkit-EXEC-measurements` directory. Run
      37             : ```
      38             : hpcstruct -jN ./hpctoolkit-EXEC-measurements
      39             : ```
      40             : where `N` is the number of cores to run on. This will generate a mapping to line
      41             : numbers, etc. in the measurements directory.
      42             : 
      43             : \warning Skipping the `hpcstruct` step will make `hprprof` below run extremely
      44             : slowly.
      45             : 
      46             : Once the run is complete, run
      47             : ```
      48             : hpcprof hpctoolkit-EXEC-measurements
      49             : ```
      50             : or if HPCToolkit version < 2024, run
      51             : ```
      52             : hpcprof -I /path/to/spectre/src/+ hpctoolkit-EXEC-measurements
      53             : ```
      54             : Note that the `+` is a literal `+` symbol.
      55             : 
      56             : This will create the directory
      57             : ```
      58             : hpctoolkit-EXEC-database
      59             : ```
      60             : which you can view using
      61             : ```
      62             : hpcviewer ./hpctoolkit-EXEC-database
      63             : ```
      64             : 
      65             : HPCViewer will generally start you in the `Top-down view` (callgraph of
      66             : callers). You can select  `Bottom-up view` (callgraph of callees) to get a
      67             : different perspective. Whether you want to look at the callgraph of callers or
      68             : callees depends a bit on the executable, what you're looking to measure, and how
      69             : you like to think about things. The callees graph can give you a nice overview
      70             : of what the low-level things taking up a lot of time are, but certainly makes
      71             : the call stack not look like you would expect. On the right of the callgraphs
      72             : you will see `CYCLES:Sum (I)` and `CYCLES:Sum (E)`. `I` means time spent
      73             : _including_ callees, while `E` means time spent in the function itself
      74             : (exclusive time). Sorting by exclusive gives a good idea of what the hot
      75             : functions are. Here is a screenshot from HPCViewer:
      76             : 
      77             : \image html HpcViewerCallees.png "HPCViewer callgraph of callees"
      78             : 
      79             : You can see that 49.1% of inclusive time is spent in primitive recovery, and the
      80             : line after the 49.1% function is a function inside the Kastaun
      81             : recovery scheme. The `__nss_database_lookup` is some system call,
      82             : e.g. `__memcpy_avx_unaligned_erms` or `__memset_avx2_unaligned_erms`. Looking at
      83             : the calling code, e.g. `prepare_neighbor_data` gives a good hint as to what's
      84             : going on. In most cases these are memory copies or memory sets (`std::vector`
      85             : default initializes its memory, which is bad for performance). The way to fix
      86             : these bottlenecks is to avoid memory copies and `std::vector<double>` as
      87             : buffers.
      88             : 
      89             : HPCToolkit allows you to sample on a variety of different event counters instead
      90             : of just cycles. Please see the HPCToolkit manual for details.
      91             : 
      92             : ## Profiling with AMD uProf {#profiling_with_amd_uprof}
      93             : 
      94             : [AMD uProf](https://developer.amd.com/amd-uprof/) is AMD's sampling-based
      95             : profiler that makes it relatively easy to do
      96             : quite a bit of detailed performance analysis. The uProf manual is quite good and
      97             : extensive, so for the most part the reader is referred to that. However, we will
      98             : go over some basics for profiling executables and understanding the
      99             : results. Make sure to compile your executable in Release mode with
     100             : `-DENABLE_PROFILING=ON -D DEBUG_SYMBOLS=ON` since otherwise you won't be able to
     101             : get call stacks and source analysis.
     102             : 
     103             : When you open uProf you may be asked to change the kernel event paranoid
     104             : level. Once you have uProf open, select `PROFILE` at the top. Specify the
     105             : application path, options, etc. We will again run on a single core to analyze
     106             : performance. It's recommended that you set the Core Affinity in AMD uProf so
     107             : that your application isn't migrated between cores during a profiling run. Then
     108             : choose `Next` in the lower right corner. Make sure the `CPU Profile Type` is set
     109             : to `CPU Profile` at the top. We will first do a `Time-based Sampling` run (on
     110             : the left). This means uProf will interrupt the application every `N`
     111             : milliseconds and see where the application is. You typically want a few thousand
     112             : total samples to get something that's reasonably representative of your
     113             : application. Under the `Advanced %Options` make sure `Enable CSS` (on the right)
     114             : is enabled (green) and that `Enable FPO` is also enabled. Now click `Start
     115             : Profile` in the bottom right. Once the profile is complete you will be presented
     116             : with a summary outlining where your code is spending most of its time. Click
     117             : `ANALYZE` at the top to get a more detailed view. On the left you can select
     118             : between a callgraph of callees (Function HotSpots), a callgraph of callers (Call
     119             : Graph), and a few other views. Below is an example of a result from the same run
     120             : we used with HPCToolkit above.
     121             : 
     122             : \image html AmdUprofCallgraph.png "AMD uProf callgraph of callees"
     123             : 
     124             : Again we see that most of our time is spent in primitive recovery but also that
     125             : a lot of time is spent copying memory. This was grouped into
     126             : `__nss_database_lookup` in HPCToolkit. Unfortunately, getting a call stack out
     127             : of the `memcpy` doesn't always work and so while you know you're spending a lot
     128             : of time copying memory, it's not so obvious where those copies are occurring.
     129             : 
     130             : ## Profiling With Charm++ Projections {#profiling_with_projections}
     131             : 
     132             : To view trace data after a profiling run you must download Charm++'s
     133             : Projections software from their [website](http://charm.cs.illinois.edu/).
     134             : If you encounter issues it may
     135             : be necessary to clone the git repository and build the correct version
     136             : from scratch. Note that the version of Charm++ used to compile SpECTRE
     137             : should match the version of Projections used to analyze the trace data.
     138             : You can collect the trace data on a different machine than the one you
     139             : will be analyzing the data on. For example, you can collect the data on
     140             : a supercomputer and analyze it on your desktop or laptop.
     141             : 
     142             : For profiling you will want to use a production build of Charm++, which
     143             : means compiling Charm++ with the `--with-production` flag. To enable trace
     144             : collecting you must build with the `--enable-tracing` flag as well. For
     145             : example, on a multicore 64-bit Linux machine the build command would be
     146             : ``` shell
     147             : ./build LIBS multicore-linux-x86_64 gcc -j8 --with-production --enable-tracing
     148             : ```
     149             : You must build your executable in Release mode as well, specifying
     150             : `-DCMAKE_BUILD_TYPE=Release` to CMake, as well as
     151             : ```
     152             : -DCHARM_TRACE_PROJECTIONS=ON -DCHARM_TRACE_SUMMARY=ON -DENABLE_PROFILING=ON
     153             : ```
     154             : to enable SpECTRE to use Charm++'s tracing features.
     155             : 
     156             : ### Running SpECTRE With Trace Output
     157             : 
     158             : When running SpECTRE you must specify a directory to output trace data into.
     159             : This is done by adding the command line argument `+traceroot DIR` where `DIR` is
     160             : the directory to dump the trace data into. Note that `DIR` must already exist,
     161             : the application will not create it.
     162             : For example,
     163             : 
     164             : ```shell
     165             : ./bin/EXEC --input-file ./Input.yaml +p4 +traceroot ./ExecTraces
     166             : ```
     167             : You might get a warning that Charm++ had to flush the log some number of times
     168             : during the run. Flushing the log adds overhead to the execution and so affects
     169             : timing measurements. While Charm++ has the ability to manually flush the log
     170             : periodically (and therefore exclude the time it takes to flush the log from the
     171             : trace), we have not yet implemented support for this. For short executable runs
     172             : you can increase the log size by specifying `+logsize M` when running the
     173             : executable. The default log size is 1,000,000 (1000000). Note that if you
     174             : increase the log size too much you will run out of memory/RAM.
     175             : 
     176             : For more information on runtime options to
     177             : control trace data see the
     178             : [Charm++ Projections manual](http://charm.cs.illinois.edu/manuals/html/projections/1.html).
     179             : 
     180             : ### Visualizing Trace %Data In Projections
     181             : 
     182             : By default Charm++ records entry method names by using the `PRETTY_FUNCTION`
     183             : macro. This means entry method names include all class (parallel component) and
     184             : action template parameter names, including any template parameters of the
     185             : template parameters. This very quickly leads to incomprehensibly long names that
     186             : are very difficult to read in the Projections interface. We include a basic
     187             : Python executable to handle the majority of renames, but the executable supports
     188             : additional basic (textual find-replace) and regular expression
     189             : replacements via a JSON file. These additional replacements are useful for
     190             : making executable-specific renames. The Python executable is
     191             : `tools/CharmSimplifyTraces.py` and an example replacements file is
     192             : `tools/CharmTraceReplacements.json`.
     193             : 
     194             : See the [Charm++ Projections manual](http://charm.cs.illinois.edu/manuals/html/projections/2.html)
     195             : for details.
     196             : 
     197             : ## Profiling with Score-P {#profiling_with_scorep}
     198             : 
     199             : See the
     200             : [Score-P](https://www.vi-hps.org/projects/score-p/overview/overview.html)
     201             : website for installation instructions specific to Score-P.
     202             : 
     203             : To compile SpECTRE with Score-P you must invoke CMake with the environment
     204             : variable `SCOREP_WRAPPER=off` (do NOT export this variable!) and the `scorep-`
     205             : compiler wrappers. For example,
     206             : ```sh
     207             : SCOREP_WRAPPER=off cmake -DCMAKE_C_COMPILER=scorep-gcc \
     208             :     -DCMAKE_CXX_COMPILER=scorep-g++ -DCMAKE_Fortran_COMPILER=scorep-gfortran \
     209             :     -D CMAKE_BUILD_TYPE=Release -D ENABLE_PROFILING=ON \
     210             :     -D BUILD_DOCS=OFF -D BUILD_PYTHON_BINDINGS=OFF $SPECTRE_HOME
     211             : ```
     212             : where `$SPECTRE_HOME` points to your spectre source tree. You can then compile
     213             : the executable you want to profile. When running the executable you will need to
     214             : make sure Score-P has enough memory to be able to function properly. For the
     215             : EvolveGhSingleBlackHole, Score-P needs 1.5GB of memory. To enable profiling,
     216             : disable tracing, and use 1.5GB of memory for Score-P, set the environment
     217             : variables
     218             : ```sh
     219             : SCOREP_ENABLE_PROFILING=1 \
     220             :     SCOREP_ENABLE_TRACING=0 \
     221             :     SCOREP_TOTAL_MEMORY=1536000000
     222             : ```
     223             : We also need to use a filter file to keep the data at all manageable and to
     224             : avoid some currently not understood interaction between Score-P and our option
     225             : parser. The profile is in `support/Profiling/ScoreP` and named `spectre.flt`.
     226             : The final command should look something like:
     227             : ```sh
     228             : SCOREP_ENABLE_PROFILING=1 SCOREP_ENABLE_TRACING=0 \
     229             :     SCOREP_TOTAL_MEMORY=1536000000 \
     230             :     SCOREP_FILTERING_FILE=$SPECTRE_HOME/support/Profiling/ScoreP/spectre.flt \
     231             :     mpirun -np 2 EvolveGhSingleBlackHole --input-file ./KerrSchild.yaml +ppn 91
     232             : ```
     233             : Depending on your profile, you might also need to increase the callpath depth by
     234             : setting
     235             : ```sh
     236             : SCOREP_PROFILING_MAX_CALLPATH_DEPTH=150
     237             : ```
     238             : Once you've successfully run the executable you should have a directory like
     239             : `scorep-20251006_1732_65976792745157592`. Please see the Score-P documentation
     240             : for how to analyze the output.

Generated by: LCOV version 1.14