preCICE v3.3.0
Loading...
Searching...
No Matches
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 <sstream>
13#include <string>
14#include <string_view>
15#include <sys/types.h>
16#include <tuple>
17#include <utility>
18#include <variant>
19
20#include "logging/LogMacros.hpp"
21#include "profiling/Event.hpp"
23#include "utils/assertion.hpp"
24#include "utils/fmt.hpp"
25
26#ifdef PRECICE_COMPRESSION
27#include <lzma.h>
28#endif
29
30namespace precice::profiling {
31
33constexpr int file_version{2};
34
37
39std::string timepoint_to_string(sys_clk::time_point c)
40{
41 using namespace std::chrono;
43 auto ms = duration_cast<microseconds>(c.time_since_epoch()) % 1000;
44
46 ss << std::put_time(std::localtime(&ts), "%FT%T") << "." << std::setw(3) << std::setfill('0') << ms.count();
47 return ss.str();
48}
49
50// -----------------------------------------------------------------------
51
56
62
63void EventRegistry::initialize(std::string_view applicationName, int rank, int size)
64{
65 auto initClock = Event::Clock::now();
66 auto initTime = std::chrono::system_clock::now();
67
68 this->_applicationName = std::move(applicationName);
69 this->_rank = rank;
70 this->_size = size;
71 this->_initTime = initTime;
72 this->_initClock = initClock;
73
74 _writeQueue.clear();
75 _nameDict.clear();
76
77 _globalId = nameToID("_GLOBAL");
78 _writeQueue.emplace_back(StartEntry{_globalId.value(), _initClock});
79
80 _initialized = true;
81 _finalized = false;
84 }
85}
86
91
93{
94 _directory = directory;
95}
96
98{
99 _mode = mode;
100}
101
102namespace {
103std::string toString(Mode m)
104{
105 switch (m) {
106 case (Mode::Off):
107 return "off";
108 case (Mode::Fundamental):
109 return "fundamental";
110 case (Mode::API):
111 return "api";
112 case (Mode::All):
113 return "all";
114 }
115 PRECICE_UNREACHABLE("Unknown mode");
116}
117} // namespace
118
120{
121 if (_mode == Mode::Off) {
122 PRECICE_DEBUG("Profiling is turned off. Backend will not start.");
123 return;
124 }
125
127
128 // Create the directory if necessary
129 bool isLocal = _directory.empty() || _directory == ".";
130 if (!isLocal) {
131 auto exists = std::filesystem::exists(_directory);
134 "The destination folder \"{}\" exists but isn't a directory. Please remove the directory \"precice-run\" and try again.",
135 _directory);
136 if (!exists) {
138 }
139 }
140 auto filename = fmt::format("{}/{}-{}-{}.txt", _directory, _applicationName, _rank, _size);
141 PRECICE_DEBUG("Starting backend with events-file: \"{}\"", filename);
142 _output.open(filename);
143 PRECICE_CHECK(_output, "Unable to open the events-file: \"{}\"", filename);
144
145 using std::literals::operator""sv;
146#ifdef PRECICE_COMPRESSION
147 _strm = LZMA_STREAM_INIT;
148 lzma_options_lzma options;
149 lzma_lzma_preset(&options, 0);
150 options.mode = LZMA_MODE_FAST;
151 if (lzma_alone_encoder(&_strm, &options) != LZMA_OK) {
152 throw std::runtime_error("Failed to init LZMA encoder");
153 }
154 auto compression = "true"sv;
155#else
156 auto compression = "false"sv;
157#endif
158
159 // write header
160 fmt::println(_output,
161 R"({{"name":"{}","rank":{},"size":{},"unix_us":"{}","tinit":"{}","mode":"{}","compression":{},"file_version":{}}})",
163 _rank,
164 _size,
167 toString(_mode),
168 compression,
170
171 _output.flush();
172 _isBackendRunning = true;
173}
174
176{
177 if (_mode == Mode::Off || !_isBackendRunning) {
178 return;
179 }
180 // create end of global event
181 auto now = Event::Clock::now();
182 put(StopEntry{*_globalId, now});
183 // flush the queue
184 flush();
185
186#ifdef PRECICE_COMPRESSION
187 lzma_ret ret;
188 do {
189 _strm.next_out = reinterpret_cast<uint8_t *>(_buf.data());
190 _strm.avail_out = _buf.size();
191
192 ret = lzma_code(&_strm, LZMA_FINISH);
193 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
194 throw std::runtime_error("Finalization failed");
195 }
196 _output.write(_buf.data(), _buf.size() - _strm.avail_out);
197 } while (ret != LZMA_STREAM_END);
198
199 lzma_end(&_strm);
200#endif
201
202 _output.close();
203 _nameDict.clear();
204
205 _isBackendRunning = false;
206}
207
209{
210 if (_finalized || !_initialized) {
211 return;
212 }
213
214 stopBackend();
215
216 _initialized = false;
217 _finalized = true;
218}
219
221{
222 _writeQueue.clear();
223}
224
226{
227 PRECICE_ASSERT(_mode != Mode::Off, "The profiling is off.");
228
229 // avoid flushing the queue when we start measuring but only if we don't explicitly want to write every entry
230 auto skipFlush = _writeQueueMax != 1 && std::holds_alternative<StartEntry>(pe);
231
232 _writeQueue.emplace_back(std::move(pe));
233 if (!skipFlush && _writeQueueMax > 0 && _writeQueue.size() > _writeQueueMax) {
234 flush();
235 }
236}
237
239{
240 PRECICE_ASSERT(_mode != Mode::Off, "The profiling is off.");
241 _writeQueue.emplace_back(std::move(pe));
242}
243
244namespace {
245template <class OIter>
246struct EventWriter {
247 OIter out;
248 Event::Clock::time_point initClock;
249
250 EventWriter(OIter iter, Event::Clock::time_point tp) : out(iter), initClock(tp) {}
251
252 auto sinceInit(Event::Clock::time_point tp)
253 {
254 return std::chrono::duration_cast<std::chrono::microseconds>(tp - initClock).count();
255 }
256
257 void operator()(const StartEntry &se)
258 {
259 fmt::format_to(out,
260 "B{}:{}\n",
261 se.eid, sinceInit(se.clock));
262 }
263
264 void operator()(const StopEntry &se)
265 {
266 fmt::format_to(out,
267 "E{}:{}\n",
268 se.eid, sinceInit(se.clock));
269 }
270
271 void operator()(const DataEntry &de)
272 {
273 fmt::format_to(out,
274 "D{}:{}:{}:{}\n",
275 de.eid, sinceInit(de.clock), de.did, de.dvalue);
276 }
277
278 void operator()(const NameEntry &ne)
279 {
280 fmt::format_to(out,
281 "N{}:{}\n",
282 ne.id, ne.name);
283 }
284};
285} // namespace
286
288try {
289 if (_mode == Mode::Off || _writeQueue.empty()) {
290 return;
291 }
292 PRECICE_ASSERT(_output, "Filestream doesn't exist.");
293
294 _inbuf.clear();
295 EventWriter ew{std::back_inserter(_inbuf), _initClock};
296 std::for_each(_writeQueue.begin(), _writeQueue.end(), [&ew](const auto &pe) { std::visit(ew, pe); });
297
298#ifdef PRECICE_COMPRESSION
299 _strm.next_in = reinterpret_cast<uint8_t *>(_inbuf.data());
300 _strm.avail_in = _inbuf.size();
301
302 while (_strm.avail_in > 0) {
303 _strm.next_out = reinterpret_cast<uint8_t *>(_buf.data());
304 _strm.avail_out = _buf.size();
305 lzma_ret ret = lzma_code(&_strm, LZMA_RUN);
306 PRECICE_ASSERT(ret == LZMA_OK, "Compression failed");
307 _output.write(_buf.data(), _buf.size() - _strm.avail_out);
308 }
309#else
310 _output.write(_inbuf.data(), _inbuf.size());
311#endif
312
313 _output.flush();
314 _writeQueue.clear();
315} catch (const std::bad_variant_access &e) {
316 PRECICE_UNREACHABLE(e.what());
317}
318
320{
321 if (auto iter = _nameDict.find(name);
322 iter == _nameDict.end()) {
323 int id = _nameDict.size();
324 _nameDict.insert(iter, {std::string(name), id});
325 _writeQueue.emplace_back(NameEntry{std::string(name), id});
326 return id;
327 } else {
328 return iter->second;
329 }
330}
331
332} // namespace precice::profiling
Eigen::Vector2d ts
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
#define PRECICE_UNREACHABLE(...)
Definition assertion.hpp:93
T back_inserter(T... args)
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.
std::vector< char > _inbuf
Buffer for the text to be written to file.
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
void put(PendingEntry pe)
Records an event.
std::map< std::string, int, std::less<> > _nameDict
int _rank
The rank/number of parallel instance of the current program.
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.
EventRegistry(EventRegistry const &)=delete
Deleted copy and move SMFs for singleton pattern.
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 create_directories(T... args)
T duration_cast(T... args)
T exists(T... args)
T for_each(T... args)
T holds_alternative(T... args)
T is_directory(T... args)
T localtime(T... args)
contains profiling utilities.
std::chrono::system_clock sys_clk
std::chrono::steady_clock stdy_clk
Mode
The Mode of the Event utility.
constexpr int file_version
The version of the Events file. Increase on changes.
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 put_time(T... args)
T setfill(T... args)
T setw(T... args)
T size(T... args)
T str(T... args)