preCICE v3.2.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
EventUtils.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <array>
3#include <cassert>
4#include <ctime>
5#include <filesystem>
6#include <fstream>
7#include <iomanip>
8#include <iterator>
9#include <memory>
10#include <optional>
11#include <ratio>
12#include <string>
13#include <string_view>
14#include <sys/types.h>
15#include <tuple>
16#include <utility>
17#include <variant>
18
19#include "logging/LogMacros.hpp"
20#include "profiling/Event.hpp"
22#include "utils/assertion.hpp"
23#include "utils/fmt.hpp"
24
25namespace precice::profiling {
26
29
31std::string timepoint_to_string(sys_clk::time_point c)
32{
33 using namespace std::chrono;
35 auto ms = duration_cast<microseconds>(c.time_since_epoch()) % 1000;
36
38 ss << std::put_time(std::localtime(&ts), "%FT%T") << "." << std::setw(3) << std::setfill('0') << ms.count();
39 return ss.str();
40}
41
42// -----------------------------------------------------------------------
43
48
54
55void EventRegistry::initialize(std::string_view applicationName, int rank, int size)
56{
58 auto initTime = std::chrono::system_clock::now();
59
60 this->_applicationName = std::move(applicationName);
61 this->_rank = rank;
62 this->_size = size;
63 this->_initTime = initTime;
64 this->_initClock = initClock;
65
66 _firstwrite = true;
67 _writeQueue.clear();
69
70 _globalId = nameToID("_GLOBAL");
72
73 _initialized = true;
74 _finalized = false;
77 }
78}
79
84
86{
87 _directory = directory;
88}
89
91{
92 _mode = mode;
93}
94
95namespace {
96std::string toString(Mode m)
97{
98 switch (m) {
99 case (Mode::Off):
100 return "off";
101 case (Mode::Fundamental):
102 return "fundamental";
103 case (Mode::All):
104 return "all";
105 }
106 PRECICE_UNREACHABLE("Unknown mode");
107}
108} // namespace
109
111{
112 if (_mode == Mode::Off) {
113 PRECICE_DEBUG("Profiling is turned off. Backend will not start.");
114 return;
115 }
116
118
119 // Create the directory if necessary
120 bool isLocal = _directory.empty() || _directory == ".";
121 if (!isLocal) {
122 auto exists = std::filesystem::exists(_directory);
125 "The destination folder \"{}\" exists but isn't a directory. Please remove the directory \"precice-run\" and try again.",
126 _directory);
127 if (!exists) {
129 }
130 }
131 auto filename = fmt::format("{}/{}-{}-{}.json", _directory, _applicationName, _rank, _size);
132 PRECICE_DEBUG("Starting backend with events-file: \"{}\"", filename);
133 _output.open(filename);
134 PRECICE_CHECK(_output, "Unable to open the events-file: \"{}\"", filename);
135
136 // write header
137 fmt::print(_output,
138 R"({{
139 "meta":{{
140 "name": "{}",
141 "rank": "{}",
142 "size": "{}",
143 "unix_us": "{}",
144 "tinit": "{}",
145 "mode": "{}"
146 }},
147 "events":[
148 )",
150 _rank,
151 _size,
152 std::chrono::duration_cast<std::chrono::microseconds>(_initTime.time_since_epoch()).count(),
154 toString(_mode));
155 _output.flush();
156 _isBackendRunning = true;
157}
158
160{
161 if (_mode == Mode::Off || !_isBackendRunning) {
162 return;
163 }
164 // create end of global event
165 auto now = Event::Clock::now();
167 // flush the queue
168 flush();
169 _output << "]}";
170 _output.close();
172
173 _isBackendRunning = false;
174}
175
177{
179 return;
180 }
181
182 stopBackend();
184 _initialized = false;
185 _finalized = true;
186}
187
189{
190 _writeQueue.clear();
191}
192
194{
195 PRECICE_ASSERT(_mode != Mode::Off, "The profiling is off.");
197 // avoid flushing the queue when we start measuring but only if we don't explicitly want to write every entry
198 auto skipFlush = _writeQueueMax != 1 && std::holds_alternative<StartEntry>(pe);
199
200 _writeQueue.emplace_back(std::move(pe));
201 if (!skipFlush && _writeQueueMax > 0 && _writeQueue.size() > _writeQueueMax) {
202 flush();
203 }
207{
208 PRECICE_ASSERT(_mode != Mode::Off, "The profiling is off.");
209 _writeQueue.emplace_back(std::move(pe));
210}
211
212namespace {
213struct EventWriter {
215 Event::Clock::time_point initClock;
217
218 auto sinceInit(Event::Clock::time_point tp)
219 {
220 return std::chrono::duration_cast<std::chrono::microseconds>(tp - initClock).count();
221 }
222
223 void operator()(const StartEntry &se)
224 {
225 fmt::print(out,
226 R"({}{{"et":"{}","eid":{},"ts":{}}})",
227 prefix, se.type, se.eid, sinceInit(se.clock));
228 }
229
230 void operator()(const StopEntry &se)
231 {
232 fmt::print(out,
233 R"({}{{"et":"{}","eid":{},"ts":{}}})",
234 prefix, se.type, se.eid, sinceInit(se.clock));
235 }
236
237 void operator()(const DataEntry &de)
238 {
239 fmt::print(out,
240 R"({}{{"et":"{}","eid":{},"ts":{},"dn":{},"dv":"{}"}})",
241 prefix, de.type, de.eid, sinceInit(de.clock), de.did, de.dvalue);
242 }
244 void operator()(const NameEntry &ne)
245 {
246 fmt::print(out,
247 R"({}{{"et":"n","en":"{}","eid":{}}})",
248 prefix, ne.name, ne.id);
249 }
250};
251} // namespace
252
254try {
255 if (_mode == Mode::Off || _writeQueue.empty()) {
256 return;
257 }
258 PRECICE_ASSERT(_output, "Filestream doesn't exist.");
259
260 auto first = _writeQueue.begin();
261 // Don't prefix the first write with a comma
262 if (_firstwrite) {
263 PRECICE_ASSERT(!_writeQueue.empty() && !_writeQueue.front().valueless_by_exception());
264 std::visit(EventWriter{_output, _initClock, ""}, _writeQueue.front());
265 ++first;
266 _firstwrite = false;
267 }
269 EventWriter ew{_output, _initClock, ","};
270 std::for_each(first, _writeQueue.end(), [&ew](const auto &pe) { std::visit(ew, pe); });
271
272 _output.flush();
273 _writeQueue.clear();
274} catch (const std::bad_variant_access &e) {
275 PRECICE_UNREACHABLE(e.what());
276}
277
279{
280 if (auto iter = _nameDict.find(name);
281 iter == _nameDict.end()) {
282 int id = _nameDict.size();
283 _nameDict.insert(iter, {std::string(name), id});
284 _writeQueue.emplace_back(NameEntry{std::string(name), id});
285 return id;
286 } else {
287 return iter->second;
288 }
289}
290
291} // namespace precice::profiling
Eigen::Vector2d ts
Event::Clock::time_point initClock
std::string prefix
std::ostream & out
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
std::string name
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
#define PRECICE_UNREACHABLE(...)
Definition assertion.hpp:93
static EventRegistry & instance()
Returns the only instance (singleton) of the EventRegistry class.
void startBackend()
Create the file and starts the filestream if profiling is turned on.
void initialize(std::string_view applicationName, int rank=0, int size=1)
Sets the global start time.
void clear()
Clears the registry.
void setWriteQueueMax(std::size_t size)
Sets the maximum size of the writequeue before calling flush(). Use 0 to flush on destruction.
void setDirectory(std::string_view directory)
Sets the directory where to write the event files to.
int nameToID(std::string_view name)
int _size
The amount of parallel instances of the current program.
void finalize()
Sets the global end time and flushes buffers.
void setMode(Mode mode)
Sets the operational mode of the registry.
void stopBackend()
Stops the global event, flushes the buffers and closes the filestream.
Mode _mode
The operational mode of the registry.
std::vector< PendingEntry > _writeQueue
bool _firstwrite
Indicator for the first record to be written.
void put(PendingEntry pe)
Records an event.
std::map< std::string, int, std::less<> > _nameDict
Event::Clock::time_point _initClock
The initial time clock, used to take runtime measurements.
std::optional< int > _globalId
The id of the global event.
std::string _applicationName
The name of the current participant.
void putCritical(PendingEntry pe)
Records an event without flushing events.
void flush()
Writes all recorded events to file and flushes the buffer.
std::chrono::system_clock::time_point _initTime
The initial time, used to describe when the run started.
T clear(T... args)
T close(T... args)
T create_directories(T... args)
T empty(T... args)
T end(T... args)
T exists(T... args)
T find(T... args)
T flush(T... args)
T for_each(T... args)
T insert(T... args)
T is_directory(T... args)
T localtime(T... args)
T make_unique(T... args)
contains profiling utilities.
Mode
The Mode of the Event utility.
std::variant< StartEntry, StopEntry, DataEntry, NameEntry > PendingEntry
std::string timepoint_to_string(sys_clk::time_point c)
Converts the time_point into a string like "2019-01-10T18:30:46.834".
T open(T... args)
T put_time(T... args)
T setfill(T... args)
T setw(T... args)
T size(T... args)
T str(T... args)
T value(T... args)
T visit(T... args)