TDycore Development
This section contains topics that are useful to anyone developing the TDycore library or trying to use it in an application.
Initializing the TDycore Library
Because the TDycore library uses MPI, PETSc, and other subsystems, we have defined a function to initialize these various subsystems at the beginning of any TDycore-based driver/program::
PetscErrorCode TDyInit(int argc, char *argv[]);
Call this function where you would ordinarily call MPI_Init
or
PetscInitialize
. It has no effect on subsequent calls. You can check whether
the library has been initialized with a call to:
PetscBool TDyInitialized(void);
Similarly, we have defined a finalization function to be called at the end of a TDycore-based program::
PetscErrorCode TDyFinalize(void);
Use this function instead of MPI_Finalize
or PetscFinalize
. This ensures
that all TDycore subsystems properly free their resources.
Memory Allocation and Deallocation
Within the TDycore library, the following functions are used to dynamically allocate and deallocate memory::
PetscErrorCode TDyAlloc(size_t size, void **result);
PetscErrorCode TDyRealloc(size_t size, void **memory);
PetscErrorCode TDyFree(void *memory);
TDyAlloc
allocates and zero-initializes a contiguous block of memory of
the specified size in bytes, aligned to PETSC_MEMALIGN
. Use this function
anywhere you would use malloc
or any of the PETSc memory allocation
functions like PetscMalloc
, PetscCalloc
, etc.
TDyRealloc
resizes an existing block of memory to a new size. Call this
where you would call realloc
or PetscRealloc
.
TDyFree
frees an allocated block of memory. Call this where you would call
free
or PetscFree
.
Fortran 90 Interface
We offer two equivalent subroutines for Fortran 90, similar to their PETSc counterparts::
TDyInit(ierr)
TDyFinalize(ierr)
Both accept an integer that stores an error code if these subroutines encounter an issue.
Timers and Profiling
We use PETSc’s Logging machinery <https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Profiling/index.html> to understand the performance of the dycores. In particular, we’ve provided some high-level wrappers around the PetscLogEvent <https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Profiling/PetscLogEvent.html> object that make it very easy to add timers for functions and blocks of code.
The TDycore Timers Subsystem
The subsystem for timers and profiling is defined almost entirely in tdytimers.h
.
When a dycore is initialized, a registry of timers is created when you call
TDyInit()
. Once this is done, you can manipulate timers with functions and
macros as described below. Make sure you include the tdytimers.h
file
wherever you use these timers.
Function-Level Profiling
The easiest way to use a timer is to instrument a function with a timer using
TDY_START_FUNCTION_TIMER()
and TDY_STOP_FUNCTION_TIMER()
. These macros
automatically create/retrieve a timer for the function and start/stop it as
you’d expect. You place the START
macro at the top of a function, and the
STOP
one at the bottom. For example::
void do_some_expensive_things(TDy dy)
{
TDY_START_FUNCTION_TIMER()
... // Do the expensive things
TDY_STOP_FUNCTION_TIMER()
}
There’s no need to understand PETSc’s logging objects–everything is done for you. These function-level timers are named after the functions in which they appear.
Manually-Created Timers
Sometimes you want to time something that happens in the middle of a function.
You can do this by calling the TDyGetTimer
function with the
TDyStartTimer
and TDyStopTimer
macros::
void do_various_things(TDy dy)
{
... // Stuff happens here
// Now we want to time a block of code in the middle of the function.
PetscLogEvent timer = TDyGetTimer("important things");
TDyStartTimer(timer);
... // Important things happen here!
TDyStopTimer(timer);
... // Other stuff happens here
}
This is a bit more involved–you need to know that timers are PetscLogEvent
objects, for example, and you need to name your timers–but not too difficult.
As you might have guessed, TDY_START_FUNCTION_TIMER
and
TDY_STOP_FUNCTION_TIMER
are just wrappers around these constructs.
TDyStartTimer
and TDyStopTimer
are themselves just macros that call
PetscLogEventBegin
and PetscLogEventEnd
, so you can always use those if
you want more control.
Profiling Stages
PETSc allows an arbitrary number of logging/profiling “stages” to be defined so
that you can organize your profiling into sections. These stages can be named
for convenience. You can enter and exit a named stage with calls to
TDyEnterProfilingStage(stageName)
and TDyExitProfilingStage(stageName)
,
where stageName
is a string containing the name of the stage.
TDycore provides these named stages, registering them in TDyInit
:
"TDycore Setup"
: for creating meshes, setting up initial conditions, calculating time-independent matrices and vectors, etc."TDycore Stepping"
, for timestepping"TDycore I/O"
, for checkpointing, restarting, generating visualizations
You can register your own named stages with TDyAddProfilingStage(stageName)
.
All of this machinery is a thin wrapper around PETSc’s PetscLogStage
mechanism, which you can use if you prefer. The functions and macros above just
simplify the bookkeeping.
Generating Profiling Logs
Adding timers to a code is only part of profiling. You also need to generate
profiling reports for runs of interest! Fortunately, this is easy–just add the
-tdy_timers
flag to your command line arguments to generate a performance
log. This log is named tdycore_profile.csv
. It’s a comma-separated variable
file containing all performance data collected by PETSc. The timers you’ve
created show up in the profile just like those embedded in the PETSc library.
If you’d rather look at the traditional profiling/log data dumped by PETSc, you
can use the -log_view
flag to have PETSc print that information to the
standard output.
Interpreting Profile Data with TDyProf
If you’ve generated a tdycore_profile.csv
file, you can use a tool called
tdyprof
(located in the tools/
subdirectory of the source tree). This
Python script digests the contents of the CSV file you give it and generates
nicely-formatted reports for desired information. Use it thus::
tdyprof <profile.csv> <command> [options]
or just type tdyprof
by itself to see its usage information. For example,
to see the top 10 “hotspots” in the performance profile::
tdyprof tdycore_profile.csv top10
tdyprof: showing top 10 hits:
Stage Name Event Name Time FLOP
Main Stage TDyTimeIntegratorRunToTime 0.139049 1.85991e+07
Main Stage SNESSolve 0.13895 1.85991e+07
Main Stage SNESJacobianEval 0.120045 609812.
Main Stage TDyMPFAOSNESJacobian_3DMesh 0.120032 609812.
Main Stage TDyMPFAOIJacobian_Vertices_3DMesh 0.118853 606312.
TDycore Setup TDyDriverInitializeTDy 0.0912797 52040.
TDycore Setup TDySetup 0.0533342 52040.
TDycore Setup TDyMPFAOInitialize 0.0533217 52040.
Main Stage DMPlexDistribute 0.0386017 0.
TDycore Setup TDyCreateJacobian 0.0370897 0.
Generating Scaling Study Plots with TDyPerfPlot
TODO