Skip to content

Commit c8c9590

Browse files
authored
Merge pull request #47 from lf-lang/statistics
Simple mechanism for collecting statistics during execution
2 parents 9a7b0a9 + 6dc85b4 commit c8c9590

File tree

9 files changed

+145
-6
lines changed

9 files changed

+145
-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: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
44+
inline static std::atomic_size_t set_ports_{0};
45+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
46+
inline static std::atomic_size_t scheduled_actions_{0};
47+
48+
inline static void increment(std::atomic_size_t& counter) {
49+
if constexpr (enabled_) {
50+
counter.fetch_add(1, std::memory_order_release);
51+
}
52+
}
53+
54+
public:
55+
inline static void increment_reactor_instances() { increment(reactor_instances_); }
56+
inline static void increment_connections() { increment(connections_); }
57+
inline static void increment_reactions() { increment(reactions_); }
58+
inline static void increment_actions() { increment(actions_); }
59+
inline static void increment_ports() { increment(ports_); }
60+
inline static void increment_processed_events() { increment(processed_events_); }
61+
inline static void increment_processed_reactions() { increment(processed_reactions_); }
62+
inline static void increment_triggered_actions() { increment(triggered_actions_); }
63+
inline static void increment_set_ports() { increment(set_ports_); }
64+
inline static void increment_scheduled_actions() { increment(scheduled_actions_); }
65+
66+
inline static auto reactor_instances() { return reactor_instances_.load(std::memory_order_acquire); }
67+
inline static auto connections() { return connections_.load(std::memory_order_acquire); }
68+
inline static auto reactions() { return reactions_.load(std::memory_order_acquire); }
69+
inline static auto actions() { return actions_.load(std::memory_order_acquire); }
70+
inline static auto ports() { return ports_.load(std::memory_order_acquire); }
71+
inline static auto processed_events() { return processed_events_.load(std::memory_order_acquire); }
72+
inline static auto processed_reactions() { return processed_reactions_.load(std::memory_order_acquire); }
73+
inline static auto triggered_actions() { return triggered_actions_.load(std::memory_order_acquire); }
74+
inline static auto set_ports() { return set_ports_.load(std::memory_order_acquire); }
75+
inline static auto scheduled_actions() { return scheduled_actions_.load(std::memory_order_acquire); }
76+
77+
inline static void print() {
78+
if constexpr (enabled_) {
79+
reactor::log::Info() << "-----------------------------------------------------------";
80+
reactor::log::Info() << "Program statistics:";
81+
reactor::log::Info() << " - number of reactors: " << reactor_instances();
82+
reactor::log::Info() << " - number of connections: " << connections();
83+
reactor::log::Info() << " - number of reactions " << reactions();
84+
reactor::log::Info() << " - number of actions: " << actions();
85+
reactor::log::Info() << " - number of ports: " << ports();
86+
reactor::log::Info() << "Execution statistics:";
87+
reactor::log::Info() << " - processed events: " << processed_events();
88+
reactor::log::Info() << " - triggered actions: " << triggered_actions();
89+
reactor::log::Info() << " - processed reactions: " << processed_reactions();
90+
reactor::log::Info() << " - set ports set: " << set_ports();
91+
reactor::log::Info() << " - scheduled actions: " << scheduled_actions();
92+
reactor::log::Info() << "-----------------------------------------------------------";
93+
}
94+
}
95+
};
96+
97+
} // namespace reactor
98+
99+
#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: 14 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
@@ -431,6 +438,7 @@ void Scheduler::schedule_sync(BaseAction* action, const Tag& tag) {
431438
<< " with tag " << tag;
432439
reactor_assert(logical_time_ < tag);
433440
tracepoint(reactor_cpp, schedule_action, action->container()->fqn(), action->name(), tag);
441+
Statistics::increment_scheduled_actions();
434442

435443
const auto& action_list = event_queue_.insert_event_at(tag);
436444
action_list->push_back(action);
@@ -477,6 +485,7 @@ auto Scheduler::schedule_empty_async_at(const Tag& tag) -> bool {
477485

478486
void Scheduler::set_port(BasePort* port) {
479487
log_.debug() << "Set port " << port->fqn();
488+
Statistics::increment_set_ports();
480489

481490
// We do not check here if port is already in the list. This means clean()
482491
// could be called multiple times for a single port. However, calling

0 commit comments

Comments
 (0)