Skip to content

Commit cf6c1cd

Browse files
committed
Simple mechanism for collecting statistics during execution
This adds a simple class that can be used to (optionally) collect statistics about a program execution. This includes information bout the program (e.g. number of reactors, reactions, posts, etc) and about the execution (e.g. number of processed events and reactions as well as the number of actions triggered).
1 parent 9a7b0a9 commit cf6c1cd

File tree

9 files changed

+133
-6
lines changed

9 files changed

+133
-6
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
4040
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
4141
endif()
4242

43+
option(REACTOR_CPP_PRINT_STATISTICS "Print statistics after execution" OFF)
4344
option(REACTOR_CPP_TRACE "Enable tracing" OFF)
4445
option(REACTOR_CPP_VALIDATE "Enable runtime validation" ON)
4546
if (NOT DEFINED REACTOR_CPP_LOG_LEVEL)

include/reactor-cpp/config.hh.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef REACTOR_CPP_CONFIG_HH
22
#define REACTOR_CPP_CONFIG_HH
33

4+
// NOLINTNEXTLINE
5+
#cmakedefine REACTOR_CPP_PRINT_STATISTICS
46
// NOLINTNEXTLINE
57
#cmakedefine REACTOR_CPP_TRACE
68
// NOLINTNEXTLINE

include/reactor-cpp/logging.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#ifndef REACTOR_CPP_LOGGING_HH
1010
#define REACTOR_CPP_LOGGING_HH
1111

12-
#include "reactor-cpp/config.hh" //NOLINT
12+
#include "reactor-cpp/config.hh"
1313
#include "reactor-cpp/time.hh"
1414
#include <chrono>
1515
#include <iostream>

include/reactor-cpp/scheduler.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ private:
165165
void terminate_all_workers();
166166
void set_port_helper(BasePort* port);
167167

168+
void advance_logical_time_to(const Tag& tag);
169+
168170
public:
169171
explicit Scheduler(Environment* env);
170172
~Scheduler();

include/reactor-cpp/statistics.hh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (C) 2023 TU Dresden
3+
* All rights reserved.
4+
*
5+
* Authors:
6+
* Christian Menard
7+
*/
8+
9+
#ifndef REACTOR_CPP_STATISTICS_HH
10+
#define REACTOR_CPP_STATISTICS_HH
11+
12+
#include <atomic>
13+
14+
#include "reactor-cpp/config.hh"
15+
#include "reactor-cpp/logging.hh"
16+
17+
namespace reactor {
18+
19+
class Statistics {
20+
private:
21+
#ifdef REACTOR_CPP_PRINT_STATISTICS
22+
constexpr static bool enabled_{true};
23+
#else
24+
constexpr static bool enabled_{false};
25+
#endif
26+
27+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
28+
inline static std::atomic_size_t reactor_instances_{0};
29+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
30+
inline static std::atomic_size_t connections_{0};
31+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
32+
inline static std::atomic_size_t reactions_{0};
33+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
34+
inline static std::atomic_size_t actions_{0};
35+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
36+
inline static std::atomic_size_t ports_{0};
37+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
38+
inline static std::atomic_size_t processed_events_{0};
39+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
40+
inline static std::atomic_size_t processed_reactions_{0};
41+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
42+
inline static std::atomic_size_t triggered_actions_{0};
43+
44+
inline static void increment(std::atomic_size_t& counter) {
45+
if constexpr (enabled_) {
46+
counter.fetch_add(1, std::memory_order_release);
47+
}
48+
}
49+
50+
public:
51+
inline static void increment_reactor_instances() { increment(reactor_instances_); }
52+
inline static void increment_connections() { increment(connections_); }
53+
inline static void increment_reactions() { increment(reactions_); }
54+
inline static void increment_actions() { increment(actions_); }
55+
inline static void increment_ports() { increment(ports_); }
56+
inline static void increment_processed_events() { increment(processed_events_); }
57+
inline static void increment_processed_reactions() { increment(processed_reactions_); }
58+
inline static void increment_triggered_actions() { increment(triggered_actions_); }
59+
60+
inline static auto reactor_instances() { return reactor_instances_.load(std::memory_order_acquire); }
61+
inline static auto connections() { return connections_.load(std::memory_order_acquire); }
62+
inline static auto reactions() { return reactions_.load(std::memory_order_acquire); }
63+
inline static auto actions() { return actions_.load(std::memory_order_acquire); }
64+
inline static auto ports() { return ports_.load(std::memory_order_acquire); }
65+
inline static auto processed_events() { return processed_events_.load(std::memory_order_acquire); }
66+
inline static auto processed_reactions() { return processed_reactions_.load(std::memory_order_acquire); }
67+
inline static auto triggered_actions() { return triggered_actions_.load(std::memory_order_acquire); }
68+
69+
inline static void print() {
70+
if constexpr (enabled_) {
71+
reactor::log::Info() << "-----------------------------------------------------------";
72+
reactor::log::Info() << "Program statistics:";
73+
reactor::log::Info() << " - number of reactors: " << reactor_instances();
74+
reactor::log::Info() << " - number of connections: " << connections();
75+
reactor::log::Info() << " - number of reactions " << reactions();
76+
reactor::log::Info() << " - number of actions: " << actions();
77+
reactor::log::Info() << " - number of ports: " << ports();
78+
reactor::log::Info() << "Execution statistics:";
79+
reactor::log::Info() << " - processed events: " << processed_events();
80+
reactor::log::Info() << " - triggered actions: " << triggered_actions();
81+
reactor::log::Info() << " - processed reactions: " << processed_reactions();
82+
reactor::log::Info() << "-----------------------------------------------------------";
83+
}
84+
}
85+
};
86+
87+
} // namespace reactor
88+
89+
#endif // REACTOR_CPP_STATISTICS_HH

lib/environment.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "reactor-cpp/logging.hh"
2020
#include "reactor-cpp/port.hh"
2121
#include "reactor-cpp/reaction.hh"
22+
#include "reactor-cpp/statistics.hh"
2223
#include "reactor-cpp/time.hh"
2324

2425
namespace reactor {
@@ -288,6 +289,11 @@ auto Environment::startup(const TimePoint& start_time) -> std::thread {
288289
for (auto& thread : threads) {
289290
thread.join();
290291
}
292+
293+
// If this is the top level environment, then print some execution statistics
294+
if (this->containing_environment_ == nullptr) {
295+
Statistics::print();
296+
}
291297
});
292298
}
293299

lib/port.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "reactor-cpp/assert.hh"
1212
#include "reactor-cpp/environment.hh"
1313
#include "reactor-cpp/reaction.hh"
14+
#include "reactor-cpp/statistics.hh"
1415

1516
namespace reactor {
1617

@@ -43,6 +44,8 @@ void BasePort::base_bind_to(BasePort* port) {
4344
port->inward_binding_ = this;
4445
[[maybe_unused]] bool result = this->outward_bindings_.insert(port).second;
4546
reactor_assert(result);
47+
48+
Statistics::increment_connections();
4649
}
4750

4851
void BasePort::register_dependency(Reaction* reaction, bool is_trigger) noexcept {

lib/reactor.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "reactor-cpp/logging.hh"
1515
#include "reactor-cpp/port.hh"
1616
#include "reactor-cpp/reaction.hh"
17+
#include "reactor-cpp/statistics.hh"
1718

1819
namespace reactor {
1920

@@ -66,6 +67,17 @@ ReactorElement::ReactorElement(const std::string& name, ReactorElement::Type typ
6667
validate(type == Type::Reactor || type == Type::Action, "Only reactors and actions can be owned by the environment!");
6768
validate(this->environment_->phase() == Environment::Phase::Construction,
6869
"Reactor elements can only be created during construction phase!");
70+
71+
switch (type) {
72+
case Type::Action:
73+
Statistics::increment_actions();
74+
break;
75+
case Type::Reactor:
76+
Statistics::increment_reactor_instances();
77+
break;
78+
default:
79+
break;
80+
}
6981
}
7082

7183
Reactor::Reactor(const std::string& name, Reactor* container)
@@ -82,6 +94,7 @@ void Reactor::register_action([[maybe_unused]] BaseAction* action) {
8294
"Actions can only be registered during construction phase!");
8395
[[maybe_unused]] bool result = actions_.insert(action).second;
8496
reactor_assert(result);
97+
Statistics::increment_actions();
8598
}
8699

87100
void Reactor::register_input(BasePort* port) {
@@ -90,6 +103,7 @@ void Reactor::register_input(BasePort* port) {
90103
"Ports can only be registered during construction phase!");
91104
[[maybe_unused]] bool result = inputs_.insert(port).second;
92105
reactor_assert(result);
106+
Statistics::increment_ports();
93107
}
94108

95109
void Reactor::register_output(BasePort* port) {
@@ -98,6 +112,7 @@ void Reactor::register_output(BasePort* port) {
98112
"Ports can only be registered during construction phase!");
99113
[[maybe_unused]] bool result = inputs_.insert(port).second;
100114
reactor_assert(result);
115+
Statistics::increment_ports();
101116
}
102117

103118
void Reactor::register_reaction([[maybe_unused]] Reaction* reaction) {
@@ -107,6 +122,7 @@ void Reactor::register_reaction([[maybe_unused]] Reaction* reaction) {
107122
"Reactions can only be registered during construction phase!");
108123
[[maybe_unused]] bool result = reactions_.insert(reaction).second;
109124
reactor_assert(result);
125+
Statistics::increment_reactions();
110126
}
111127

112128
void Reactor::register_reactor([[maybe_unused]] Reactor* reactor) {
@@ -115,6 +131,7 @@ void Reactor::register_reactor([[maybe_unused]] Reactor* reactor) {
115131
"Reactions can only be registered during construction phase!");
116132
[[maybe_unused]] bool result = reactors_.insert(reactor).second;
117133
reactor_assert(result);
134+
Statistics::increment_reactor_instances();
118135
}
119136

120137
void Reactor::startup() {

lib/scheduler.cc

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "reactor-cpp/logging.hh"
2020
#include "reactor-cpp/port.hh"
2121
#include "reactor-cpp/reaction.hh"
22+
#include "reactor-cpp/statistics.hh"
2223
#include "reactor-cpp/time_barrier.hh"
2324
#include "reactor-cpp/trace.hh"
2425

@@ -82,6 +83,8 @@ void Worker::execute_reaction(Reaction* reaction) const {
8283
tracepoint(reactor_cpp, reaction_execution_starts, identity_, reaction->fqn(), scheduler_.logical_time());
8384
reaction->trigger();
8485
tracepoint(reactor_cpp, reaction_execution_finishes, identity_, reaction->fqn(), scheduler_.logical_time());
86+
87+
Statistics::increment_processed_reactions();
8588
}
8689

8790
void Scheduler::schedule() noexcept {
@@ -297,6 +300,12 @@ void Scheduler::start() {
297300
}
298301
}
299302

303+
void Scheduler::advance_logical_time_to(const Tag& tag) {
304+
log_.debug() << "advance logical time to tag " << tag;
305+
logical_time_.advance_to(tag);
306+
Statistics::increment_processed_events();
307+
}
308+
300309
void Scheduler::next() { // NOLINT
301310
// Notify other environments and let them know that we finished processing the
302311
// current tag
@@ -348,8 +357,7 @@ void Scheduler::next() { // NOLINT
348357
log_.debug() << "Schedule the last round of reactions including all "
349358
"termination reactions";
350359
triggered_actions_ = event_queue_.extract_next_event();
351-
log_.debug() << "advance logical time to tag " << t_next;
352-
logical_time_.advance_to(t_next);
360+
advance_logical_time_to(t_next);
353361
} else {
354362
return;
355363
}
@@ -390,9 +398,7 @@ void Scheduler::next() { // NOLINT
390398
// queue.
391399
triggered_actions_ = event_queue_.extract_next_event();
392400

393-
// advance logical time
394-
log_.debug() << "advance logical time to tag " << t_next;
395-
logical_time_.advance_to(t_next);
401+
advance_logical_time_to(t_next);
396402

397403
// If there are no triggered actions at the event, then release the
398404
// current tag and go back to the start of the loop
@@ -407,6 +413,7 @@ void Scheduler::next() { // NOLINT
407413
log_.debug() << "events: " << triggered_actions_->size();
408414
for (auto* action : *triggered_actions_) {
409415
log_.debug() << "Action " << action->fqn();
416+
Statistics::increment_triggered_actions();
410417
action->setup();
411418
for (auto* reaction : action->triggers()) {
412419
// There is no need to acquire the mutex. At this point the scheduler

0 commit comments

Comments
 (0)