From db0d26e751f6ed073686c4cdfd95da09150c6e63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Fri, 6 Oct 2023 19:22:04 +0200
Subject: [PATCH] Print execution statistics at the end of execution

First sequential implementation
---
 src/main.cpp                       |  7 ++-
 src/utils/CMakeLists.txt           |  1 +
 src/utils/ExecutionStatManager.cpp | 84 ++++++++++++++++++++++++++++++
 src/utils/ExecutionStatManager.hpp | 28 ++++++++++
 4 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 src/utils/ExecutionStatManager.cpp
 create mode 100644 src/utils/ExecutionStatManager.hpp

diff --git a/src/main.cpp b/src/main.cpp
index 7e03cdc80..bcd10541d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,6 +4,7 @@
 #include <mesh/DualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
+#include <utils/ExecutionStatManager.hpp>
 #include <utils/PugsUtils.hpp>
 #include <utils/RandomEngine.hpp>
 
@@ -18,14 +19,18 @@ main(int argc, char* argv[])
   MeshDataManager::create();
   DualConnectivityManager::create();
   DualMeshManager::create();
+  ExecutionStatManager::create();
 
   parser(filename);
 
+  ExecutionStatManager::printInfo();
+
+  ExecutionStatManager::destroy();
   DualMeshManager::destroy();
   DualConnectivityManager::destroy();
   MeshDataManager::destroy();
-  RandomEngine::destroy();
   QuadratureManager::destroy();
+  RandomEngine::destroy();
   SynchronizerManager::destroy();
 
   finalize();
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index c4070ff64..5478323a7 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 000000000..0ea87c550
--- /dev/null
+++ b/src/utils/ExecutionStatManager.cpp
@@ -0,0 +1,84 @@
+#include <utils/ExecutionStatManager.hpp>
+
+#include <utils/Exceptions.hpp>
+#include <utils/Messenger.hpp>
+
+#include <cmath>
+#include <rang.hpp>
+#include <sys/resource.h>
+
+ExecutionStatManager* ExecutionStatManager::m_instance = nullptr;
+
+void
+ExecutionStatManager::_printMaxResidentMemory() const
+{
+  const std::vector<std::string> units = {"B", "KB", "MB", "GB", "TB", "PB", "EB"};
+
+  double count = [] {
+    rusage u;
+    getrusage(RUSAGE_SELF, &u);
+    return u.ru_maxrss * 1024;
+  }();
+
+  size_t i_unit = 0;
+  while ((count >= 1024) and (i_unit < units.size())) {
+    ++i_unit;
+    count /= 1024;
+  }
+
+  std::cout << rang::style::bold << "Maximum memory: " << rang::fgB::cyan << count << rang::style::reset
+            << rang::style::bold << units[i_unit] << rang::style::reset << '\n';
+}
+
+void
+ExecutionStatManager::_printElapseTime() const
+{
+  std::cout << rang::style::bold << "Execution time: " << rang::fgB::cyan << m_instance->m_elapse_time.seconds()
+            << rang::style::reset << rang::style::bold << 's' << rang::style::reset << rang::fg::reset << '\n';
+}
+
+void
+ExecutionStatManager::_printTotalCPUTime() const
+{
+  rusage u;
+  getrusage(RUSAGE_SELF, &u);
+
+  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 << rang::style::bold << "Total CPU time: " << rang::fgB::cyan << total_cpu_time << rang::style::reset
+            << rang::style::bold << 's' << rang::style::reset << rang::fg::reset << '\n';
+  std::cout << " Using " << parallel::allReduceSum(Kokkos::DefaultHostExecutionSpace::concurrency())
+            << " threads distributed over " << parallel::size() << " processes"
+            << "\n";
+}
+
+void
+ExecutionStatManager::printInfo()
+{
+  std::cout << "----------------- " << rang::fgB::green << "pugs exec stats" << rang::fg::reset
+            << " ---------------------\n";
+
+  ExecutionStatManager::m_instance->_printElapseTime();
+  ExecutionStatManager::m_instance->_printTotalCPUTime();
+  ExecutionStatManager::m_instance->_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 000000000..4728366bc
--- /dev/null
+++ b/src/utils/ExecutionStatManager.hpp
@@ -0,0 +1,28 @@
+#ifndef EXECUTION_STAT_MANAGER_HPP
+#define EXECUTION_STAT_MANAGER_HPP
+
+#include <utils/Timer.hpp>
+
+class ExecutionStatManager
+{
+ private:
+  static ExecutionStatManager* m_instance;
+
+  Timer m_elapse_time;
+
+  void _printMaxResidentMemory() const;
+  void _printElapseTime() const;
+  void _printTotalCPUTime() const;
+
+  explicit ExecutionStatManager()                   = default;
+  ExecutionStatManager(ExecutionStatManager&&)      = delete;
+  ExecutionStatManager(const ExecutionStatManager&) = delete;
+  ~ExecutionStatManager()                           = default;
+
+ public:
+  static void printInfo();
+  static void create();
+  static void destroy();
+};
+
+#endif   // EXECUTION_STAT_MANAGER_HPP
-- 
GitLab