diff --git a/cmake/PugsDoc.cmake b/cmake/PugsDoc.cmake index b067a5cc828ec0c245e8d51c0bda46a96dcce702..2be770b3a3163c6ea490413b0f73a1f4f828c82f 100644 --- a/cmake/PugsDoc.cmake +++ b/cmake/PugsDoc.cmake @@ -97,7 +97,19 @@ if (EMACS AND GNUPLOT_FOUND AND GMSH) COMMENT "Building user documentation in doc/userdoc.pdf" VERBATIM) - add_custom_target(userdoc-pdf DEPENDS pugsdoc-dir "${PUGS_BINARY_DIR}/doc/userdoc.pdf" ) + configure_file("${PUGS_SOURCE_DIR}/doc/build-userdoc-pdf.sh.in" + "${PUGS_BINARY_DIR}/doc/build-userdoc-pdf.sh" + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + @ONLY) + + set_source_files_properties( + ${PUGS_BINARY_DIR}/build-pdf.sh2 + PROPERTIES + GENERATED TRUE + HEADER_FILE_ONLY TRUE + ) + + add_custom_target(userdoc-pdf DEPENDS pugsdoc-dir "${PUGS_BINARY_DIR}/doc/userdoc.pdf" "${PUGS_BINARY_DIR}/doc/build-userdoc-pdf.sh") add_dependencies(userdoc userdoc-pdf) diff --git a/doc/build-userdoc-pdf.sh.in b/doc/build-userdoc-pdf.sh.in new file mode 100644 index 0000000000000000000000000000000000000000..9445e7e75cbb62486a4aa3647fa41a171adb7087 --- /dev/null +++ b/doc/build-userdoc-pdf.sh.in @@ -0,0 +1,5 @@ +#! /usr/bin/env bash + +@PDFLATEX_COMPILER@ -shell-escape -interaction nonstopmode userdoc +@PDFLATEX_COMPILER@ -shell-escape -interaction nonstopmode userdoc +@PDFLATEX_COMPILER@ -shell-escape -interaction nonstopmode userdoc diff --git a/doc/lisp/build-doc-config.el b/doc/lisp/build-doc-config.el index 1da3a5c9fecf3540dfd701380a340722379642cf..6f06400a6dc1176370550342ea7bfb04ac976c11 100644 --- a/doc/lisp/build-doc-config.el +++ b/doc/lisp/build-doc-config.el @@ -64,9 +64,7 @@ (setq org-latex-listings 'minted org-latex-packages-alist '(("" "minted")) org-latex-pdf-process - '("cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" - "cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f" - "cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")) + '("cd ${PUGS_BINARY_DIR}/doc; ./build-userdoc-pdf.sh")) (setq python-indent-guess-indent-offset-verbose nil) diff --git a/doc/lisp/share/ob-pugs-error.el b/doc/lisp/share/ob-pugs-error.el index 1e4680cbcc71729be805622081b37dfad6176d9c..e2228f9f4cc3ca0a0fcb65d32d8c2f8780419229 100644 --- a/doc/lisp/share/ob-pugs-error.el +++ b/doc/lisp/share/ob-pugs-error.el @@ -131,7 +131,7 @@ (with-temp-file in-file (insert body)) (org-babel-eval - (format "${PUGS} --no-preamble --no-color %s 2>&1 | sed 's@/.*\.pgs:@test.pgs:@'" + (format "${PUGS} --no-exec-stat --no-preamble --no-color --threads=1 %s 2>&1 | sed 's@/.*\.pgs:@test.pgs:@'" (org-babel-process-file-name in-file)) ""))) diff --git a/doc/lisp/share/ob-pugs.el b/doc/lisp/share/ob-pugs.el index 6c3d72956d519bea339b63cb883c57f9bf1cd000..fb346b80b68f95a10b214192a99c8a1e1d8f4a48 100644 --- a/doc/lisp/share/ob-pugs.el +++ b/doc/lisp/share/ob-pugs.el @@ -130,7 +130,7 @@ (with-temp-file in-file (insert body)) (org-babel-eval - (format "${PUGS} --no-preamble --no-color %s" + (format "${PUGS} --no-exec-stat --no-preamble --no-color --threads=1 %s" (org-babel-process-file-name in-file)) ""))) diff --git a/doc/userdoc.org b/doc/userdoc.org index 490db865223c800e05ee0ee31b52bd86486f8872..b80850ee590d3416622539b6aa4e9b3b9665d092 100644 --- a/doc/userdoc.org +++ b/doc/userdoc.org @@ -391,8 +391,8 @@ answer a specific need. It must not be done /because it is possible to do it/! #+begin_verse -When designing a language, the difficulty is not to offer new functionalities,\\ -it is generally to decide not to offer them.\\ +When designing a language, the difficulty is not to offer new functionalities, +it is generally to decide not to offer them. --- Bjarne Stroustrup, C++ conference 2021. #+end_verse @@ -1390,7 +1390,7 @@ they follow a few rules. When comparing a boolean value (type ~B~) with another scalar value type (~N~, ~Z~ or ~R~), the value ~true~ is interpreted as $1$ and the value ~false~ as $0$. -\\ + \\ For vector and matrix basic types, the only allowed operators are ~==~ and ~!=~. #+begin_src latex :results drawer :exports results @@ -1406,7 +1406,7 @@ they follow a few rules. \right. \end{equation*} #+end_src -\\ + This is also the case for ~string~ values: only allowed operators are ~==~ and ~!=~. #+begin_src latex :results drawer :exports results @@ -3136,7 +3136,6 @@ available in parallel ***** Item types -\\ The following functions are used to designate a specific ~item_type~ - ~cell: void -> item_type~ - ~face: void -> item_type~ diff --git a/src/main.cpp b/src/main.cpp index 0cae9351ff97396484cf28f52c242692ce9299f5..9fc0b67439e128e958552a7b8c72cc354fc2076c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,12 +5,14 @@ #include <mesh/DualMeshManager.hpp> #include <mesh/MeshDataManager.hpp> #include <mesh/SynchronizerManager.hpp> +#include <utils/ExecutionStatManager.hpp> #include <utils/PugsUtils.hpp> #include <utils/RandomEngine.hpp> int main(int argc, char* argv[]) { + ExecutionStatManager::create(); ParallelChecker::create(); std::string filename = initialize(argc, argv); @@ -23,17 +25,19 @@ main(int argc, char* argv[]) DualMeshManager::create(); parser(filename); + ExecutionStatManager::printInfo(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); MeshDataManager::destroy(); - RandomEngine::destroy(); QuadratureManager::destroy(); + RandomEngine::destroy(); SynchronizerManager::destroy(); finalize(); ParallelChecker::destroy(); + ExecutionStatManager::destroy(); return 0; } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ca07c7dc6882942600e2c5e9b216ca1998fbbd71..82b31ae419fcb2f16b314aef5d4d55b4bb83e2fb 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -8,6 +8,7 @@ add_library( ConsoleManager.cpp Demangle.cpp Exceptions.cpp + ExecutionStatManager.cpp FPEManager.cpp Messenger.cpp Partitioner.cpp diff --git a/src/utils/ExecutionStatManager.cpp b/src/utils/ExecutionStatManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8fbd32e2c3a200808cd91d26fd845df7a1eb256a --- /dev/null +++ b/src/utils/ExecutionStatManager.cpp @@ -0,0 +1,150 @@ +#include <utils/ExecutionStatManager.hpp> + +#include <utils/Exceptions.hpp> +#include <utils/Messenger.hpp> + +#include <cmath> +#include <iomanip> +#include <rang.hpp> +#include <sys/resource.h> + +ExecutionStatManager* ExecutionStatManager::m_instance = nullptr; + +void +ExecutionStatManager::_printMaxResidentMemory() const +{ + class Memory + { + private: + double m_value; + + public: + PUGS_INLINE const double& + value() const + { + return m_value; + } + + std::string + prettyPrint() const + { + const std::vector<std::string> units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}; + + double local_memory = m_value; + size_t i_unit = 0; + while ((local_memory >= 1024) and (i_unit < units.size())) { + ++i_unit; + local_memory /= 1024; + } + std::ostringstream os; + os << local_memory << units[i_unit]; + return os.str(); + } + + Memory() + { + rusage u; + getrusage(RUSAGE_SELF, &u); + m_value = u.ru_maxrss * 1024; + } + + Memory(double value) : m_value{value} {} + }; + + Memory memory; + std::cout << "Memory: " << rang::style::bold << Memory{parallel::allReduceSum(memory.value())}.prettyPrint() + << rang::style::reset; + if (parallel::size() > 1) { + std::cout << " (over " << parallel::size() << " processes)"; + std::cout << " Avg: " << rang::style::bold + << Memory{parallel::allReduceSum(memory.value()) / parallel::size()}.prettyPrint() << rang::style::reset; + std::cout << " Min: " << rang::style::bold << Memory{parallel::allReduceMin(memory.value())}.prettyPrint() + << rang::style::reset; + std::cout << " Max: " << rang::style::bold << Memory{parallel::allReduceMax(memory.value())}.prettyPrint() + << rang::style::reset; + } + std::cout << '\n'; +} + +void +ExecutionStatManager::_printElapseTime() const +{ + std::cout << "Execution: " << rang::style::bold << m_instance->m_elapse_time.seconds() << 's' << rang::style::reset + << '\n'; +} + +void +ExecutionStatManager::_printTotalCPUTime() const +{ + rusage u; + getrusage(RUSAGE_SELF, &u); + + const double total_cpu_time = + u.ru_utime.tv_sec + u.ru_stime.tv_sec + (u.ru_utime.tv_usec + u.ru_stime.tv_usec) * 1E-6; + + std::cout << "Total CPU: " << rang::style::bold << parallel::allReduceSum(total_cpu_time) << 's' + << rang::style::reset; + std::cout << " (" << parallel::allReduceSum(Kokkos::DefaultHostExecutionSpace::concurrency()) << " threads over " + << parallel::size() << " processes)"; + if (total_cpu_time > 60) { + size_t seconds = std::floor(total_cpu_time); + const size_t days = seconds / (24 * 3600); + seconds -= days * (24 * 3600); + const size_t hours = seconds / 3600; + seconds -= hours * 3600; + const size_t minutes = seconds / 60; + seconds -= minutes * 60; + std::cout << " " << rang::style::bold; + bool print = false; + if (days > 0) { + print = true; + std::cout << days << "d" << ' '; + } + if (print or (hours > 0)) { + print = true; + std::cout << std::setw(2) << std::setfill('0') << hours << "h"; + } + if (print or (minutes > 0)) { + print = true; + std::cout << std::setw(2) << std::setfill('0') << minutes << "mn"; + } + if (print) { + std::cout << rang::style::bold << std::setw(2) << std::setfill('0') << seconds << "s"; + } + std::cout << rang::style::reset; + } + std::cout << '\n'; +} + +void +ExecutionStatManager::printInfo() +{ + if (ExecutionStatManager::getInstance().doPrint()) { + std::cout << "----------------- " << rang::fg::green << "pugs exec stats" << rang::fg::reset + << " ---------------------\n"; + + ExecutionStatManager::getInstance()._printElapseTime(); + ExecutionStatManager::getInstance()._printTotalCPUTime(); + ExecutionStatManager::getInstance()._printMaxResidentMemory(); + } +} + +void +ExecutionStatManager::create() +{ + if (ExecutionStatManager::m_instance == nullptr) { + ExecutionStatManager::m_instance = new ExecutionStatManager; + } else { + throw UnexpectedError("ExecutionStatManager already created"); + } +} + +void +ExecutionStatManager::destroy() +{ + // One allows multiple destruction to handle unexpected code exit + if (ExecutionStatManager::m_instance != nullptr) { + delete ExecutionStatManager::m_instance; + ExecutionStatManager::m_instance = nullptr; + } +} diff --git a/src/utils/ExecutionStatManager.hpp b/src/utils/ExecutionStatManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..476b31a84f80234a6e8d509ddac88daf9d3b38f8 --- /dev/null +++ b/src/utils/ExecutionStatManager.hpp @@ -0,0 +1,52 @@ +#ifndef EXECUTION_STAT_MANAGER_HPP +#define EXECUTION_STAT_MANAGER_HPP + +#include <utils/PugsAssert.hpp> +#include <utils/Timer.hpp> + +class ExecutionStatManager +{ + private: + static ExecutionStatManager* m_instance; + + Timer m_elapse_time; + bool m_do_print = true; + + void _printMaxResidentMemory() const; + void _printElapseTime() const; + void _printTotalCPUTime() const; + + explicit ExecutionStatManager() = default; + ExecutionStatManager(ExecutionStatManager&&) = delete; + ExecutionStatManager(const ExecutionStatManager&) = delete; + ~ExecutionStatManager() = default; + + public: + PUGS_INLINE + bool + doPrint() const + { + return m_do_print; + } + + PUGS_INLINE + void + setPrint(bool do_print) + { + m_do_print = do_print; + } + + PUGS_INLINE + static ExecutionStatManager& + getInstance() + { + Assert(m_instance != nullptr); // LCOV_EXCL_LINE + return *m_instance; + } + + static void printInfo(); + static void create(); + static void destroy(); +}; + +#endif // EXECUTION_STAT_MANAGER_HPP diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp index e4e905765f964b0760a10fa3349315e7ce15459d..2919ff18866dce74847bf672b61f0200f6a77dc6 100644 --- a/src/utils/PugsUtils.cpp +++ b/src/utils/PugsUtils.cpp @@ -5,6 +5,7 @@ #include <utils/BuildInfo.hpp> #include <utils/CommunicatorManager.hpp> #include <utils/ConsoleManager.hpp> +#include <utils/ExecutionStatManager.hpp> #include <utils/FPEManager.hpp> #include <utils/Messenger.hpp> #include <utils/PETScWrapper.hpp> @@ -115,8 +116,12 @@ initialize(int& argc, char* argv[]) bool show_preamble = true; app.add_flag("--preamble,!--no-preamble", show_preamble, "Show execution info preamble [default: true]"); - bool show_backtrace = false; - app.add_flag("-b,--backtrace,!--no-backtrace", show_backtrace, "Show backtrace on failure [default: false]"); + bool print_exec_stat = true; + app.add_flag("--exec-stat,!--no-exec-stat", print_exec_stat, + "Display memory and CPU usage after execution [default: true]"); + + bool show_backtrace = true; + app.add_flag("-b,--backtrace,!--no-backtrace", show_backtrace, "Show backtrace on failure [default: true]"); app.add_flag("--signal,!--no-signal", enable_signals, "Catches signals [default: true]"); @@ -157,6 +162,7 @@ initialize(int& argc, char* argv[]) CommunicatorManager::setSplitColor(mpi_split_color); } + ExecutionStatManager::getInstance().setPrint(print_exec_stat); BacktraceManager::setShow(show_backtrace); ConsoleManager::setShowPreamble(show_preamble); ConsoleManager::init(enable_color);