diff --git a/.github/workflows/double-precision.yml b/.github/workflows/double-precision.yml index d8b8fbb631..9303af7523 100644 --- a/.github/workflows/double-precision.yml +++ b/.github/workflows/double-precision.yml @@ -25,10 +25,18 @@ jobs: with: indexsize: ${{ matrix.indexsize }} precision: ${{ matrix.precision }} - - name: Archive files from failed build + - name: Archive build files from failed build uses: actions/upload-artifact@v2.2.4 if: failure() with: name: build_files path: | ${{ github.workspace }}/test/build_* + !${{ github.workspace }}/test/build_*/Testing/output + - name: Archive output files from failed build + uses: actions/upload-artifact@v2.2.4 + if: failure() + with: + name: output_files + path: | + ${{ github.workspace }}/test/build_*/Testing/output diff --git a/.github/workflows/extended-precision.yml b/.github/workflows/extended-precision.yml index 7af3fb6e76..dc6f32e726 100644 --- a/.github/workflows/extended-precision.yml +++ b/.github/workflows/extended-precision.yml @@ -24,10 +24,18 @@ jobs: with: indexsize: ${{ matrix.indexsize }} precision: ${{ matrix.precision }} - - name: Archive files from failed build + - name: Archive build files from failed build uses: actions/upload-artifact@v2.2.4 if: failure() with: name: build_files path: | ${{ github.workspace }}/test/build_* + !${{ github.workspace }}/test/build_*/Testing/output + - name: Archive output files from failed build + uses: actions/upload-artifact@v2.2.4 + if: failure() + with: + name: output_files + path: | + ${{ github.workspace }}/test/build_*/Testing/output diff --git a/.github/workflows/single-precision.yml b/.github/workflows/single-precision.yml index cf5ab5f213..304ae4e3bc 100644 --- a/.github/workflows/single-precision.yml +++ b/.github/workflows/single-precision.yml @@ -24,10 +24,18 @@ jobs: with: indexsize: ${{ matrix.indexsize }} precision: ${{ matrix.precision }} - - name: Archive files from failed build + - name: Archive build files from failed build uses: actions/upload-artifact@v2.2.4 if: failure() with: name: build_files path: | ${{ github.workspace }}/test/build_* + !${{ github.workspace }}/test/build_*/Testing/output + - name: Archive output files from failed build + uses: actions/upload-artifact@v2.2.4 + if: failure() + with: + name: output_files + path: | + ${{ github.workspace }}/test/build_*/Testing/output diff --git a/.github/workflows/spack-develop.yml b/.github/workflows/spack-develop.yml index 915e38838c..07dce581c2 100644 --- a/.github/workflows/spack-develop.yml +++ b/.github/workflows/spack-develop.yml @@ -27,10 +27,18 @@ jobs: with: indexsize: ${{ matrix.indexsize }} precision: ${{ matrix.precision }} - - name: Archive files from failed build + - name: Archive build files from failed build uses: actions/upload-artifact@v2.2.4 if: failure() with: name: build_files path: | ${{ github.workspace }}/test/build_* + !${{ github.workspace }}/test/build_*/Testing/output + - name: Archive output files from failed build + uses: actions/upload-artifact@v2.2.4 + if: failure() + with: + name: output_files + path: | + ${{ github.workspace }}/test/build_*/Testing/output diff --git a/.github/workflows/windows-latest-mingw.yml b/.github/workflows/windows-latest-mingw.yml index ef1fad6ee2..ab20ea721a 100644 --- a/.github/workflows/windows-latest-mingw.yml +++ b/.github/workflows/windows-latest-mingw.yml @@ -21,7 +21,7 @@ jobs: - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -G "MinGW Makefiles" -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -G "MinGW Makefiles" -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DSUNDIALS_BUILD_WITH_PROFILING=ON -DSUNDIALS_LOGGING_LEVEL=2 -DSUNDIALS_TEST_UNITTESTS=OFF -DEXAMPLES_ENABLE_CXX=ON - name: Build # Build your program with the given configuration diff --git a/.github/workflows/windows-latest.yml b/.github/workflows/windows-latest.yml index fee4f246d9..d640eb6bd2 100644 --- a/.github/workflows/windows-latest.yml +++ b/.github/workflows/windows-latest.yml @@ -17,7 +17,7 @@ jobs: - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC_LIBS=OFF + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC_LIBS=OFF -DSUNDIALS_BUILD_WITH_PROFILING=ON -DSUNDIALS_LOGGING_LEVEL=2 -DSUNDIALS_TEST_UNITTESTS=ON -DEXAMPLES_ENABLE_CXX=ON - name: Build # Build your program with the given configuration diff --git a/CHANGELOG.md b/CHANGELOG.md index 34c8cf9949..8d41c15931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ Fixed scaling bug in `SUNMatScaleAddI_Sparse` for non-square matrices. Fixed missing soversions in some `SUNLinearSolver` and `SUNNonlinearSolver` CMake targets. -Added Fortran support for the LAPACK dense `SUNLinearSolver` implementations. +Added Fortran support for the LAPACK dense `SUNLinearSolver` implementation. Fixed the build system support for MAGMA when using a NVIDIA HPC SDK installation of CUDA and fixed the targets used for rocBLAS and rocSPARSE. @@ -38,6 +38,8 @@ ERK method `ARKODE_SOFRONIOU_SPALETTA_5_3_4`, the sixth order ERK method the eighth order ERK method `ARKODE_VERNER_13_7_8`, and the ninth order ERK method `ARKODE_VERNER_16_8_9`. +Changed the `SUNProfiler` so that it does not rely on `MPI_WTime` in any case. +This fixes https://github.com/LLNL/sundials/issues/312. ## Changes to SUNDIALS in release 6.6.1 diff --git a/cmake/SundialsSetupCompilers.cmake b/cmake/SundialsSetupCompilers.cmake index 3cd0c055a0..b993ec85f5 100644 --- a/cmake/SundialsSetupCompilers.cmake +++ b/cmake/SundialsSetupCompilers.cmake @@ -223,13 +223,6 @@ if(SUNDIALS_POSIX_TIMERS AND POSIX_TIMERS_NEED_POSIX_C_SOURCE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_POSIX_C_SOURCE=${SUNDIALS_POSIX_C_SOURCE}") endif() -# Check if profiling is being built with no timers. -if(SUNDIALS_BUILD_WITH_PROFILING AND - (NOT ENABLE_CALIPER) AND - (NOT ENABLE_MPI) AND - (NOT SUNDIALS_POSIX_TIMERS)) - message(SEND_ERROR "The SUNDIALS native profiler requires POSIX timers or MPI_Wtime, but neither were found.") -endif() # --------------------------------------------------------------- # Check for deprecated attribute with message diff --git a/cmake/macros/SundialsAddLibrary.cmake b/cmake/macros/SundialsAddLibrary.cmake index 96d71765ec..5bf25ab4b8 100644 --- a/cmake/macros/SundialsAddLibrary.cmake +++ b/cmake/macros/SundialsAddLibrary.cmake @@ -152,6 +152,8 @@ macro(sundials_add_library target) # create the target for the object library add_library(${obj_target} OBJECT ${sources}) + set_target_properties(${obj_target} PROPERTIES FOLDER "obj") + # add all object libraries to object library if(sundials_add_library_OBJECT_LIBRARIES) target_link_libraries(${obj_target} @@ -229,6 +231,8 @@ macro(sundials_add_library target) add_library(${_actual_target_name} ${_libtype} $) + set_target_properties(${_actual_target_name} PROPERTIES FOLDER "src") + # add any object library dependencies if(sundials_add_library_OBJECT_LIBRARIES) if(${_libtype} MATCHES "STATIC") diff --git a/doc/arkode/guide/source/Introduction.rst b/doc/arkode/guide/source/Introduction.rst index 8a0e8702d2..dd4e67ae9d 100644 --- a/doc/arkode/guide/source/Introduction.rst +++ b/doc/arkode/guide/source/Introduction.rst @@ -162,6 +162,9 @@ ERK method ``ARKODE_SOFRONIOU_SPALETTA_5_3_4``, the sixth order ERK method the eighth order ERK method ``ARKODE_VERNER_13_7_8``, and the ninth order ERK method ``ARKODE_VERNER_16_8_9``. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Added Fortran support for the LAPACK dense ``SUNLinearSolver`` implementation. Changes in v5.6.1 @@ -993,7 +996,6 @@ utilize a zero initial guess. A bug was fixed in the ARKODE stepper modules where the stop time may be passed after resetting the integrator. - Changes in v4.7.0 ----------------- diff --git a/doc/arkode/guide/source/Usage/ARKStep_c_interface/User_callable.rst b/doc/arkode/guide/source/Usage/ARKStep_c_interface/User_callable.rst index df8a32a095..eb1f05979e 100644 --- a/doc/arkode/guide/source/Usage/ARKStep_c_interface/User_callable.rst +++ b/doc/arkode/guide/source/Usage/ARKStep_c_interface/User_callable.rst @@ -726,11 +726,8 @@ the user has set a stop time (with a call to the optional input function in :numref:`ARKODE.Mathematics.Interpolation`). The *ARK_ONE_STEP* option tells the solver to only take a - single internal step :math:`y_{n-1} \to y_{n}` and then return - control back to the calling program. If this step will - overtake *tout* then the solver will again return an - interpolated result; otherwise it will return a copy of the - internal solution :math:`y_{n}` in the vector *yout*. + single internal step, :math:`y_{n-1} \to y_{n}`, and return the solution + at that point, :math:`y_{n}`, in the vector *yout*. **Return value:** * *ARK_SUCCESS* if successful. @@ -795,8 +792,8 @@ the user has set a stop time (with a call to the optional input function only to get the direction and a rough scale of the independent variable. - All failure return values are negative and so testing the return argument for - negative values will trap all :c:func:`ARKStepEvolve()` failures. + All failure return values are negative and so testing the return argument + for negative values will trap all :c:func:`ARKStepEvolve()` failures. Since interpolation may reduce the accuracy in the reported solution, if full method accuracy is desired the user should issue diff --git a/doc/arkode/guide/source/Usage/ERKStep_c_interface/User_callable.rst b/doc/arkode/guide/source/Usage/ERKStep_c_interface/User_callable.rst index d8782622a6..a14ed89456 100644 --- a/doc/arkode/guide/source/Usage/ERKStep_c_interface/User_callable.rst +++ b/doc/arkode/guide/source/Usage/ERKStep_c_interface/User_callable.rst @@ -352,11 +352,8 @@ has requested rootfinding. :numref:`ARKODE.Mathematics.Interpolation`). The *ARK_ONE_STEP* option tells the solver to only take a - single internal step :math:`y_{n-1} \to y_{n}` and then return - control back to the calling program. If this step will - overtake *tout* then the solver will again return an - interpolated result; otherwise it will return a copy of the - internal solution :math:`y_{n}` in the vector *yout*. + single internal step, :math:`y_{n-1} \to y_{n}`, and return the solution + at that point, :math:`y_{n}`, in the vector *yout*. **Return value:** * *ARK_SUCCESS* if successful. @@ -399,7 +396,9 @@ has requested rootfinding. In *ARK_ONE_STEP* mode, *tout* is used only on the first call, and only to get the direction and a rough scale of the independent - variable. All failure return values are negative and so testing the + variable. + + All failure return values are negative and so testing the return argument for negative values will trap all :c:func:`ERKStepEvolve()` failures. diff --git a/doc/arkode/guide/source/Usage/MRIStep_c_interface/User_callable.rst b/doc/arkode/guide/source/Usage/MRIStep_c_interface/User_callable.rst index 2daf213487..e39f20bbbf 100644 --- a/doc/arkode/guide/source/Usage/MRIStep_c_interface/User_callable.rst +++ b/doc/arkode/guide/source/Usage/MRIStep_c_interface/User_callable.rst @@ -545,11 +545,8 @@ the user has set a stop time (with a call to the optional input function in :numref:`ARKODE.Mathematics.Interpolation`). The *ARK_ONE_STEP* option tells the solver to only take a - single internal step :math:`y_{n-1} \to y_{n}` and then return - control back to the calling program. If this step will - overtake *tout* then the solver will again return an - interpolated result; otherwise it will return a copy of the - internal solution :math:`y_{n}` in the vector *yout*. + single internal step, :math:`y_{n-1} \to y_{n}`, and return the solution + at that point, :math:`y_{n}`, in the vector *yout*. **Return value:** * *ARK_SUCCESS* if successful. @@ -604,8 +601,8 @@ the user has set a stop time (with a call to the optional input function only to get the direction and a rough scale of the independent variable. - All failure return values are negative and so testing the return argument for - negative values will trap all :c:func:`MRIStepEvolve()` failures. + All failure return values are negative and so testing the return argument + for negative values will trap all :c:func:`MRIStepEvolve()` failures. Since interpolation may reduce the accuracy in the reported solution, if full method accuracy is desired the user should issue diff --git a/doc/arkode/guide/source/Usage/SPRKStep_c_interface/User_callable.rst b/doc/arkode/guide/source/Usage/SPRKStep_c_interface/User_callable.rst index 1450ea2c64..28fb2f2ca8 100644 --- a/doc/arkode/guide/source/Usage/SPRKStep_c_interface/User_callable.rst +++ b/doc/arkode/guide/source/Usage/SPRKStep_c_interface/User_callable.rst @@ -152,11 +152,8 @@ has requested rootfinding. :numref:`ARKODE.Mathematics.Interpolation`). The *ARK_ONE_STEP* option tells the solver to only take a - single internal step :math:`y_{n-1} \to y_{n}` and then return - control back to the calling program. If this step will - overtake *tout* then the solver will again return an - interpolated result; otherwise it will return a copy of the - internal solution :math:`y_{n}` in the vector *yout*. + single internal step, :math:`y_{n-1} \to y_{n}`, and return the + solution at that point, :math:`y_{n}`, in the vector *yout*. :retval ARK_SUCCESS: if successful. :retval ARK_ROOT_RETURN: if :c:func:`SPRKStepEvolve()` succeeded, and @@ -191,7 +188,9 @@ has requested rootfinding. In *ARK_ONE_STEP* mode, *tout* is used only on the first call, and only to get the direction and a rough scale of the independent - variable. All failure return values are negative and so testing the + variable. + + All failure return values are negative and so testing the return argument for negative values will trap all :c:func:`SPRKStepEvolve()` failures. diff --git a/doc/cvode/guide/source/Introduction.rst b/doc/cvode/guide/source/Introduction.rst index 7a175c0ca0..b4c2388e34 100644 --- a/doc/cvode/guide/source/Introduction.rst +++ b/doc/cvode/guide/source/Introduction.rst @@ -129,6 +129,9 @@ Fixed scaling bug in ``SUNMatScaleAddI_Sparse`` for non-square matrices. Fixed missing soversions in some ``SUNLinearSolver`` and ``SUNNonlinearSolver`` CMake targets. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Added Fortran support for the LAPACK dense ``SUNLinearSolver`` implementation. Changes in v6.6.1 diff --git a/doc/cvodes/guide/source/Introduction.rst b/doc/cvodes/guide/source/Introduction.rst index bb561ccca5..d6ff7db84e 100644 --- a/doc/cvodes/guide/source/Introduction.rst +++ b/doc/cvodes/guide/source/Introduction.rst @@ -132,6 +132,9 @@ Fixed scaling bug in ``SUNMatScaleAddI_Sparse`` for non-square matrices. Fixed missing soversions in some ``SUNLinearSolver`` and ``SUNNonlinearSolver`` CMake targets. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Added Fortran support for the LAPACK dense ``SUNLinearSolver`` implementation. Changes in v6.6.1 diff --git a/doc/ida/guide/source/Introduction.rst b/doc/ida/guide/source/Introduction.rst index 9f54d64a68..369147817e 100644 --- a/doc/ida/guide/source/Introduction.rst +++ b/doc/ida/guide/source/Introduction.rst @@ -90,6 +90,9 @@ Fixed scaling bug in ``SUNMatScaleAddI_Sparse`` for non-square matrices. Fixed missing soversions in some ``SUNLinearSolver`` and ``SUNNonlinearSolver`` CMake targets. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Added Fortran support for the LAPACK dense ``SUNLinearSolver`` implementation. Changes in v6.6.1 diff --git a/doc/idas/guide/source/Introduction.rst b/doc/idas/guide/source/Introduction.rst index 0073960dc2..4dc93c7b41 100644 --- a/doc/idas/guide/source/Introduction.rst +++ b/doc/idas/guide/source/Introduction.rst @@ -107,6 +107,9 @@ Fixed scaling bug in ``SUNMatScaleAddI_Sparse`` for non-square matrices. Fixed missing soversions in some ``SUNLinearSolver`` and ``SUNNonlinearSolver`` CMake targets. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Added Fortran support for the LAPACK dense ``SUNLinearSolver`` implementation. Changes in v5.6.1 diff --git a/doc/kinsol/guide/source/Introduction.rst b/doc/kinsol/guide/source/Introduction.rst index ee33d5a33a..a4c33cc470 100644 --- a/doc/kinsol/guide/source/Introduction.rst +++ b/doc/kinsol/guide/source/Introduction.rst @@ -112,6 +112,9 @@ Updated the Tpetra NVector interface to support Trilinos 14. Fixed a memory leak when destroying a CUDA, HIP, SYCL, or system SUNMemoryHelper object. +Changed the ``SUNProfiler`` so that it does not rely on ``MPI_WTime`` in any case. +This fixes `GitHub Issue #312 `_. + Changes in v6.6.0 ----------------- diff --git a/doc/shared/Install.rst b/doc/shared/Install.rst index 10c1326ff8..9021e5b340 100644 --- a/doc/shared/Install.rst +++ b/doc/shared/Install.rst @@ -1022,6 +1022,7 @@ illustration only. .. cmakeoption:: SUNDIALS_BUILD_WITH_PROFILING Build SUNDIALS with capabilties for fine-grained profiling. + This requires POSIX timers or the Windows ``profileapi.h`` timers. Default: OFF diff --git a/doc/shared/sundials/Profiling.rst b/doc/shared/sundials/Profiling.rst index 4f9e9357df..4695643f29 100644 --- a/doc/shared/sundials/Profiling.rst +++ b/doc/shared/sundials/Profiling.rst @@ -48,6 +48,11 @@ If Caliper is enabled, then users should refer to the `Caliper documentation #endif -#endif /* SUNDIALS_PROFILER_H_ */ +#endif /* SUNDIALS_PROFILER_H */ diff --git a/include/sundials/sundials_profiler.hpp b/include/sundials/sundials_profiler.hpp new file mode 100644 index 0000000000..81bc95f920 --- /dev/null +++ b/include/sundials/sundials_profiler.hpp @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------- + * Programmer: Cody J. Balos @ LLNL + * ----------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2023, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * -----------------------------------------------------------------*/ + +#ifndef _SUNDIALS_PROFILER_HPP +#define _SUNDIALS_PROFILER_HPP + +#include +#include +#include + +#if defined(SUNDIALS_BUILD_WITH_PROFILING) && defined(SUNDIALS_CALIPER_ENABLED) +#define SUNDIALS_CXX_MARK_FUNCTION(projobj) CALI_CXX_MARK_FUNCTION +#elif defined(SUNDIALS_BUILD_WITH_PROFILING) +#define SUNDIALS_CXX_MARK_FUNCTION(profobj) \ + sundials::ProfilerMarkScope __ProfilerMarkScope(profobj, __func__) +#else +#define SUNDIALS_CXX_MARK_FUNCTION(profobj) +#endif + +namespace sundials { +/* Convenience class for C++ codes. + Allows for simpler profiler statements using C++ scoping rules. */ +class ProfilerMarkScope +{ +public: + ProfilerMarkScope(SUNProfiler prof, const char* name) + { + prof_ = prof; + name_ = name; + SUNProfiler_Begin(prof_, name_); + } + + ~ProfilerMarkScope() { SUNProfiler_End(prof_, name_); } + +private: + SUNProfiler prof_; + const char* name_; +}; +} // namespace sundials + +#endif /* SUNDIALS_PROFILER_HPP */ diff --git a/scripts/shared b/scripts/shared index 74cb6bfbba..1347175464 100755 --- a/scripts/shared +++ b/scripts/shared @@ -152,6 +152,7 @@ $tar $tarfile $distrobase/include/sundials/sundials_nonlinearsolver.hpp $tar $tarfile $distrobase/include/sundials/sundials_nvector_senswrapper.h $tar $tarfile $distrobase/include/sundials/sundials_nvector.h $tar $tarfile $distrobase/include/sundials/sundials_profiler.h +$tar $tarfile $distrobase/include/sundials/sundials_profiler.hpp $tar $tarfile $distrobase/include/sundials/sundials_sycl_policies.hpp $tar $tarfile $distrobase/include/sundials/sundials_types.h $tar $tarfile $distrobase/include/sundials/sundials_version.h @@ -842,3 +843,4 @@ echo " --- Add unit tests files to $tarfile" $tar $tarfile $distrobase/test/unit_tests/CMakeLists.txt $tar $tarfile $distrobase/test/unit_tests/reductions $tar $tarfile $distrobase/test/unit_tests/sunmemory +$tar $tarfile $distrobase/test/unit_tests/profiling diff --git a/src/sundials/CMakeLists.txt b/src/sundials/CMakeLists.txt index 16dd005cee..08afde2570 100644 --- a/src/sundials/CMakeLists.txt +++ b/src/sundials/CMakeLists.txt @@ -42,6 +42,7 @@ set(sundials_HEADERS sundials_nvector.h sundials_nvector.hpp sundials_profiler.h + sundials_profiler.hpp sundials_logger.h sundials_types.h sundials_version.h diff --git a/src/sundials/sundials_profiler.c b/src/sundials/sundials_profiler.c index 2761b9e3dc..adc2052c2d 100644 --- a/src/sundials/sundials_profiler.c +++ b/src/sundials/sundials_profiler.c @@ -15,29 +15,43 @@ #include #if SUNDIALS_MPI_ENABLED -#include #include -#elif defined(SUNDIALS_HAVE_POSIX_TIMERS) +#include +#endif + +#if defined(SUNDIALS_HAVE_POSIX_TIMERS) /* Minimum POSIX version needed for struct timespec and clock_monotonic */ #if !defined(_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 199309L) #define _POSIX_C_SOURCE 199309L #endif -#include #include +#include #include +#elif defined(WIN32) || defined(_WIN32) +#include #else -#error Either MPI_Wtime or clock_getttime is required but neither were found +#error SUNProfiler needs POSIX or Windows timers #endif #include #include - -#include #include -#include "sundials_hashmap.h" +#include + #include "sundials_debug.h" +#include "sundials_hashmap.h" -#define SUNDIALS_ROOT_TIMER ((const char*) "From profiler epoch") +#define SUNDIALS_ROOT_TIMER ((const char*)"From profiler epoch") + +#if defined(SUNDIALS_HAVE_POSIX_TIMERS) +typedef struct timespec sunTimespec; +#else +typedef struct _sunTimespec +{ + long int tv_sec; + long int tv_nsec; +} sunTimespec; +#endif /* Private functions */ #if SUNDIALS_MPI_ENABLED @@ -45,6 +59,7 @@ static int sunCollectTimers(SUNProfiler p); #endif static void sunPrintTimers(int idx, SUNHashMapKeyValue kv, FILE* fp, void* pvoid); static int sunCompareTimes(const void* l, const void* r); +static int sunclock_gettime_monotonic(sunTimespec* tp); /* sunTimerStruct. @@ -53,96 +68,78 @@ static int sunCompareTimes(const void* l, const void* r); struct _sunTimerStruct { -#if SUNDIALS_MPI_ENABLED - double tic; - double toc; -#else - struct timespec* tic; - struct timespec* toc; -#endif + sunTimespec* tic; + sunTimespec* toc; double average; double maximum; double elapsed; - long count; + long count; }; typedef struct _sunTimerStruct sunTimerStruct; static sunTimerStruct* sunTimerStructNew() { - sunTimerStruct* ts = (sunTimerStruct*) malloc(sizeof(sunTimerStruct)); -#if SUNDIALS_MPI_ENABLED - ts->tic = 0.0; - ts->toc = 0.0; -#else - ts->tic = (struct timespec *) malloc(sizeof(struct timespec)); - ts->toc = (struct timespec *) malloc(sizeof(struct timespec)); - ts->tic->tv_sec = 0; - ts->tic->tv_nsec = 0; -#endif - ts->elapsed = 0.0; - ts->average = 0.0; - ts->maximum = 0.0; - ts->count = 0; + sunTimerStruct* ts = (sunTimerStruct*)malloc(sizeof(sunTimerStruct)); + ts->tic = (sunTimespec*)malloc(sizeof(sunTimespec)); + ts->toc = (sunTimespec*)malloc(sizeof(sunTimespec)); + ts->tic->tv_sec = 0; + ts->tic->tv_nsec = 0; + ts->elapsed = 0.0; + ts->average = 0.0; + ts->maximum = 0.0; + ts->count = 0; return ts; } static void sunTimerStructFree(void* TS) { - sunTimerStruct* ts = (sunTimerStruct*) TS; + sunTimerStruct* ts = (sunTimerStruct*)TS; if (ts) { -#if !SUNDIALS_MPI_ENABLED if (ts->tic) free(ts->tic); if (ts->toc) free(ts->toc); -#endif free(ts); } } static void sunStartTiming(sunTimerStruct* entry) { -#if SUNDIALS_MPI_ENABLED - entry->tic = MPI_Wtime(); -#else - clock_gettime(CLOCK_MONOTONIC, entry->tic); -#endif + sunclock_gettime_monotonic(entry->tic); } static void sunStopTiming(sunTimerStruct* entry) { -#if SUNDIALS_MPI_ENABLED - entry->toc = MPI_Wtime(); - entry->elapsed += entry->toc - entry->tic; -#else - clock_gettime(CLOCK_MONOTONIC, entry->toc); - entry->elapsed += - ((double) (entry->toc->tv_sec - entry->tic->tv_sec) + - (double) (entry->toc->tv_nsec - entry->tic->tv_nsec) * 1e-9); -#endif - /* Initialize to total value */ + long s_difference = 0; + long ns_difference = 0; + + sunclock_gettime_monotonic(entry->toc); + + s_difference = entry->toc->tv_sec - entry->tic->tv_sec; + ns_difference = entry->toc->tv_nsec - entry->tic->tv_nsec; + if (ns_difference < 0) + { + s_difference--; + ns_difference = 1000000000 + entry->toc->tv_nsec - entry->tic->tv_nsec; + } + + entry->elapsed += ((double)s_difference) + ((double)ns_difference) * 1e-9; entry->average = entry->elapsed; entry->maximum = entry->elapsed; } static void sunResetTiming(sunTimerStruct* entry) { -#if SUNDIALS_MPI_ENABLED - entry->tic = 0.0; - entry->toc = 0.0; -#else entry->tic->tv_sec = 0; entry->tic->tv_nsec = 0; entry->toc->tv_sec = 0; entry->toc->tv_nsec = 0; -#endif - entry->elapsed = 0.0; - entry->average = 0.0; - entry->maximum = 0.0; - entry->count = 0; + entry->elapsed = 0.0; + entry->average = 0.0; + entry->maximum = 0.0; + entry->count = 0; } - /* SUNProfiler. @@ -151,11 +148,11 @@ static void sunResetTiming(sunTimerStruct* entry) struct _SUNProfiler { - void* comm; - char* title; - SUNHashMap map; + void* comm; + char* title; + SUNHashMap map; sunTimerStruct* overhead; - double sundials_time; + double sundials_time; }; int SUNProfiler_Create(void* comm, const char* title, SUNProfiler* p) @@ -164,23 +161,22 @@ int SUNProfiler_Create(void* comm, const char* title, SUNProfiler* p) int max_entries; char* max_entries_env; - *p = profiler = (SUNProfiler) malloc(sizeof(struct _SUNProfiler)); + *p = profiler = (SUNProfiler)malloc(sizeof(struct _SUNProfiler)); - if (profiler == NULL) - return(-1); + if (profiler == NULL) return (-1); profiler->overhead = sunTimerStructNew(); if (profiler->overhead == NULL) { free(profiler); *p = profiler = NULL; - return(-1); + return (-1); } sunStartTiming(profiler->overhead); /* Check to see if max entries env variable was set, and use if it was. */ - max_entries = 2560; + max_entries = 2560; max_entries_env = getenv("SUNPROFILER_MAX_ENTRIES"); if (max_entries_env) max_entries = atoi(max_entries_env); if (max_entries <= 0) max_entries = 2560; @@ -188,10 +184,10 @@ int SUNProfiler_Create(void* comm, const char* title, SUNProfiler* p) /* Create the hashmap used to store the timers */ if (SUNHashMap_New(max_entries, &profiler->map)) { - sunTimerStructFree((void*) profiler->overhead); + sunTimerStructFree((void*)profiler->overhead); free(profiler); *p = profiler = NULL; - return(-1); + return (-1); } /* Attach the comm, duplicating it if MPI is used. */ @@ -200,7 +196,7 @@ int SUNProfiler_Create(void* comm, const char* title, SUNProfiler* p) if (comm != NULL) { profiler->comm = malloc(sizeof(MPI_Comm)); - MPI_Comm_dup(*((MPI_Comm*) comm), (MPI_Comm*) profiler->comm); + MPI_Comm_dup(*((MPI_Comm*)comm), (MPI_Comm*)profiler->comm); } #else profiler->comm = comm; @@ -217,19 +213,19 @@ int SUNProfiler_Create(void* comm, const char* title, SUNProfiler* p) SUNDIALS_MARK_BEGIN(profiler, SUNDIALS_ROOT_TIMER); sunStopTiming(profiler->overhead); - return(0); + return (0); } int SUNProfiler_Free(SUNProfiler* p) { - if (p == NULL) return(-1); + if (p == NULL) return (-1); SUNDIALS_MARK_END(*p, SUNDIALS_ROOT_TIMER); if (*p) { SUNHashMap_Destroy(&(*p)->map, sunTimerStructFree); - sunTimerStructFree((void*) (*p)->overhead); + sunTimerStructFree((void*)(*p)->overhead); #if SUNDIALS_MPI_ENABLED if ((*p)->comm) { @@ -242,7 +238,7 @@ int SUNProfiler_Free(SUNProfiler* p) } *p = NULL; - return(0); + return (0); } int SUNProfiler_Begin(SUNProfiler p, const char* name) @@ -254,25 +250,27 @@ int SUNProfiler_Begin(SUNProfiler p, const char* name) char* errmsg; #endif - if (p == NULL) return(-1); + if (p == NULL) return (-1); sunStartTiming(p->overhead); - if (SUNHashMap_GetValue(p->map, name, (void**) &timer)) + if (SUNHashMap_GetValue(p->map, name, (void**)&timer)) { timer = sunTimerStructNew(); - ier = SUNHashMap_Insert(p->map, name, (void*) timer); + ier = SUNHashMap_Insert(p->map, name, (void*)timer); if (ier) { #ifdef SUNDIALS_DEBUG - slen = strlen(name); - errmsg = malloc(slen*sizeof(char)); - snprintf(errmsg, 128+slen, "(((( [ERROR] in SUNProfilerBegin: SUNHashMapInsert failed with code %d while inserting %s))))\n", ier, name); + slen = strlen(name); + errmsg = malloc(slen * sizeof(char)); + snprintf(errmsg, + 128 + slen, "(((( [ERROR] in SUNProfilerBegin: SUNHashMapInsert failed with code %d while inserting %s))))\n", + ier, name); SUNDIALS_DEBUG_PRINT(errmsg); free(errmsg); #endif sunTimerStructFree(timer); sunStopTiming(p->overhead); - return(-1); + return (-1); } } @@ -280,31 +278,67 @@ int SUNProfiler_Begin(SUNProfiler p, const char* name) sunStartTiming(timer); sunStopTiming(p->overhead); - return(0); + return (0); } int SUNProfiler_End(SUNProfiler p, const char* name) { sunTimerStruct* timer; - if (p == NULL) return(-1); + if (p == NULL) return (-1); sunStartTiming(p->overhead); - if (SUNHashMap_GetValue(p->map, name, (void**) &timer)) + if (SUNHashMap_GetValue(p->map, name, (void**)&timer)) { sunStopTiming(p->overhead); - return(-1); + return (-1); } sunStopTiming(timer); sunStopTiming(p->overhead); - return(0); + return (0); +} + +int SUNProfiler_GetTimerResolution(SUNProfiler p, double* resolution) +{ +#if defined(SUNDIALS_HAVE_POSIX_TIMERS) + sunTimespec spec; + clock_getres(CLOCK_MONOTONIC, &spec); + *resolution = 1e-9 * ((double)spec.tv_nsec); + + return (0); +#elif (defined(WIN32) || defined(_WIN32)) + static LARGE_INTEGER ticks_per_sec; + + if (!ticks_per_sec.QuadPart) + { + QueryPerformanceFrequency(&ticks_per_sec); + if (!ticks_per_sec.QuadPart) { return -1; } + } + + *resolution = (double) ticks_per_sec.QuadPart; + + return (0); +#else +#error SUNProfiler needs POSIX or Windows timers +#endif +} + +int SUNProfiler_GetElapsedTime(SUNProfiler p, const char* name, double* time) +{ + sunTimerStruct* timer; + + if (SUNHashMap_GetValue(p->map, name, (void**)&timer)) { return (-1); } + + *time = timer->elapsed; + + return (0); } int SUNProfiler_Reset(SUNProfiler p) { - int i = 0; + int i = 0; sunTimerStruct* timer = NULL; /* Check for valid input */ @@ -336,26 +370,26 @@ int SUNProfiler_Reset(SUNProfiler p) int SUNProfiler_Print(SUNProfiler p, FILE* fp) { - int i = 0; - int rank = 0; - sunTimerStruct* timer = NULL; + int i = 0; + int rank = 0; + sunTimerStruct* timer = NULL; SUNHashMapKeyValue* sorted = NULL; - if (p == NULL) return(-1); + if (p == NULL) return (-1); sunStartTiming(p->overhead); /* Get the total SUNDIALS time up to this point */ SUNDIALS_MARK_END(p, SUNDIALS_ROOT_TIMER); SUNDIALS_MARK_BEGIN(p, SUNDIALS_ROOT_TIMER); - if (SUNHashMap_GetValue(p->map, SUNDIALS_ROOT_TIMER, (void**) &timer)) - return(-1); + if (SUNHashMap_GetValue(p->map, SUNDIALS_ROOT_TIMER, (void**)&timer)) + return (-1); p->sundials_time = timer->elapsed; #if SUNDIALS_MPI_ENABLED if (p->comm) { - MPI_Comm_rank(*((MPI_Comm*) p->comm), &rank); + MPI_Comm_rank(*((MPI_Comm*)p->comm), &rank); /* Find the max and average time across all ranks */ sunCollectTimers(p); } @@ -363,23 +397,29 @@ int SUNProfiler_Print(SUNProfiler p, FILE* fp) if (rank == 0) { + double resolution; /* Sort the timers in descending order */ - if (SUNHashMap_Sort(p->map, &sorted, sunCompareTimes)) - return(-1); - fprintf(fp, "\n================================================================================================================\n"); + if (SUNHashMap_Sort(p->map, &sorted, sunCompareTimes)) return (-1); + SUNProfiler_GetTimerResolution(p, &resolution); + fprintf(fp, "\n============================================================" + "====================================================\n"); fprintf(fp, "SUNDIALS GIT VERSION: %s\n", SUNDIALS_GIT_VERSION); fprintf(fp, "SUNDIALS PROFILER: %s\n", p->title); - fprintf(fp, "%-40s\t %% time (inclusive) \t max/rank \t average/rank \t count \n", "Results:"); - fprintf(fp, "================================================================================================================\n"); + fprintf(fp, "TIMER RESOLUTION: %gs\n", resolution); + fprintf(fp, "%-40s\t %% time (inclusive) \t max/rank \t average/rank \t count \n", + "RESULTS:"); + fprintf(fp, "==============================================================" + "==================================================\n"); #if SUNDIALS_MPI_ENABLED if (p->comm == NULL) - printf("WARNING: no MPI communicator provided, times shown are for rank 0\n"); + printf( + "WARNING: no MPI communicator provided, times shown are for rank 0\n"); #endif /* Print all the other timers out */ for (i = 0; i < p->map->size; i++) - if (sorted[i]) sunPrintTimers(i, sorted[i], fp, (void*) p); + if (sorted[i]) sunPrintTimers(i, sorted[i], fp, (void*)p); free(sorted); } @@ -388,24 +428,26 @@ int SUNProfiler_Print(SUNProfiler p, FILE* fp) if (rank == 0) { /* Print out the total time and the profiler overhead */ - fprintf(fp, "%-40s\t %6.2f%% \t %.6fs \t -- \t\t -- \n", "Est. profiler overhead", - p->overhead->elapsed/p->sundials_time, + fprintf(fp, "%-40s\t %6.2f%% \t %.6fs \t -- \t\t -- \n", + "Est. profiler overhead", p->overhead->elapsed / p->sundials_time, p->overhead->elapsed); /* End of output */ fprintf(fp, "\n"); } - return(0); + return (0); } #if SUNDIALS_MPI_ENABLED -static void sunTimerStructReduceMaxAndSum(void* a, void* b, int* len, MPI_Datatype* dType) +static void sunTimerStructReduceMaxAndSum(void* a, void* b, int* len, + MPI_Datatype* dType) { - sunTimerStruct* a_ts = (sunTimerStruct*) a; - sunTimerStruct* b_ts = (sunTimerStruct*) b; + sunTimerStruct* a_ts = (sunTimerStruct*)a; + sunTimerStruct* b_ts = (sunTimerStruct*)b; int i; - for (i = 0; i < *len; ++i) { + for (i = 0; i < *len; ++i) + { b_ts[i].average += a_ts[i].elapsed; b_ts[i].maximum = SUNMAX(a_ts[i].maximum, b_ts[i].maximum); } @@ -416,24 +458,24 @@ int sunCollectTimers(SUNProfiler p) { int i, rank, nranks; - MPI_Comm comm = *((MPI_Comm*) p->comm); + MPI_Comm comm = *((MPI_Comm*)p->comm); MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &nranks); sunTimerStruct** values = NULL; /* Extract the elapsed times from the hash map */ - SUNHashMap_Values(p->map, (void***) &values, sizeof(sunTimerStruct)); - sunTimerStruct* reduced = (sunTimerStruct*) malloc(p->map->size*sizeof(sunTimerStruct)); - for (i = 0; i < p->map->size; ++i) - reduced[i] = *values[i]; + SUNHashMap_Values(p->map, (void***)&values, sizeof(sunTimerStruct)); + sunTimerStruct* reduced = + (sunTimerStruct*)malloc(p->map->size * sizeof(sunTimerStruct)); + for (i = 0; i < p->map->size; ++i) reduced[i] = *values[i]; /* Register MPI datatype for sunTimerStruct */ MPI_Datatype tmp_type, MPI_sunTimerStruct; - const int block_lens[2] = { 5, 1 }; - const MPI_Datatype types[2] = { MPI_DOUBLE, MPI_LONG }; - const MPI_Aint displ[2] = { offsetof(sunTimerStruct, tic), - offsetof(sunTimerStruct, count) }; + const int block_lens[2] = {5, 1}; + const MPI_Datatype types[2] = {MPI_DOUBLE, MPI_LONG}; + const MPI_Aint displ[2] = {offsetof(sunTimerStruct, tic), + offsetof(sunTimerStruct, count)}; MPI_Aint lb, extent; MPI_Type_create_struct(2, block_lens, displ, types, &tmp_type); @@ -464,15 +506,16 @@ int sunCollectTimers(SUNProfiler p) MPI_Op_free(&MPI_sunTimerStruct_MAXANDSUM); /* Update the values that are in this rank's hash map. */ - for (i = 0; i < p->map->size; ++i) { - values[i]->average = reduced[i].average / (realtype) nranks; + for (i = 0; i < p->map->size; ++i) + { + values[i]->average = reduced[i].average / (double)nranks; values[i]->maximum = reduced[i].maximum; } free(reduced); free(values); - return(0); + return (0); } #endif @@ -480,13 +523,15 @@ int sunCollectTimers(SUNProfiler p) max across ranks, average across ranks, and the timer counter. */ void sunPrintTimers(int idx, SUNHashMapKeyValue kv, FILE* fp, void* pvoid) { - SUNProfiler p = (SUNProfiler) pvoid; - sunTimerStruct* ts = (sunTimerStruct*) kv->value; - double maximum = ts->maximum; - double average = ts->average; - double percent = strcmp((const char*) kv->key, (const char*) SUNDIALS_ROOT_TIMER) ? maximum / p->sundials_time * 100 : 100; - fprintf(fp, "%-40s\t %6.2f%% \t %.6fs \t %.6fs \t %ld\n", - kv->key, percent, maximum, average, ts->count); + SUNProfiler p = (SUNProfiler)pvoid; + sunTimerStruct* ts = (sunTimerStruct*)kv->value; + double maximum = ts->maximum; + double average = ts->average; + double percent = strcmp((const char*)kv->key, (const char*)SUNDIALS_ROOT_TIMER) + ? maximum / p->sundials_time * 100 + : 100; + fprintf(fp, "%-40s\t %6.2f%% \t %.6fs \t %.6fs \t %ld\n", kv->key, + percent, maximum, average, ts->count); } /* Comparator for qsort that compares key-value pairs @@ -496,22 +541,45 @@ int sunCompareTimes(const void* l, const void* r) double left_max; double right_max; - const SUNHashMapKeyValue left = *((SUNHashMapKeyValue*) l); - const SUNHashMapKeyValue right = *((SUNHashMapKeyValue*) r); + const SUNHashMapKeyValue left = *((SUNHashMapKeyValue*)l); + const SUNHashMapKeyValue right = *((SUNHashMapKeyValue*)r); + + if (left == NULL && right == NULL) return (0); + if (left == NULL) return (1); + if (right == NULL) return (-1); + + left_max = ((sunTimerStruct*)left->value)->maximum; + right_max = ((sunTimerStruct*)right->value)->maximum; + + if (left_max < right_max) return (1); + if (left_max > right_max) return (-1); + return (0); +} + +int sunclock_gettime_monotonic(sunTimespec* ts) +{ +#if defined(SUNDIALS_HAVE_POSIX_TIMERS) + return clock_gettime(CLOCK_MONOTONIC, ts); +#elif (defined(WIN32) || defined(_WIN32)) + static LARGE_INTEGER ticks_per_sec; + LARGE_INTEGER ticks; - if (left == NULL && right == NULL) - return(0); - if (left == NULL) - return(1); - if (right == NULL) - return(-1); + if (!ticks_per_sec.QuadPart) + { + QueryPerformanceFrequency(&ticks_per_sec); + if (!ticks_per_sec.QuadPart) { return -1; } + } - left_max = ((sunTimerStruct*) left->value)->maximum; - right_max = ((sunTimerStruct*) right->value)->maximum; + QueryPerformanceCounter(&ticks); - if (left_max < right_max) - return(1); - if (left_max > right_max) - return(-1); - return(0); + /* QueryPerformanceCounter is ticks in microseconds */ + + ts->tv_sec = (long)(ticks.QuadPart / ticks_per_sec.QuadPart); + ts->tv_nsec = (long)(((ticks.QuadPart % ticks_per_sec.QuadPart) * 1000000) / + ticks_per_sec.QuadPart); + + return 0; +#else +#error SUNProfiler needs POSIX or Windows timers +#endif } diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt index fce631573e..77cfb20cad 100644 --- a/test/unit_tests/CMakeLists.txt +++ b/test/unit_tests/CMakeLists.txt @@ -43,4 +43,7 @@ endif() if(CXX_FOUND) add_subdirectory(reductions) add_subdirectory(sunmemory) + if(SUNDIALS_BUILD_WITH_PROFILING) + add_subdirectory(profiling) + endif() endif() diff --git a/test/unit_tests/arkode/CXX_serial/CMakeLists.txt b/test/unit_tests/arkode/CXX_serial/CMakeLists.txt index 84d040ad24..b0ea386c44 100644 --- a/test/unit_tests/arkode/CXX_serial/CMakeLists.txt +++ b/test/unit_tests/arkode/CXX_serial/CMakeLists.txt @@ -50,12 +50,23 @@ foreach(test_tuple ${unit_tests}) ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) - # Libraries to link against + # We explicitly choose which object libraries to link to and link in the + # arkode objects so that we have access to private functions w/o changing + # their visibility in the installed libraries. target_link_libraries(${test_target} - sundials_arkode - sundials_nvecserial + $ + sundials_sunmemsys_obj + sundials_nvecserial_obj + sundials_sunlinsolband_obj + sundials_sunlinsoldense_obj + sundials_sunnonlinsolnewton_obj + sundials_sunnonlinsolfixedpoint_obj ${EXE_EXTRA_LINK_LIBS}) + # Tell CMake that we depend on the ARKODE library since it does not pick + # that up from $. + add_dependencies(${test_target} sundials_arkode_obj) + endif() # Check if test args are provided and set the test name diff --git a/test/unit_tests/arkode/CXX_serial/ark_test_analytic_sys_mri.cpp b/test/unit_tests/arkode/CXX_serial/ark_test_analytic_sys_mri.cpp index 4abd91c925..ce0b9bdd04 100644 --- a/test/unit_tests/arkode/CXX_serial/ark_test_analytic_sys_mri.cpp +++ b/test/unit_tests/arkode/CXX_serial/ark_test_analytic_sys_mri.cpp @@ -22,9 +22,10 @@ *-----------------------------------------------------------------*/ // Header files -#include +#include #include -#include +#include +#include #include #include #include @@ -99,8 +100,8 @@ int main(int argc, char* argv[]) // if an argument supplied, set fixedpoint (otherwise use SUNFALSE) fixedpoint = SUNFALSE; - if (argc > 1) fixedpoint = stoi(argv[1], NULL); - if (argc > 2) Nt = stoi(argv[2], NULL); + if (argc > 1) fixedpoint = std::stoi(argv[1], NULL); + if (argc > 2) Nt = std::stoi(argv[2], NULL); // Initial problem output cout << "\nAnalytical ODE test problem:\n"; diff --git a/test/unit_tests/arkode/CXX_serial/ark_test_dahlquist_mri.cpp b/test/unit_tests/arkode/CXX_serial/ark_test_dahlquist_mri.cpp index 7f52a88d86..ffb23ec7c1 100644 --- a/test/unit_tests/arkode/CXX_serial/ark_test_dahlquist_mri.cpp +++ b/test/unit_tests/arkode/CXX_serial/ark_test_dahlquist_mri.cpp @@ -17,10 +17,11 @@ * ---------------------------------------------------------------------------*/ // Header files -#include +#include #include -#include +#include #include +#include #include #include @@ -91,12 +92,12 @@ int main(int argc, char* argv[]) UserData udata; // Check for inputs - if (argc > 1) udata.lambda_e = stod(argv[1]); - if (argc > 2) udata.lambda_i = stod(argv[2]); - if (argc > 3) udata.lambda_f = stod(argv[3]); - if (argc > 4) hs = stod(argv[4]); - if (argc > 5) hf = stod(argv[5]); - if (argc > 5) nsteps = stoi(argv[6]); + if (argc > 1) udata.lambda_e = std::stod(argv[1]); + if (argc > 2) udata.lambda_i = std::stod(argv[2]); + if (argc > 3) udata.lambda_f = std::stod(argv[3]); + if (argc > 4) hs = std::stod(argv[4]); + if (argc > 5) hf = std::stod(argv[5]); + if (argc > 5) nsteps = std::stoi(argv[6]); // Output problem setup cout << "\nDahlquist ODE test problem:\n"; diff --git a/test/unit_tests/arkode/C_serial/CMakeLists.txt b/test/unit_tests/arkode/C_serial/CMakeLists.txt index 2c4daa7d74..bddff54d88 100644 --- a/test/unit_tests/arkode/C_serial/CMakeLists.txt +++ b/test/unit_tests/arkode/C_serial/CMakeLists.txt @@ -55,12 +55,22 @@ foreach(test_tuple ${ARKODE_unit_tests}) ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) - # libraries to link against + # We explicitly choose which object libraries to link to and link in the + # arkode objects so that we have access to private functions w/o changing + # their visibility in the installed libraries. target_link_libraries(${test} - sundials_arkode - sundials_nvecserial + $ + sundials_sunmemsys_obj + sundials_nvecserial_obj + sundials_sunlinsolband_obj + sundials_sunlinsoldense_obj + sundials_sunnonlinsolnewton_obj ${EXE_EXTRA_LINK_LIBS}) + # Tell CMake that we depend on the ARKODE library since it does not pick + # that up from $. + add_dependencies(${test} sundials_arkode_obj) + endif() # check if test args are provided and set the test name diff --git a/test/unit_tests/profiling/CMakeLists.txt b/test/unit_tests/profiling/CMakeLists.txt new file mode 100644 index 0000000000..8c0ce56ea6 --- /dev/null +++ b/test/unit_tests/profiling/CMakeLists.txt @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------------------ +# Programmer(s): David J. Gardner @ LLNL +# ------------------------------------------------------------------------------ +# SUNDIALS Copyright Start +# Copyright (c) 2002-2023, Lawrence Livermore National Security +# and Southern Methodist University. +# All rights reserved. +# +# See the top-level LICENSE and NOTICE files for details. +# +# SPDX-License-Identifier: BSD-3-Clause +# SUNDIALS Copyright End +# ------------------------------------------------------------------------------ + +# List of test tuples of the form "name\;args" +set(unit_tests "test_profiling\;") + +# Add the build and install targets for each test +foreach(test_tuple ${unit_tests}) + + # parse the test tuple + list(GET test_tuple 0 test) + list(GET test_tuple 1 test_args) + + # check if this test has already been added, only need to add + # test source files once for testing with different inputs + if(NOT TARGET ${test}) + + # test source files + add_executable(${test} ${test}.cpp) + + set_target_properties(${test} PROPERTIES FOLDER "unit_tests") + + # include location of public and private header files + target_include_directories(${test} PRIVATE + $ + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/src) + + # libraries to link against + target_link_libraries(${test} + sundials_generic + ${EXE_EXTRA_LINK_LIBS}) + + endif() + + # check if test args are provided and set the test name + if("${test_args}" STREQUAL "") + set(test_name ${test}) + else() + string(REPLACE " " "_" test_name "${test}_${test_args}") + string(REPLACE " " ";" test_args "${test_args}") + endif() + + # add test to regression tests + add_test(NAME ${test_name} COMMAND ${test} ${test_args}) + +endforeach() + +message(STATUS "Added profiling units tests") diff --git a/test/unit_tests/profiling/test_profiling.cpp b/test/unit_tests/profiling/test_profiling.cpp new file mode 100644 index 0000000000..7228fd7c9c --- /dev/null +++ b/test/unit_tests/profiling/test_profiling.cpp @@ -0,0 +1,240 @@ +/* ----------------------------------------------------------------------------- + * Programmer(s): David J. Gardner @ LLNL + * ----------------------------------------------------------------------------- + * SUNDIALS Copyright Start + * Copyright (c) 2002-2023, Lawrence Livermore National Security + * and Southern Methodist University. + * All rights reserved. + * + * See the top-level LICENSE and NOTICE files for details. + * + * SPDX-License-Identifier: BSD-3-Clause + * SUNDIALS Copyright End + * ---------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +#include "sundials/sundials_math.h" +#include "sundials/sundials_profiler.h" + +int sleep(SUNProfiler prof, int sec, double* chrono) +{ + auto begin = std::chrono::steady_clock::now(); + + // We dont check the flag returned to avoid introducing extra + // overhead which makes it harder to compare the SUNProfiler + // time with the chrono time. + SUNProfiler_Begin(prof, "sleep"); + std::this_thread::sleep_for(std::chrono::seconds(sec)); + SUNProfiler_End(prof, "sleep"); + + auto end = std::chrono::steady_clock::now(); + + auto elapsed = + std::chrono::duration_cast(end - begin).count(); + *chrono = std::chrono::duration(elapsed).count() * 1e-9; + + return 0; +} + +int print_timings(SUNProfiler prof) +{ + // Output timing in default (table) format + int flag = SUNProfiler_Print(prof, stdout); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Print returned " << flag << "\n"; + return 1; + } + + return 0; +} + +int main() +{ + // ----- + // Setup + // ----- + + double chrono; + + std::cout << "Testing SUNProfiler\n"; + + SUNProfiler prof = nullptr; + int flag = SUNProfiler_Create(nullptr, "SUNProfiler Test", &prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Create returned " << flag << "\n"; + return 1; + } + + // ------ + // Test 1 + // ------ + + std::cout << "\nTest 1: sleep 1s, check timings \n"; + + flag = sleep(prof, 1, &chrono); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "sleep returned " << flag << "\n"; + return 1; + } + + flag = print_timings(prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "print_timings returned " << flag << "\n"; + return 1; + } + + double time = 0; + double resolution = 0; + flag = SUNProfiler_GetElapsedTime(prof, "sleep", &time); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_GetElapsedTime returned " << flag << "\n"; + return 1; + } + + flag = SUNProfiler_GetTimerResolution(prof, &resolution); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_GetTimerResolution returned " << flag << "\n"; + return 1; + } + + if (SUNRCompareTol(time, chrono, 1e-2)) + { + std::cerr << ">>> FAILURE: " + << "time recorded was " << time << "s, but expected " << chrono + << "s +/- " << 1e-2 << "\n"; + return 1; + } + + // ------ + // Test 2 + // ------ + + std::cout << "\nTest 2: reset, sleep 2s, print timings\n"; + + // Reset timing + flag = SUNProfiler_Reset(prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Reset returned " << flag << "\n"; + return 1; + } + + flag = sleep(prof, 2, &chrono); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "sleep returned " << flag << "\n"; + return 1; + } + + flag = print_timings(prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "print_timings returned " << flag << "\n"; + return 1; + } + + flag = SUNProfiler_GetElapsedTime(prof, "sleep", &time); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_GetElapsedTime returned " << flag << "\n"; + return 1; + } + + flag = SUNProfiler_GetTimerResolution(prof, &resolution); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_GetTimerResolution returned " << flag << "\n"; + return 1; + } + + if (SUNRCompareTol(time, chrono, 1e-2)) + { + std::cerr << ">>> FAILURE: " + << "time recorded was " << time << "s, but expected " << chrono + << "s +/- " << 1e-2 << "\n"; + return 1; + } + + // ------ + // Test 3 + // ------ + + std::cout << "\nTest 3: multiple outputs to a file for plot test\n"; + + std::FILE* fout = std::fopen("profiling_test_output.txt", "w"); + if (fout == nullptr) + { + std::cerr << ">>> FAILURE: " + << "fopen returned a null pointer\n"; + return 1; + } + + for (int i = 1; i < 4; i++) + { + // Reset timing + flag = SUNProfiler_Reset(prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Reset returned " << flag << "\n"; + return 1; + } + + flag = sleep(prof, i, &chrono); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "sleep returned " << flag << "\n"; + return 1; + } + + flag = SUNProfiler_Print(prof, fout); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Print returned " << flag << "\n"; + return 1; + } + } + + std::fclose(fout); + + // -------- + // Clean up + // -------- + + flag = SUNProfiler_Free(&prof); + if (flag) + { + std::cerr << ">>> FAILURE: " + << "SUNProfiler_Free returned " << flag << "\n"; + return 1; + } + + std::cout << "\nSUCCESS - test complete\n"; + + return 0; +}