Adding PSyclone profiling calipers to UKCA in LFRic
Building the PSyclone interface
First, navigate to your PSyclone directory. From here, go to lib/profiling
.
Call make
on the PSyclone library you want to build with (e.g. Vernier/NVIDIA NVTX) in the relevant folder. See the information here on how to do this generically, and the README.md
in each of the relevant subfolders in the PSyclone GitHub repo for advice on speciific tools.
Make sure you edit the Makefile
and use the same compiler/flags as you do for your LFRic build! I had segfault issues where I had -Mchkptr
and -Mchkstk
enabled in one but not the other. You may also need to point VERNIER_ROOT
for example to your installed Vernier.
Getting it into LFRic
You also need to interface the PSyData libraries into the build system. See here for details. The tl;dr of this:
Create a new folder in a branch of LFRic (
infrastructure/source/psydata
)Add the relevant f90 files that you built earlier to this folder. Now they are part of the LFRic source code, they will be analysed, built against and linked into the final binary.
I had many issues when trying to get this going trying to get
DependencyAnalyser
(the tool used by LFRic to check dependencies all work before building). I fixed this by addingvernier_mod
andvernier
to the list of things forDependencyAnalyser to ignore
- this is because the libraries are pre-built and linked in (using-lvernier
,-lvernier_psy
,-lvernier_f
and-lvernier_c
in the Vernier case). You can do this withexport IGNORE_DEPENDENCIES +=...
in your environment/job submission script, or you can amendinfrastructure/build/import.mk
in your LFRic branch. You may also need toexport EXTERNAL_DYNAMIC_LIBRARIES += vernier
also - either in the build script or inimport.mk
.Add the includes and libraries - again you can use environment variables in your job submission script. e.g. for Vernier
export LDFLAGS+=' -L<psyclone_path>/lib/profiling/vernier -lvernier_psy -lvernier_f -lvernier_c -lvernier'
andexport FFLAGS+=' -I<psyclone_path>/lib/profiling/vernier
You then need to include calls to profile_PSyDataInit()
and profile_PSyDataShutdown()
into LFRic - in infrastructure/source/utilities/mpi_mod.F90
. Where exactly these go depends on the profiling library you use. For Vernier, since it gives separate output files per MPI rank, you need to include these inside the calls to MPI_init
and MPI_finalize
. For example:
use profile_psy_data_mod, only : profile_PSyDataInit, profile_PSyDataShutdown
...
...
...
subroutine create_comm(out_comm)
implicit none
integer, intent(out) :: out_comm
integer :: ierr
#ifdef NO_MPI
! Don't initialise mpi in non-mpi build.
call profile_PSyDataInit()
out_comm = 0
ierr=0 ! Set local variable to avoid unused variable errors
#else
call mpi_init(ierr)
call profile_PSyDataInit()
if (ierr /= mpi_success) &
call log_event('Unable to initialise MPI', LOG_LEVEL_ERROR )
out_comm = mpi_comm_world
#endif
end subroutine create_comm
!> Helper function (not type bound) that finalises mpi and releases
!> mpi_comm_world
!>
subroutine destroy_comm()
implicit none
integer :: ierr
#ifdef NO_MPI
! Don't finalise mpi in non-mpi build
call profile_PSyDataShutdown()
ierr=0 ! Set local variable to avoid unused variable errors
#else
call profile_PSyDataShutdown()
call mpi_finalize(ierr)
if (ierr /= mpi_success) &
call log_event('Unable to finalise MPI', LOG_LEVEL_ERROR )
#endif
end subroutine destroy_comm
Adding the calipers into the UKCA code
You can use a transformer to instrument specific loops, or you can simple pass the --profile routines
option to add instrumentation to all subroutines that you parse with PSyclone. See here for details. My current workflow is to do this work on a branch, then point dependencies.sh
to that branch. WIP is to get Benjamin Went’s work incorporated, so we include this step within the build system itself.
Tips for building
An important note is to delete your working
directory if your build fails and you make changes to the Makefiles etc. when you build them. Whether this is always necessary I’m not sure, but oftentimes I’ll make a change (e.g. adding a module to be ignored) and it doesn’t pick it up until I do this.
The neat thing is that you can apply this to any arbitrary Fortran source code, not just LFRic - in fact it is easier in this case as you don’t need to mess around with the complex LFRic build system (this was written before Fab
/Baf
were put into routine use, so this comment may be out of date!)