diff --git a/CMakeLists.txt b/CMakeLists.txt index 730eba4a..4b47ecea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ enable_language(Fortran) option(BUILD_MPI "Build MPI version of MPI" ON) option(FG "Build the Fine-Grained ModEM Version - (Only compatiable with SP2)" OFF) option(USE_C_TIMERS "Use the C timers in ModEM_timers" OFF) +option(MODEM_MEMORY "Compile the ModEM Memory utilitie and C memory routines" OFF) set(FORWARD_FORMULATION "SP2" CACHE STRING "Set the forward formulation: < MF | SP | SP2 >") set(MODEM_BUILD_DIMS "3D" CACHE STRING "Specify whether to build either the 2D or 3D versino of ModEM < 2D | 3D >") @@ -31,6 +32,11 @@ if (USE_C_TIMERS) enable_language(C) endif() +if (MODEM_MEMORY) + enable_language(C) + add_compile_definitions(MODEM_MEMORY) +endif() + if (NOT BUILD_GPU IN_LIST SUPPORTED_GPU_OPTS) message(FATAL_ERROR "-DBUILD_GPU option ('${BUILD_GPU}') was not valid - choose from: ${SUPPORTED_GPU_OPTS}") endif() diff --git a/f90/UTILS/CMakeLists.txt b/f90/UTILS/CMakeLists.txt index 6977d9bf..86875931 100644 --- a/f90/UTILS/CMakeLists.txt +++ b/f90/UTILS/CMakeLists.txt @@ -18,3 +18,10 @@ if (USE_C_TIMERS) add_compile_definitions(USE_C_TIMERS) endif() + +if (MODEM_MEMORY) + target_sources(${MODEM_EXE} + PRIVATE ModEM_memory.f90 + PRIVATE ModEM_getrusage.c + ) +endif() diff --git a/f90/UTILS/ModEM_getrusage.c b/f90/UTILS/ModEM_getrusage.c new file mode 100644 index 00000000..edd61e0a --- /dev/null +++ b/f90/UTILS/ModEM_getrusage.c @@ -0,0 +1,38 @@ +#include +#include + +/* It should be noted that the below function might have different + * results depending on the system you are on. POSIX doesn't guarantee + * ru_maxrss even be implemented. + * + * It's on most systems, but your results may very. + */ + +/* +* To call this C function from Fortran, use this interface: +* +* interface +* subroutine get_maxrss() result(maxrss) bind(c) +* use iso_c_binding, only : c_long +* integer (c_long), intent(out) :: maxrss +* end subroutine get_maxrss +* end interface +*/ +void get_maxrss(long *maxrss_bytes) { + + int conversion; + struct rusage usage; + +#if defined(__APPLE_) || defined(__MACH__) + // BSD's ru_maxrss (used by Apple) is in bytes + conversion = 1.0; +#elif __linux__ + // Linux's ru_maxrss is in KiB + conversion = 1000.0; +#else + conversion = 1.0; +#endif + + getrusage(RUSAGE_SELF, &usage); + *maxrss_bytes = usage.ru_maxrss / conversion; +} diff --git a/f90/UTILS/ModEM_memory.f90 b/f90/UTILS/ModEM_memory.f90 new file mode 100644 index 00000000..6a6439f5 --- /dev/null +++ b/f90/UTILS/ModEM_memory.f90 @@ -0,0 +1,143 @@ +! module ModEM_memory +! +! Use getrusage to track memory high-water mark. +! +! This module contains helpful routines to call and +! log the results of ru_maxrss by calling the +! getrusage system call. +! +! The public routines print ru_maxrss, which is +! a high-water mark of memory. More information on +! getrusage can be found by in `man 2 getrusage`. +! + +module ModEM_memory + + use utilities + use iso_c_binding, only : c_long +#ifdef MPI + use mpi +#endif + use ModEM_logger + + implicit none + + private + + integer :: task_id + + public ModEM_memory_print_report + public ModEM_memory_log_report +#ifdef MPI + public ModEM_memory_get_all +#endif + +contains + + subroutine ModEM_memory_create_log_fname() + + end subroutine + + subroutine ModEM_memory_get_maxrss(maxrss_bytes) + + implicit none + + integer, intent(out) :: maxrss_bytes + integer (c_long) :: maxrss_bytes_c + +#ifdef MODEM_MEMORY + interface + subroutine get_maxrss(maxrss_bytes) bind(c) + use iso_c_binding, only : c_long + integer (c_long), intent(out) :: maxrss_bytes + end subroutine get_maxrss + end interface + + call get_maxrss(maxrss_bytes_c) + maxrss_bytes = maxrss_bytes_c +#else + write(0,*) "ERROR: ModEM was not built with the ModEM_getrusage.c file!" + write(0,*) "ERROR: In order to use ModEM_memory routines you must" + write(0,*) "ERROR: build ModEM with CMake and specify '-DMODEM_MEMORY=on'" + call ModEM_abort() +#endif + + end subroutine ModEM_memory_get_maxrss + + subroutine ModEM_memory_print_report(message) + + implicit none + + integer :: maxrss + character (len=*), intent(in) :: message + real :: maxrss_bytes, maxrss_kb, maxrss_mb, maxrss_gb + character (len=*), parameter :: LOG_MSG_FMT = "(I4.4, A, A, A, F18.1, A, F18.1, A, F18.1, A)" + character (len=512) :: log_message + + call ModEM_memory_get_maxrss(maxrss) + call ModEM_memory_convert_maxrss(maxrss, maxrss_kb, maxrss_mb, maxrss_gb) + + write(log_message, LOG_MSG_FMT) task_id, ' - ', trim(message), ", ", maxrss_kb, ' kb ', maxrss_mb, ' mb ', maxrss_gb, ' gb' + write(6,*) trim(log_message) + + end subroutine ModEM_memory_print_report + + subroutine ModEM_memory_convert_maxrss(maxrss_bytes, maxrss_kb, maxrss_mb, maxrss_gb) + + implicit none + + integer :: maxrss_bytes + real, intent(out) :: maxrss_kb + real, intent(out) :: maxrss_mb + real, intent(out) :: maxrss_gb + + maxrss_kb = maxrss_bytes / 1.0 + maxrss_mb = maxrss_kb / 1000.0 + maxrss_gb = maxrss_mb / 1000.1 + + end subroutine ModEM_memory_convert_maxrss + + subroutine ModEM_memory_log_report(message) + + implicit none + + character (len=*), intent(in) :: message + character (len=*), parameter :: LOG_MSG_FMT = "(A, A, F18.1, A, F18.1, A, F18.1, A)" + integer :: maxrss + real :: maxrss_kb, maxrss_mb, maxrss_gb + character (len=512) :: log_message + + call ModEM_memory_get_maxrss(maxrss) + + call ModEM_memory_convert_maxrss(maxrss, maxrss_kb, maxrss_mb, maxrss_gb) + write(log_message, LOG_MSG_FMT) trim(message), ", ", maxrss_kb, ' kb ', maxrss_mb, ' mb ', maxrss_gb, ' gb' + call ModEM_log(log_message, mainOnly=.false., flush_log=.true.) + + end subroutine ModEM_memory_log_report + +#ifdef MPI + subroutine ModEM_memory_get_all(message) + + implicit none + + character (len=*), intent(in) :: message + character (len=*), parameter :: LOG_MSG_FMT = "(A, A, F18.1, A, F18.1, A, F18.1, A)" + character (len=512) :: log_message + + integer :: maxrss, global_maxrss + real :: maxrss_kb, maxrss_mb, maxrss_gb + + call ModEM_memory_get_maxrss(maxrss) + + call MPI_reduce(maxrss, global_maxrss, 1, MPI_INTEGER, MPI_SUM, 0, MPI_COMM_WORLD, ierr) + + if (taskid == 0) then + call ModEM_memory_convert_maxrss(global_maxrss, maxrss_kb, maxrss_mb, maxrss_gb) + write(log_message, LOG_MSG_FMT) trim(message), ", ", maxrss_kb, ' kb ', maxrss_mb, ' mb ', maxrss_gb, ' gb' + call ModEM_log(log_message, mainOnly=.false., flush_log=.true.) + end if + + end subroutine ModEM_memory_get_all +#endif + +end module ModEM_memory