18  Profiling

Profiling tools and memory events tracking.

18.1 MPI Calls

You can use the mpiP tool (https://github.com/LLNL/mpiP) to get statistics about the MPI calls in IPPL.

To use it, download it from Github and follow the instructions to install it. You may run into some issues while installing, here is a list of common issues and the solution: - On Cray systems “MPI_Init not defined”: This I fixed by passing the correct Cray wrappers for the compilers to the configure: ./configure CC=cc FC=ftn F77=ftn - If you have an issue with it not recognizing a function symbol in Fortran 77, you need to substitute the line echo "main(){ FF(); return 0; }" > flink.c (line 706) in the file configure.ac by the following line echo "extern void FF(); int main() { FF(); return 0; }" > flink.c - During the make all, if you run into an issue of some Testing file not recognizing mpi.h, then you need to add the following line CXX = CC in the file Testing/Makefile.

If the installation was successful, you should have the library libmpip.so in the mpiP directory.

To instument your application with the mpiP library, add the following line to your jobscript (or run it in your command line if you are running locally/on an interactive node): export LD_PRELOAD=$[path to mpip directory]/libmpiP.so To pass any options to mpiP, you can export the variable MPIP with the options you want. For example, if you would like to get a histogram of the data sent by MPI calls (option -y), you would need to add the following line to your jobscript: export MPIP="-y"

If you application has been correctly instrumented, you will see that mpiP has been found and its version is printed at the top of the standard output. At the end of the standard output, you will get the name of the file containing the MPI statistics: Storing mpiP output in ...

To get a total amount of bytes moved around by your application, you can use the python script mpiP.py (found in the top level IPPL directory) in the following form: python3 mpiP.py [path/to/directory] where path/to/directory refers to the place where the .mpiP output can be found. This python script will then print out the total amount of Bytes moved by MPI in your application.

18.2 Profiling on LUMI

18.2.1 rocprof

Analysis with: https://ui.perfetto.dev/

#!/bin/bash -l
#
#SBATCH --job-name=opalx1
#SBATCH --error=opalx-%j.error
#SBATCH --output=opalx-2-%j.out
#SBATCH --time=00:05:00
#SBATCH --partition=standard-g
#SBATCH --nodes 1
#SBATCH --ntasks-per-core=1
#SBATCH -c 56 --threads-per-core=1
#SBATCH --ntasks-per-node=1
#SBATCH --gpus-per-node=8
#SBATCH --account=project_465001705 
#SBATCH --hint=nomultithread
#SBATCH --hint=exclusive
CPU_BIND="map_cpu:49,57,17,25,1,9,33,41"
export MPICH_GPU_SUPPORT_ENABLED=1
 
ulimit -s unlimited
export EXE_DIR=/users/adelmann/sandbox/opalx/build/src/
module load cray-python/3.11.7 
module use /appl/local/containers/test-modules
module load LUMI/24.03 partition/G cpeAMD rocm/6.1.3 buildtools/24.03

cat << EOF > select_gpu
#!/bin/bash
export HIP_VISIBLE_DEVICES=\$SLURM_LOCALID
exec \$*
EOF
chmod +x ./select_gpu
srun ./select_gpu rocprof --hip-trace ${EXE_DIR}/opalx input.in --info 5
rm -rf ./select_gpu

18.2.2 omniperf (do not use omnitrace)

doc url: https://rocm.docs.amd.com/projects/rocprofiler-compute/en/docs-6.2.4/how-to/profile/mode.html

#!/bin/bash -l
#
#SBATCH --job-name=opalx1
#SBATCH --error=opalx-%j.error
#SBATCH --output=opalx-2-%j.out
#SBATCH --time=00:05:00
#SBATCH --partition=standard-g
#SBATCH --nodes 1
#SBATCH --ntasks-per-core=1
#SBATCH -c 56 --threads-per-core=1
#SBATCH --ntasks-per-node=1
#SBATCH --gpus-per-node=8
#SBATCH --account=project_465001705 
#SBATCH --hint=nomultithread
#SBATCH --hint=exclusive
CPU_BIND="map_cpu:49,57,17,25,1,9,33,41"
export MPICH_GPU_SUPPORT_ENABLED=1
 
ulimit -s unlimited
export EXE_DIR=/users/adelmann/sandbox/opalx/build/src/
module load cray-python/3.11.7 
module use /appl/local/containers/test-modules
module load LUMI/24.03 partition/G cpeAMD rocm/6.1.3 buildtools/24.03
module load omniperf
cat << EOF > select_gpu
#!/bin/bash
#export ROCR_VISIBLE_DEVICES=\$SLURM_LOCALID
export HIP_VISIBLE_DEVICES=\$SLURM_LOCALID
exec \$*
EOF
chmod +x ./select_gpu
srun ./select_gpu omniperf profile --name opalx  --roof-only --kernel-names -- ${EXE_DIR}/opalx input.in --info 5
rm -rf ./select_gpu

18.3 Kokkos Profiling Tools (MemoryEvents)

!Ported from old Doxygen, review required!

In certain applications, you might want to use profiling tools for debugging and testing. Since IPPL uses Kokkos as a backend, you can leverage Kokkos’ built-in profiling tools.

This guide explains how to use Kokkos’ profiling tools, using the MemoryEvents tool as an example.

18.3.1 Description of MemoryEvents

MemoryEvents tracks a timeline of allocation and deallocation events in Kokkos Memory Spaces. It records time, pointer, size, memory-space-name, and allocation-name. This is in particular useful for debugging purposes to understand where all the memory is going.

Additionally, the tool provides a timeline of memory usage for each individual Kokkos Memory Space.

The tool is located at: https://github.com/kokkos/kokkos-tools/tree/develop/profiling/memory-events

18.3.2 1. Clone the Kokkos Tools Repository

First, clone the Kokkos tools repository, which contains a variety of profiling tools:

git clone https://github.com/kokkos/kokkos-tools

18.3.3 2. Build and Install the Tools

Navigate into the repository and build the tools using CMake:

cd kokkos-tools
cmake ..
make -j
sudo make install

18.3.4 3. Set Up the Profiling Tool

Before running your application, export the Kokkos Tools environment variable to point to the kp_memory_events.so tool:

export KOKKOS_TOOLS_LIBS={PATH_TO_TOOL_DIRECTORY}/kp_memory_events.so 

Replace {PATH_TO_TOOL_DIRECTORY} with the actual path where the tool is located.

18.3.5 4. Run your Application

Execute your application normally. The MemoryEvents tool will automatically collect data during execution. For example:

./application COMMANDS

18.3.6 5. Output Files

The MemoryEvents tool will generate the following files:

  • HOSTNAME-PROCESSID.mem_events: Lists memory events.
  • HOSTNAME-PROCESSID-MEMSPACE.memspace_usage: Provides a utilization timeline for each active memory space.

18.3.7 6. Example on with SLURM

Here’s an example of how to run the profiling with a SLURM system using sbatch:

sbatch -n 2 --wrap="export KOKKOS_TOOLS_LIBS=$HOME/kokkos-tools/kp_memory_events.so; \
mpirun -n 2 LandauDamping 128 128 128 10000 10 FFT 0.01 LeapFrog --overallocate 2.0 --info 10"

In this example:

  • sbatch -n 2 specifies 2 nodes.
  • The Kokkos tool is exported and applied to the LandauDamping application.

This guide provides the basic steps for integrating Kokkos profiling tools into your IPPL-based projects. You can adjust the commands as needed depending on your specific application and environment.

18.3.8 Example

Consider the following code:

#include <Kokkos_Core.hpp>

  typedef Kokkos::View<int*,Kokkos::CudaSpace> a_type;
  typedef Kokkos::View<int*,Kokkos::CudaUVMSpace> b_type;
  typedef Kokkos::View<int*,Kokkos::CudaHostPinnedSpace> c_type;

int main() {
  Kokkos::initialize();
  {
    int N = 10000000;
    for(int i =0; i<2; i++) { 
      a_type a("A",N);
      {
        b_type b("B",N);
        c_type c("C",N);
        for(int j =0; j<N; j++) {
          b(j)=2*j;
          c(j)=3*j;
        }
      }
    }
  }
  Kokkos::finalize();

}

This will produce the following output:

HOSTNAME-PROCESSID.mem_events

# Memory Events
# Time     Ptr                  Size        MemSpace      Op         Name
0.311749      0x2048a0080            128   CudaHostPinned Allocate   InternalScratchUnified
0.311913     0x2305ca0080           2048             Cuda Allocate   InternalScratchFlags
0.312108     0x2305da0080          16384             Cuda Allocate   InternalScratchSpace
0.312667     0x23060a0080       40000000             Cuda Allocate   A
0.317260     0x23086e0080       40000000          CudaUVM Allocate   B
0.335289      0x2049a0080       40000000   CudaHostPinned Allocate   C
0.368485      0x2049a0080      -40000000   CudaHostPinned DeAllocate C
0.377285     0x23086e0080      -40000000          CudaUVM DeAllocate B
0.379795     0x23060a0080      -40000000             Cuda DeAllocate A
0.380185     0x23060a0080       40000000             Cuda Allocate   A
0.384785     0x23086e0080       40000000          CudaUVM Allocate   B
0.400073      0x2049a0080       40000000   CudaHostPinned Allocate   C
0.433218      0x2049a0080      -40000000   CudaHostPinned DeAllocate C
0.441988     0x23086e0080      -40000000          CudaUVM DeAllocate B
0.444391     0x23060a0080      -40000000             Cuda DeAllocate A

HOSTNAME-PROCESSID-Cuda.memspace_usage

# Space Cuda
# Time(s)  Size(MB)   HighWater(MB)   HighWater-Process(MB)
0.311913 0.0 0.0 81.8
0.312108 0.0 0.0 81.8
0.312667 38.2 38.2 81.8
0.379795 0.0 38.2 158.1
0.380185 38.2 38.2 158.1
0.444391 0.0 38.2 158.1

HOSTNAME-PROCESSID-CudaUVM.memspace_usage

# Space CudaUVM
# Time(s)  Size(MB)   HighWater(MB)   HighWater-Process(MB)
0.317260 38.1 38.1 81.8
0.377285 0.0 38.1 158.1
0.384785 38.1 38.1 158.1
0.441988 0.0 38.1 158.1

HOSTNAME-PROCESSID-CudaHostPinned.memspace_usage

# Space CudaHostPinned
# Time(s)  Size(MB)   HighWater(MB)   HighWater-Process(MB)
0.311749 0.0 0.0 81.8
0.335289 38.1 38.1 120.0
0.368485 0.0 38.1 158.1
0.400073 38.1 38.1 158.1
0.433218 0.0 38.1 158.1

Happy profiling!