41 #include <string_view>
44 #include <boost/stacktrace/stacktrace.hpp>
46 #ifndef ACTS_EXAMPLES_NO_TBB
50 #include <boost/algorithm/string.hpp>
51 #include <boost/algorithm/string/predicate.hpp>
52 #include <boost/core/demangle.hpp>
53 #include <dfe/dfe_io_dsv.hpp>
54 #include <dfe/dfe_namedtuple.hpp>
56 namespace ActsExamples {
60 std::string_view getAlgorithmType(
const SequenceElement&
element) {
61 if (dynamic_cast<const IWriter*>(&element) !=
nullptr) {
64 if (dynamic_cast<const IReader*>(&element) !=
nullptr) {
73 size_t saturatedAdd(
size_t a,
size_t b) {
75 res |= -
static_cast<int>(res <
a);
81 name = boost::core::demangle(name.c_str());
84 const static std::regex vector_pattern(
85 R
"??(std::vector<(.*), std::allocator<(\1\s*)>\s*>)??");
86 name = std::regex_replace(name, vector_pattern, "std::vector<$1>");
89 const static std::regex variant_pattern(
90 R
"??(std::variant<(Acts::Measurement<Acts::BoundIndices, [0-9]ul>(,|)\s+)+>)??");
91 name = std::regex_replace(name, variant_pattern,
92 "Acts::BoundVariantMeasurement");
95 boost::algorithm::replace_all(name,
"std::",
"");
96 boost::algorithm::replace_all(name,
"boost::container::",
"");
97 boost::algorithm::replace_all(name,
"Acts::",
"");
98 boost::algorithm::replace_all(name,
"ActsExamples::",
"");
99 boost::algorithm::replace_all(name,
"ActsFatras::",
"");
108 m_taskArena((
m_cfg.numThreads < 0) ? tbb::task_arena::automatic
111 #ifndef ACTS_EXAMPLES_NO_TBB
114 ACTS_INFO(
"Create Sequencer (single-threaded)");
115 #ifndef ACTS_EXAMPLES_NO_TBB
117 ROOT::EnableThreadSafety();
122 const char* envvar = std::getenv(
"ACTS_SEQUENCER_DISABLE_FPEMON");
123 if (envvar !=
nullptr) {
125 "Overriding FPE tracking Sequencer based on environment variable "
126 "ACTS_SEQUENCER_DISABLE_FPEMON");
132 std::shared_ptr<IContextDecorator> decorator) {
134 throw std::invalid_argument(
"Can not add empty/NULL context decorator");
142 throw std::invalid_argument(
"Can not add empty/NULL reader");
150 throw std::invalid_argument(
"Can not add empty/NULL algorithm");
158 throw std::invalid_argument(
"Can not add empty/NULL writer");
165 throw std::invalid_argument(
"Can not add empty/NULL element");
170 std::string elementType{getAlgorithmType(*element)};
172 elementTypeCapitalized[0] = std::toupper(elementTypeCapitalized[0]);
173 ACTS_INFO(
"Add " << elementType <<
" '" << element->name() <<
"'");
179 auto symbol = [&](
const char*
in) {
182 while (pos + 80 < s.size()) {
183 ACTS_INFO(
" " + s.substr(pos, pos + 80));
191 for (
const auto* handle : element->readHandles()) {
192 if (!handle->isInitialized()) {
196 ACTS_INFO(
"<- " << handle->name() <<
" '" << handle->key() <<
"':");
197 symbol(handle->typeInfo().name());
202 if (!
source.isCompatible(*handle)) {
204 << elementType <<
" " << element->name() <<
":"
205 <<
"\n-> white board will contain key '" << handle->key()
207 <<
"\nat this point in the sequence (source: "
208 <<
source.fullName() <<
"),"
209 <<
"\nbut the type will be\n"
210 <<
"'" << demangleAndShorten(
source.typeInfo().name()) <<
"'"
212 <<
"'" << demangleAndShorten(handle->typeInfo().name())
217 ACTS_ERROR(
"Adding " << elementType <<
" " << element->name() <<
":"
218 <<
"\n-> white board will not contain key"
219 <<
" '" << handle->key()
220 <<
"' at this point in the sequence."
221 <<
"\n Needed for read data handle '"
222 << handle->name() <<
"'")
228 for (
const auto* handle : element->writeHandles()) {
229 if (!handle->isInitialized()) {
233 ACTS_INFO(
"-> " << handle->name() <<
" '" << handle->key() <<
"':");
234 symbol(handle->typeInfo().name());
239 ACTS_ERROR(
"White board will already contain key '"
240 << handle->key() <<
"'. Source: '" <<
source.fullName()
241 <<
"' (cannot overwrite)");
250 ACTS_DEBUG(
"Key '" << handle->key() <<
"' aliased to '" <<
it->second
267 throw std::invalid_argument(
"Alias to '" + aliasName +
"' -> '" +
268 objectName +
"' already set");
278 std::vector<std::string>
names;
282 names.push_back(
"Decorator:" + decorator->name());
293 constexpr
auto kInvalidEventsRange = std::make_pair(SIZE_MAX, SIZE_MAX);
306 size_t end = SIZE_MAX;
308 auto available =
reader->availableEvents();
309 beg = std::max(beg, available.first);
310 end =
std::min(end, available.second);
316 ACTS_ERROR(
"Available events ranges from readers do not overlap");
317 return kInvalidEventsRange;
323 return kInvalidEventsRange;
326 if (end <= saturatedAdd(beg,
m_cfg.
skip)) {
327 ACTS_ERROR(
"Less events available than requested to skip");
328 return kInvalidEventsRange;
331 if ((beg == 0
u) and (end == SIZE_MAX) and (!
m_cfg.
events.has_value())) {
332 ACTS_ERROR(
"Could not determine number of events");
333 return kInvalidEventsRange;
337 auto begSelected = saturatedAdd(beg,
m_cfg.
skip);
338 auto endSelected =
end;
340 auto endRequested = saturatedAdd(begSelected,
m_cfg.
events.value());
341 endSelected =
std::min(end, endRequested);
342 if (end < endRequested) {
343 ACTS_INFO(
"Restrict requested number of events to available ones");
347 return {begSelected, endSelected};
352 using Clock = std::chrono::high_resolution_clock;
354 using Timepoint = Clock::time_point;
355 using Seconds = std::chrono::duration<double>;
356 using NanoSeconds = std::chrono::duration<double, std::nano>;
363 StopWatch(Duration&
s) :
start(Clock::now()),
store(s) {}
364 ~StopWatch() {
store += Clock::now() -
start; }
368 template <
typename D>
371 if (1e9 < std::abs(ns)) {
373 }
else if (1e6 < std::abs(ns)) {
375 }
else if (1e3 < std::abs(ns)) {
383 template <
typename D>
384 inline std::string perEvent(
D duration,
size_t numEvents) {
385 return asString(duration / numEvents) +
"/event";
397 void storeTiming(
const std::vector<std::string>& identifiers,
398 const std::vector<Duration>& durations, std::size_t numEvents,
400 dfe::NamedTupleTsvWriter<TimingInfo>
writer(path, 4);
401 for (
size_t i = 0;
i < identifiers.size(); ++
i) {
403 info.identifier = identifiers[
i];
405 std::chrono::duration_cast<Seconds>(durations[
i]).
count();
406 info.time_perevent_s = info.time_total_s / numEvents;
414 Timepoint clockWallStart = Clock::now();
417 std::vector<Duration> clocksAlgorithms(names.size(),
Duration::zero());
423 if ((eventsRange.first == SIZE_MAX) and (eventsRange.second == SIZE_MAX)) {
427 ACTS_INFO(
"Processing events [" << eventsRange.first <<
", "
428 << eventsRange.second <<
")");
435 size_t nAlgorithms = 0;
437 if (dynamic_cast<const IWriter*>(alg.get()) !=
nullptr) {
439 }
else if (dynamic_cast<const IReader*>(alg.get()) !=
nullptr) {
441 }
else if (dynamic_cast<const IAlgorithm*>(alg.get()) !=
nullptr) {
444 throw std::runtime_error{
"Unknown sequence element type"};
448 ACTS_INFO(
" " << nReaders <<
" readers");
449 ACTS_INFO(
" " << nAlgorithms <<
" algorithms");
450 ACTS_INFO(
" " << nWriters <<
" writers");
453 for (
auto& [alg, fpe] : m_sequenceElements) {
454 ACTS_VERBOSE(
"Initialize " << getAlgorithmType(*alg) <<
": "
457 ACTS_FATAL(
"Failed to initialize " << getAlgorithmType(*alg) <<
": "
459 throw std::runtime_error(
"Failed to process event data");
464 std::atomic<size_t> nProcessedEvents = 0;
465 size_t nTotalEvents = eventsRange.second - eventsRange.first;
468 tbb::blocked_range<size_t>(eventsRange.first, eventsRange.second),
469 [&](
const tbb::blocked_range<size_t>&
r) {
470 std::vector<Duration> localClocksAlgorithms(names.size(),
473 for (
size_t event = r.begin();
event != r.end(); ++
event) {
488 StopWatch sw(localClocksAlgorithms[ialgo++]);
489 ACTS_VERBOSE(
"Execute context decorator: " << cdr->name());
491 throw std::runtime_error(
"Failed to decorate event context");
497 for (
auto& [alg, fpe] : m_sequenceElements) {
498 std::optional<Acts::FpeMonitor> mon;
503 StopWatch sw(localClocksAlgorithms[ialgo++]);
504 ACTS_VERBOSE(
"Execute " << getAlgorithmType(*alg) <<
": "
507 ACTS_FATAL(
"Failed to execute " << getAlgorithmType(*alg)
508 <<
": " << alg->name());
509 throw std::runtime_error(
"Failed to process event data");
513 auto& local = fpe.local();
516 mon->result().stackTraces()) {
518 if (nMasked <
count) {
519 std::stringstream ss;
520 ss <<
"FPE of type " <<
type
521 <<
" exceeded configured per-event threshold of "
522 << nMasked <<
" (mask: " << maskLoc
523 <<
") (seen: " <<
count <<
" FPEs)\n"
531 local.merge(mon->result());
534 }
else if (!local.contains(
type, *st)) {
540 local.merge(mon->result());
548 }
else if (nTotalEvents <= 100) {
550 }
else if (nProcessedEvents % 100 == 0) {
551 ACTS_INFO(nProcessedEvents <<
" / " << nTotalEvents
552 <<
" events processed");
559 for (
size_t i = 0;
i < clocksAlgorithms.size(); ++
i) {
560 clocksAlgorithms[
i] += localClocksAlgorithms[
i];
567 for (
auto& [alg, fpe] : m_sequenceElements) {
568 ACTS_VERBOSE(
"Finalize " << getAlgorithmType(*alg) <<
": " << alg->name());
570 ACTS_FATAL(
"Failed to finalize " << getAlgorithmType(*alg) <<
": "
572 throw std::runtime_error(
"Failed to process event data");
579 Duration totalWall = Clock::now() - clockWallStart;
580 Duration totalReal = std::accumulate(
581 clocksAlgorithms.begin(), clocksAlgorithms.end(),
Duration::zero());
582 size_t numEvents = eventsRange.second - eventsRange.first;
583 ACTS_INFO(
"Processed " << numEvents <<
" events in " << asString(totalWall)
585 ACTS_INFO(
"Average time per event: " << perEvent(totalReal, numEvents));
587 for (
size_t i = 0;
i <
names.size(); ++
i) {
589 << perEvent(clocksAlgorithms[
i], numEvents));
592 if (!
m_cfg.outputDir.empty()) {
593 storeTiming(
names, clocksAlgorithms, numEvents,
597 if (m_nUnmaskedFpe > 0) {
604 void Sequencer::fpeReport()
const {
605 if (!
m_cfg.trackFpes) {
609 for (
auto& [alg, fpe] : m_sequenceElements) {
610 auto merged = std::accumulate(
612 [](
const auto&
lhs,
const auto&
rhs) {
return lhs.merged(
rhs); });
617 ACTS_INFO(
"-----------------------------------");
618 ACTS_INFO(
"FPE summary for " << getAlgorithmType(*alg) <<
": "
620 ACTS_INFO(
"-----------------------------------");
622 std::vector<std::reference_wrapper<const Acts::FpeMonitor::Result::FpeInfo>>
625 merged.stackTraces().begin(),
merged.stackTraces().end(),
626 std::back_inserter(sorted),
627 [](
const auto&
f) ->
const auto& {
return f; });
628 std::sort(sorted.begin(), sorted.end(), [](
const auto&
a,
const auto&
b) {
629 return a.get().count >
b.get().count;
632 std::vector<std::reference_wrapper<const Acts::FpeMonitor::Result::FpeInfo>>
635 for (
const auto& el : sorted) {
636 const auto& [
count,
type, st] = el.get();
637 auto [maskLoc, nMasked] = fpeMaskCount(*st, type);
640 " per event by " + maskLoc +
"]"
644 *st,
m_cfg.fpeStackTraceLength));
648 if (m_nUnmaskedFpe > 0) {
649 ACTS_ERROR(
"Encountered " << m_nUnmaskedFpe <<
" unmasked FPEs");
651 ACTS_INFO(
"No unmasked FPEs encountered");
655 std::pair<std::string, std::size_t> Sequencer::fpeMaskCount(
657 for (
const auto& frame : st) {
659 auto it = loc.find_last_of(
':');
661 unsigned int locLine = std::stoi(loc.substr(
it + 1));
664 if (boost::algorithm::ends_with(locFile,
file) &&
665 (
start <= locLine && locLine <
end) && fType == type) {
678 for (
auto& [alg, fpe] : m_sequenceElements) {
679 merged.
merge(std::accumulate(
681 [](
const auto&
lhs,
const auto&
rhs) {
return lhs.merged(
rhs); }));
688 os <<
"FpeMask(" << m.
file <<
":";
693 os <<
"(" << m.
lines.first <<
", " << m.
lines.second <<
"]";
695 os <<
", " << m.
type <<
" <= " << m.
count <<
")";