timer_registrar.h Source File

CPP API: timer_registrar.h Source File
timer_registrar.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Rene Schmieding
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef MIO_TIMER_TIMER_REGISTRAR_H
21 #define MIO_TIMER_TIMER_REGISTRAR_H
22 
29 #include "memilio/utils/miompi.h"
30 
31 #include <cassert>
32 #include <iostream>
33 #include <list>
34 #include <memory>
35 #include <mutex>
36 
37 namespace mio
38 {
39 namespace timing
40 {
41 
44 {
45 public:
57  {
58  static TimerRegistrar t;
59  return t;
60  }
61 
72  void add_timer(TimerRegistration&& registration)
73  {
74  m_registration_lock.lock();
75  m_register.emplace_back(std::move(registration));
76  m_registration_lock.unlock();
77  }
78 
80  const auto& get_register() const
81  {
82  return m_register;
83  }
84 
92  void print_timers(std::ostream& out = std::cout) const
93  {
94  PRAGMA_OMP(single nowait)
95  {
96  m_printer->print(m_register, out);
97  }
98  }
99 
108  void print_gathered_timers(mpi::Comm comm, std::ostream& out = std::cout)
109  {
110  PRAGMA_OMP(single nowait)
111  {
112  std::list<BasicTimer> external_timers; // temporary storage for gathered registrations
113  std::list<TimerRegistration> gathered_register; // combined registrations of all ranks
114  gather_timers(comm, external_timers, gathered_register);
115  if (mpi::is_root()) {
116  m_printer->print(gathered_register, out);
117  }
118  }
119  }
120 
123  {
124  m_print_on_death = false;
125  }
126 
140  void set_printer(std::unique_ptr<Printer>&& printer)
141  {
142  m_printer.swap(printer);
143  // old value of m_printer (now stored in printer) is deleted at end of scope
144  }
145 
152  void operator=(TimerRegistrar&) = delete;
153  void operator=(TimerRegistrar&&) = delete;
156 private:
158  TimerRegistrar() = default;
159 
162  {
163  if (m_print_on_death) {
164  PRAGMA_OMP(single nowait)
165  {
166  // print_timers already uses a "single nowait", but the cout for titling the table needs one, too
167  std::cout << "Final Timer Summary\n";
168  print_timers();
169  }
170  }
171  }
172 
174  void gather_timers(mpi::Comm comm, std::list<BasicTimer>& external_timers,
175  std::list<TimerRegistration>& gathered_register) const
176  {
177 #ifndef MEMILIO_ENABLE_MPI
178  mio::unused(comm, external_timers);
179 #endif
180  if (mpi::is_root()) {
181  std::ranges::transform(m_register, std::back_inserter(gathered_register), [](const TimerRegistration& r) {
182  return TimerRegistration{r.name, r.scope, r.timer, r.thread_id, 0};
183  });
184  }
185  if (comm == nullptr) {
186  if (mpi::is_root()) {
187  log_error("Got nullptr as MPI Comm. Only timers on root rank are gathered.");
188  }
189  return;
190  }
191 #ifdef MEMILIO_ENABLE_MPI
192  // name, scope, timer, thread_id
193  using GatherableRegistration = std::tuple<std::string, std::string, BasicTimer, int>;
194  int comm_size;
195  MPI_Comm_size(comm, &comm_size);
196 
197  if (mpi::is_root()) {
198  for (int snd_rank = 1; snd_rank < comm_size; snd_rank++) { // skip root rank!
199  int bytes_size;
200  MPI_Recv(&bytes_size, 1, MPI_INT, snd_rank, 0, comm, MPI_STATUS_IGNORE);
201  ByteStream bytes(bytes_size);
202  MPI_Recv(bytes.data(), bytes.data_size(), MPI_BYTE, snd_rank, 0, mpi::get_world(), MPI_STATUS_IGNORE);
203 
204  auto rec_register = deserialize_binary(bytes, Tag<std::vector<GatherableRegistration>>{});
205  if (!rec_register) {
206  log_error("Error receiving ensemble results from rank {}.", snd_rank);
207  }
208  std::ranges::transform(
209  rec_register.value(), std::back_inserter(gathered_register), [&](const GatherableRegistration& r) {
210  const auto& [name, scope, timer, thread_id] = r;
211  external_timers.push_back(timer);
212  return TimerRegistration{name, scope, external_timers.back(), thread_id, snd_rank};
213  });
214  }
215  }
216  else {
217  std::vector<GatherableRegistration> snd_register;
218  std::ranges::transform(m_register, std::back_inserter(snd_register), [](const TimerRegistration& r) {
219  return GatherableRegistration{r.name, r.scope, r.timer, r.thread_id};
220  });
221  ByteStream bytes = serialize_binary(snd_register);
222  int bytes_size = int(bytes.data_size());
223  MPI_Send(&bytes_size, 1, MPI_INT, 0, 0, comm);
224  MPI_Send(bytes.data(), bytes.data_size(), MPI_BYTE, 0, 0, comm);
225  }
226 #endif
227  }
228 
229  std::unique_ptr<Printer> m_printer = std::make_unique<TablePrinter>();
230  bool m_print_on_death = true;
231  std::list<TimerRegistration> m_register;
232  std::mutex m_registration_lock;
233 };
234 
235 } // namespace timing
236 } // namespace mio
237 
238 #endif // MIO_TIMER_TIMER_REGISTRAR_H
In-memory stream of bytes.
Definition: binary_serializer.h:39
const unsigned char * data() const
Get the pointer to the buffer of the stream.
Definition: binary_serializer.h:111
size_t data_size() const
Get the size of the buffer of the stream.
Definition: binary_serializer.h:124
TimerRegistrar is a singleton to keep track of and print timers. Does not manage storage.
Definition: timer_registrar.h:44
void operator=(TimerRegistrar &&)=delete
TimerRegistrar must not be copied or moved, use TimerRegistrar::get_instance() to access it.
const auto & get_register() const
Returns a read only list of all TimerRegistrations. Can be used to print or evaluate timers.
Definition: timer_registrar.h:80
~TimerRegistrar()
Specify a destructor to allow printing all timers after exit from main.
Definition: timer_registrar.h:161
void gather_timers(mpi::Comm comm, std::list< BasicTimer > &external_timers, std::list< TimerRegistration > &gathered_register) const
Gather timers from all ranks, using external_timers as temporary timer storage for gathered_register.
Definition: timer_registrar.h:174
void set_printer(std::unique_ptr< Printer > &&printer)
Replace the internal printer used for print_timers.
Definition: timer_registrar.h:140
std::unique_ptr< Printer > m_printer
A printer to visualize all timers.
Definition: timer_registrar.h:229
void add_timer(TimerRegistration &&registration)
Add a new timer to the TimerRegistrar.
Definition: timer_registrar.h:72
TimerRegistrar()=default
Instead of constructing a TimerRegistrar, use its static method TimerRegistrar::get_instance().
static TimerRegistrar & get_instance()
Access the TimerRegistrar.
Definition: timer_registrar.h:56
TimerRegistrar(TimerRegistrar &&)=delete
TimerRegistrar must not be copied or moved, use TimerRegistrar::get_instance() to access it.
void print_gathered_timers(mpi::Comm comm, std::ostream &out=std::cout)
Print all timers gathered on the given MPI Comm using a Printer.
Definition: timer_registrar.h:108
TimerRegistrar(TimerRegistrar &)=delete
TimerRegistrar must not be copied or moved, use TimerRegistrar::get_instance() to access it.
std::list< TimerRegistration > m_register
List that allows access to timers without having their name.
Definition: timer_registrar.h:231
std::mutex m_registration_lock
Lock to safeguard m_register against concurrent writes.
Definition: timer_registrar.h:232
void print_timers(std::ostream &out=std::cout) const
Print all timers using a Printer.
Definition: timer_registrar.h:92
void operator=(TimerRegistrar &)=delete
TimerRegistrar must not be copied or moved, use TimerRegistrar::get_instance() to access it.
bool m_print_on_death
Whether to call m_printer during the destructor.
Definition: timer_registrar.h:230
void disable_final_timer_summary()
Prevent the TimerRegistrar from calling print_timers on exit from main.
Definition: timer_registrar.h:122
#define PRAGMA_OMP(x)
Macro for OpenMP directives.
Definition: mioomp.h:43
Comm get_world()
Get the global MPI communicator.
Definition: miompi.cpp:32
bool is_root()
Returns true if the calling process is the root process.
Definition: miompi.cpp:58
void * Comm
Alias for MPI_Comm, to be used in APIs to avoid having to use #ifdef everywhere.
Definition: miompi.h:40
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:407
IOResult< T > deserialize_binary(ByteStream &stream, Tag< T >, int flags=0)
Deserialize an object from binary format.
Definition: binary_serializer.h:468
void log_error(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:100
ByteStream serialize_binary(const T &t, int flags=0)
Serialize an object into binary format.
Definition: binary_serializer.h:452
void unused(T &&...)
Does nothing, can be used to mark variables as not used.
Definition: compiler_diagnostics.h:30
Struct used to register (.
Definition: registration.h:35
std::string scope
Definition: registration.h:37
std::string name
Definition: registration.h:36
BasicTimer & timer
Definition: registration.h:38
int thread_id
Definition: registration.h:39