diff --git a/HeterogeneousCore/CUDAServices/plugins/BuildFile.xml b/HeterogeneousCore/CUDAServices/plugins/BuildFile.xml
index 6ce4aaa9b5d6a..db15cc188f456 100644
--- a/HeterogeneousCore/CUDAServices/plugins/BuildFile.xml
+++ b/HeterogeneousCore/CUDAServices/plugins/BuildFile.xml
@@ -12,6 +12,7 @@
+
diff --git a/HeterogeneousCore/CUDAServices/plugins/NVProfilerService.cc b/HeterogeneousCore/CUDAServices/plugins/NVProfilerService.cc
index 68b67e0964046..08f96ef80366f 100644
--- a/HeterogeneousCore/CUDAServices/plugins/NVProfilerService.cc
+++ b/HeterogeneousCore/CUDAServices/plugins/NVProfilerService.cc
@@ -1,1235 +1,226 @@
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-
#include
#include
-#include "DataFormats/Common/interface/HLTPathStatus.h"
-#include "DataFormats/Provenance/interface/EventID.h"
-#include "DataFormats/Provenance/interface/LuminosityBlockID.h"
-#include "DataFormats/Provenance/interface/ModuleDescription.h"
-#include "DataFormats/Provenance/interface/RunID.h"
-#include "DataFormats/Provenance/interface/Timestamp.h"
-#include "FWCore/MessageLogger/interface/MessageLogger.h"
-#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
-#include "FWCore/ParameterSet/interface/ParameterSet.h"
-#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
-#include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
-#include "FWCore/ServiceRegistry/interface/GlobalContext.h"
-#include "FWCore/ServiceRegistry/interface/ModuleCallingContext.h"
-#include "FWCore/ServiceRegistry/interface/PathContext.h"
-#include "FWCore/ServiceRegistry/interface/PathsAndConsumesOfModulesBase.h"
-#include "FWCore/ServiceRegistry/interface/ProcessContext.h"
-#include "FWCore/ServiceRegistry/interface/Service.h"
-#include "FWCore/ServiceRegistry/interface/StreamContext.h"
-#include "FWCore/ServiceRegistry/interface/SystemBounds.h"
-#include "FWCore/Utilities/interface/BranchType.h"
-#include "FWCore/Utilities/interface/Exception.h"
-#include "FWCore/Utilities/interface/ProductKindOfType.h"
-#include "FWCore/Utilities/interface/TimeOfDay.h"
#include "HeterogeneousCore/CUDAServices/interface/CUDAInterface.h"
-using namespace std::string_literals;
+#include "PerfTools/ProfilerService/interface/ProfilerService.h"
namespace {
- int nvtxDomainRangePush(nvtxDomainHandle_t domain, const char* message) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- return nvtxDomainRangePushEx(domain, &eventAttrib);
- }
-
- __attribute__((unused)) int nvtxDomainRangePushColor(nvtxDomainHandle_t domain, const char* message, uint32_t color) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.colorType = NVTX_COLOR_ARGB;
- eventAttrib.color = color;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- return nvtxDomainRangePushEx(domain, &eventAttrib);
- }
-
- __attribute__((unused)) nvtxRangeId_t nvtxDomainRangeStart(nvtxDomainHandle_t domain, const char* message) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- return nvtxDomainRangeStartEx(domain, &eventAttrib);
- }
-
- nvtxRangeId_t nvtxDomainRangeStartColor(nvtxDomainHandle_t domain, const char* message, uint32_t color) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.colorType = NVTX_COLOR_ARGB;
- eventAttrib.color = color;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- return nvtxDomainRangeStartEx(domain, &eventAttrib);
- }
-
- void nvtxDomainMark(nvtxDomainHandle_t domain, const char* message) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- nvtxDomainMarkEx(domain, &eventAttrib);
- }
-
- __attribute__((unused)) void nvtxDomainMarkColor(nvtxDomainHandle_t domain, const char* message, uint32_t color) {
- nvtxEventAttributes_t eventAttrib = {};
- eventAttrib.version = NVTX_VERSION;
- eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
- eventAttrib.colorType = NVTX_COLOR_ARGB;
- eventAttrib.color = color;
- eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
- eventAttrib.message.ascii = message;
- nvtxDomainMarkEx(domain, &eventAttrib);
- }
-
- enum {
- nvtxBlack = 0x00000000,
- nvtxRed = 0x00ff0000,
- nvtxDarkGreen = 0x00009900,
- nvtxGreen = 0x0000ff00,
- nvtxLightGreen = 0x00ccffcc,
- nvtxBlue = 0x000000ff,
- nvtxAmber = 0x00ffbf00,
- nvtxLightAmber = 0x00fff2cc,
- nvtxWhite = 0x00ffffff
- };
-
- constexpr nvtxRangeId_t nvtxInvalidRangeId = 0xfffffffffffffffful;
-} // namespace
-
-class NVProfilerService {
-public:
- NVProfilerService(const edm::ParameterSet&, edm::ActivityRegistry&);
- ~NVProfilerService();
-
- static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
-
- void preallocate(edm::service::SystemBounds const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preBeginJob(edm::ProcessContext const&);
- void postBeginJob();
-
- void lookupInitializationComplete(edm::PathsAndConsumesOfModulesBase const&, edm::ProcessContext const&);
-
- // there is no preEndJob() signal
- void postEndJob();
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preGlobalBeginRun(edm::GlobalContext const&);
- void postGlobalBeginRun(edm::GlobalContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preGlobalEndRun(edm::GlobalContext const&);
- void postGlobalEndRun(edm::GlobalContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preStreamBeginRun(edm::StreamContext const&);
- void postStreamBeginRun(edm::StreamContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preStreamEndRun(edm::StreamContext const&);
- void postStreamEndRun(edm::StreamContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preGlobalBeginLumi(edm::GlobalContext const&);
- void postGlobalBeginLumi(edm::GlobalContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preGlobalEndLumi(edm::GlobalContext const&);
- void postGlobalEndLumi(edm::GlobalContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preStreamBeginLumi(edm::StreamContext const&);
- void postStreamBeginLumi(edm::StreamContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preStreamEndLumi(edm::StreamContext const&);
- void postStreamEndLumi(edm::StreamContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preEvent(edm::StreamContext const&);
- void postEvent(edm::StreamContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void prePathEvent(edm::StreamContext const&, edm::PathContext const&);
- void postPathEvent(edm::StreamContext const&, edm::PathContext const&, edm::HLTPathStatus const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preModuleEventPrefetching(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleEventPrefetching(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preOpenFile(std::string const&);
- void postOpenFile(std::string const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preCloseFile(std::string const&);
- void postCloseFile(std::string const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preSourceConstruction(edm::ModuleDescription const&);
- void postSourceConstruction(edm::ModuleDescription const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preSourceRun(edm::RunIndex);
- void postSourceRun(edm::RunIndex);
-
- // these signal pair are guaranteed to be called by the same thread
- void preSourceLumi(edm::LuminosityBlockIndex);
- void postSourceLumi(edm::LuminosityBlockIndex);
-
- // these signal pair are guaranteed to be called by the same thread
- void preSourceEvent(edm::StreamID);
- void postSourceEvent(edm::StreamID);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleConstruction(edm::ModuleDescription const&);
- void postModuleConstruction(edm::ModuleDescription const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleDestruction(edm::ModuleDescription const&);
- void postModuleDestruction(edm::ModuleDescription const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleBeginJob(edm::ModuleDescription const&);
- void postModuleBeginJob(edm::ModuleDescription const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleEndJob(edm::ModuleDescription const&);
- void postModuleEndJob(edm::ModuleDescription const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleBeginStream(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleBeginStream(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleEndStream(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleEndStream(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleGlobalBeginRun(edm::GlobalContext const&, edm::ModuleCallingContext const&);
- void postModuleGlobalBeginRun(edm::GlobalContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleGlobalEndRun(edm::GlobalContext const&, edm::ModuleCallingContext const&);
- void postModuleGlobalEndRun(edm::GlobalContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleGlobalBeginLumi(edm::GlobalContext const&, edm::ModuleCallingContext const&);
- void postModuleGlobalBeginLumi(edm::GlobalContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleGlobalEndLumi(edm::GlobalContext const&, edm::ModuleCallingContext const&);
- void postModuleGlobalEndLumi(edm::GlobalContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleStreamBeginRun(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleStreamBeginRun(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleStreamEndRun(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleStreamEndRun(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleStreamBeginLumi(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleStreamBeginLumi(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleStreamEndLumi(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleStreamEndLumi(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleEventAcquire(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleEventAcquire(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleEvent(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleEvent(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleEventDelayedGet(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleEventDelayedGet(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preEventReadFromSource(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postEventReadFromSource(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- void preModuleTransformPrefetching(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleTransformPrefetching(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleTransformAcquiring(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleTransformAcquiring(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
- // these signal pair are guaranteed to be called by the same thread
- void preModuleTransform(edm::StreamContext const&, edm::ModuleCallingContext const&);
- void postModuleTransform(edm::StreamContext const&, edm::ModuleCallingContext const&);
-
-private:
- bool highlight(std::string const& label) const {
- return (std::binary_search(highlightModules_.begin(), highlightModules_.end(), label));
- }
-
- uint32_t labelColor(std::string const& label) const { return highlight(label) ? nvtxAmber : nvtxGreen; }
-
- uint32_t labelColorLight(std::string const& label) const {
- return highlight(label) ? nvtxLightAmber : nvtxLightGreen;
- }
-
- std::vector highlightModules_;
- const bool showModulePrefetching_;
- const bool skipFirstEvent_;
-
- std::atomic globalFirstEventDone_ = false;
- std::vector> streamFirstEventDone_;
- std::vector event_; // per-stream event ranges
- std::vector> stream_modules_; // per-stream, per-module ranges
- // use a tbb::concurrent_vector rather than an std::vector because its final size is not known
- tbb::concurrent_vector global_modules_; // global per-module events
-
- nvtxDomainHandle_t global_domain_; // NVTX domain for global EDM transitions
- std::vector stream_domain_; // NVTX domains for per-EDM-stream transitions
-};
-
-NVProfilerService::NVProfilerService(edm::ParameterSet const& config, edm::ActivityRegistry& registry)
- : highlightModules_(config.getUntrackedParameter>("highlightModules")),
- showModulePrefetching_(config.getUntrackedParameter("showModulePrefetching")),
- skipFirstEvent_(config.getUntrackedParameter("skipFirstEvent")) {
- // make sure that CUDA is initialised, and that the CUDAInterface destructor is called after this service's destructor
- edm::Service cuda;
- if (not cuda or not cuda->enabled())
- return;
-
- std::sort(highlightModules_.begin(), highlightModules_.end());
-
- // create the NVTX domain for global EDM transitions
- global_domain_ = nvtxDomainCreate("EDM Global");
-
- // enables profile collection; if profiling is already enabled it has no effect
- if (not skipFirstEvent_) {
- cudaProfilerStart();
- }
-
- registry.watchPreallocate(this, &NVProfilerService::preallocate);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreBeginJob(this, &NVProfilerService::preBeginJob);
- registry.watchPostBeginJob(this, &NVProfilerService::postBeginJob);
-
- registry.watchLookupInitializationComplete(this, &NVProfilerService::lookupInitializationComplete);
-
- // there is no preEndJob() signal
- registry.watchPostEndJob(this, &NVProfilerService::postEndJob);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreGlobalBeginRun(this, &NVProfilerService::preGlobalBeginRun);
- registry.watchPostGlobalBeginRun(this, &NVProfilerService::postGlobalBeginRun);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreGlobalEndRun(this, &NVProfilerService::preGlobalEndRun);
- registry.watchPostGlobalEndRun(this, &NVProfilerService::postGlobalEndRun);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreStreamBeginRun(this, &NVProfilerService::preStreamBeginRun);
- registry.watchPostStreamBeginRun(this, &NVProfilerService::postStreamBeginRun);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreStreamEndRun(this, &NVProfilerService::preStreamEndRun);
- registry.watchPostStreamEndRun(this, &NVProfilerService::postStreamEndRun);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreGlobalBeginLumi(this, &NVProfilerService::preGlobalBeginLumi);
- registry.watchPostGlobalBeginLumi(this, &NVProfilerService::postGlobalBeginLumi);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreGlobalEndLumi(this, &NVProfilerService::preGlobalEndLumi);
- registry.watchPostGlobalEndLumi(this, &NVProfilerService::postGlobalEndLumi);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreStreamBeginLumi(this, &NVProfilerService::preStreamBeginLumi);
- registry.watchPostStreamBeginLumi(this, &NVProfilerService::postStreamBeginLumi);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreStreamEndLumi(this, &NVProfilerService::preStreamEndLumi);
- registry.watchPostStreamEndLumi(this, &NVProfilerService::postStreamEndLumi);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreEvent(this, &NVProfilerService::preEvent);
- registry.watchPostEvent(this, &NVProfilerService::postEvent);
-
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPrePathEvent(this, &NVProfilerService::prePathEvent);
- registry.watchPostPathEvent(this, &NVProfilerService::postPathEvent);
-
- if (showModulePrefetching_) {
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreModuleEventPrefetching(this, &NVProfilerService::preModuleEventPrefetching);
- registry.watchPostModuleEventPrefetching(this, &NVProfilerService::postModuleEventPrefetching);
- }
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreOpenFile(this, &NVProfilerService::preOpenFile);
- registry.watchPostOpenFile(this, &NVProfilerService::postOpenFile);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreCloseFile(this, &NVProfilerService::preCloseFile);
- registry.watchPostCloseFile(this, &NVProfilerService::postCloseFile);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreSourceConstruction(this, &NVProfilerService::preSourceConstruction);
- registry.watchPostSourceConstruction(this, &NVProfilerService::postSourceConstruction);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreSourceRun(this, &NVProfilerService::preSourceRun);
- registry.watchPostSourceRun(this, &NVProfilerService::postSourceRun);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreSourceLumi(this, &NVProfilerService::preSourceLumi);
- registry.watchPostSourceLumi(this, &NVProfilerService::postSourceLumi);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreSourceEvent(this, &NVProfilerService::preSourceEvent);
- registry.watchPostSourceEvent(this, &NVProfilerService::postSourceEvent);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleConstruction(this, &NVProfilerService::preModuleConstruction);
- registry.watchPostModuleConstruction(this, &NVProfilerService::postModuleConstruction);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleDestruction(this, &NVProfilerService::preModuleDestruction);
- registry.watchPostModuleDestruction(this, &NVProfilerService::postModuleDestruction);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleBeginJob(this, &NVProfilerService::preModuleBeginJob);
- registry.watchPostModuleBeginJob(this, &NVProfilerService::postModuleBeginJob);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleEndJob(this, &NVProfilerService::preModuleEndJob);
- registry.watchPostModuleEndJob(this, &NVProfilerService::postModuleEndJob);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleBeginStream(this, &NVProfilerService::preModuleBeginStream);
- registry.watchPostModuleBeginStream(this, &NVProfilerService::postModuleBeginStream);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleEndStream(this, &NVProfilerService::preModuleEndStream);
- registry.watchPostModuleEndStream(this, &NVProfilerService::postModuleEndStream);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleGlobalBeginRun(this, &NVProfilerService::preModuleGlobalBeginRun);
- registry.watchPostModuleGlobalBeginRun(this, &NVProfilerService::postModuleGlobalBeginRun);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleGlobalEndRun(this, &NVProfilerService::preModuleGlobalEndRun);
- registry.watchPostModuleGlobalEndRun(this, &NVProfilerService::postModuleGlobalEndRun);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleGlobalBeginLumi(this, &NVProfilerService::preModuleGlobalBeginLumi);
- registry.watchPostModuleGlobalBeginLumi(this, &NVProfilerService::postModuleGlobalBeginLumi);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleGlobalEndLumi(this, &NVProfilerService::preModuleGlobalEndLumi);
- registry.watchPostModuleGlobalEndLumi(this, &NVProfilerService::postModuleGlobalEndLumi);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleStreamBeginRun(this, &NVProfilerService::preModuleStreamBeginRun);
- registry.watchPostModuleStreamBeginRun(this, &NVProfilerService::postModuleStreamBeginRun);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleStreamEndRun(this, &NVProfilerService::preModuleStreamEndRun);
- registry.watchPostModuleStreamEndRun(this, &NVProfilerService::postModuleStreamEndRun);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleStreamBeginLumi(this, &NVProfilerService::preModuleStreamBeginLumi);
- registry.watchPostModuleStreamBeginLumi(this, &NVProfilerService::postModuleStreamBeginLumi);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleStreamEndLumi(this, &NVProfilerService::preModuleStreamEndLumi);
- registry.watchPostModuleStreamEndLumi(this, &NVProfilerService::postModuleStreamEndLumi);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleEventAcquire(this, &NVProfilerService::preModuleEventAcquire);
- registry.watchPostModuleEventAcquire(this, &NVProfilerService::postModuleEventAcquire);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleEvent(this, &NVProfilerService::preModuleEvent);
- registry.watchPostModuleEvent(this, &NVProfilerService::postModuleEvent);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleEventDelayedGet(this, &NVProfilerService::preModuleEventDelayedGet);
- registry.watchPostModuleEventDelayedGet(this, &NVProfilerService::postModuleEventDelayedGet);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreEventReadFromSource(this, &NVProfilerService::preEventReadFromSource);
- registry.watchPostEventReadFromSource(this, &NVProfilerService::postEventReadFromSource);
-
- if (showModulePrefetching_) {
- // these signal pair are NOT guaranteed to be called by the same thread
- registry.watchPreModuleTransformPrefetching(this, &NVProfilerService::preModuleTransformPrefetching);
- registry.watchPostModuleTransformPrefetching(this, &NVProfilerService::postModuleTransformPrefetching);
- }
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleTransform(this, &NVProfilerService::preModuleTransform);
- registry.watchPostModuleTransform(this, &NVProfilerService::postModuleTransform);
-
- // these signal pair are guaranteed to be called by the same thread
- registry.watchPreModuleTransformAcquiring(this, &NVProfilerService::preModuleTransformAcquiring);
- registry.watchPostModuleTransformAcquiring(this, &NVProfilerService::postModuleTransformAcquiring);
-}
-
-NVProfilerService::~NVProfilerService() {
- for (unsigned int sid = 0; sid < stream_domain_.size(); ++sid) {
- nvtxDomainDestroy(stream_domain_[sid]);
- }
- nvtxDomainDestroy(global_domain_);
- cudaProfilerStop();
-}
+ /**
+ * \brief Backend for NVidia's Nsight Systems profiling.
+ */
+ class NVTXBackend {
+ public:
+ // Forward definitions
+ using Color = ProfilerServiceBase::Color;
+ static constexpr auto to_underlying = ProfilerServiceBase::to_underlying;
+ using SpinLock = ProfilerServiceBase::SpinLock;
+ class Range;
+ class Domain;
+ static void mark(const Domain& domain, const char* message, Color color);
+ /**
+ * \note These functions can be used multiple times. See doc at:
+ * https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__PROFILER.html
+ */
+ static void profilerStart() { cudaProfilerStart(); }
+ static void profilerStop() { cudaProfilerStop(); }
+
+ private:
+ static __attribute__((unused)) void nvtxDomainMarkColor(nvtxDomainHandle_t domain,
+ const char* message,
+ uint32_t color) {
+ nvtxEventAttributes_t eventAttrib = {};
+ eventAttrib.version = NVTX_VERSION;
+ eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
+ eventAttrib.colorType = NVTX_COLOR_ARGB;
+ eventAttrib.color = color;
+ eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
+ eventAttrib.message.ascii = message;
+ nvtxDomainMarkEx(domain, &eventAttrib);
+ }
-void NVProfilerService::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
- edm::ParameterSetDescription desc;
- desc.addUntracked>("highlightModules", {})->setComment("");
- desc.addUntracked("showModulePrefetching", false)
- ->setComment("Show the stack of dependencies that requested to run a module.");
- desc.addUntracked("skipFirstEvent", false)
- ->setComment(
- "Start profiling after the first event has completed.\nWith multiple streams, ignore transitions belonging "
- "to events started in parallel to the first event.\nRequires running nvprof with the '--profile-from-start "
- "off' option.");
- descriptions.add("NVProfilerService", desc);
- descriptions.setComment(R"(This Service provides CMSSW-aware annotations to nvprof/nvvm.
+ public:
+ using EDMService = edm::Service;
+ class Domain {
+ public:
+ friend class Range;
+ friend void NVTXBackend::mark(const Domain& domain, const char* message, Color color);
+ Domain() = default;
+ ~Domain() {
+ if (domain_ != nvtxInvalidDomainId) {
+ nvtxDomainDestroy(domain_);
+ domain_ = nvtxInvalidDomainId;
+ }
+ }
+ void create(const std::string& name) {
+ // Assert the domain is created only once
+ assert(domain_ == nvtxInvalidDomainId);
+ domain_ = nvtxDomainCreateA(name.c_str());
+ }
+ void destroy() {
+ if (domain_ != nvtxInvalidDomainId) {
+ nvtxDomainDestroy(domain_);
+ domain_ = nvtxInvalidDomainId;
+ }
+ }
+
+ private:
+ static constexpr nvtxDomainHandle_t nvtxInvalidDomainId = nullptr;
+ nvtxDomainHandle_t nativeHandle() const { return domain_; }
+ nvtxDomainHandle_t domain_ = nvtxInvalidDomainId;
+ };
+
+ class Range {
+ public:
+ friend void NVTXBackend::mark(const Domain& domain, const char* message, Color color);
+ Range() = default;
+ // copy constructor deleted
+ Range(const Range&) = delete;
+ /// Move copy constructor: we take a lock and move the contents
+ /// We need it to resize vectors
+ Range(Range&& o) noexcept {
+ std::scoped_lock lock(o.mtx_);
+ std::scoped_lock lock2(mtx_);
+ domain_ = o.domain_;
+ range_ = o.range_;
+ o.domain_ = Domain::nvtxInvalidDomainId;
+ o.range_ = nvtxInvalidRangeId;
+ }
+ ~Range() {
+ std::scoped_lock lock(mtx_);
+ if (range_ != nvtxInvalidRangeId)
+ nvtxDomainRangeEnd(domain_, range_);
+ }
+
+ private:
+ nvtxRangeId_t nvtxDomainRangeStartColor(const Domain& domain, const char* message, uint32_t color) {
+ nvtxEventAttributes_t eventAttrib = {};
+ eventAttrib.version = NVTX_VERSION;
+ eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
+ eventAttrib.colorType = NVTX_COLOR_ARGB;
+ eventAttrib.color = color;
+ eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
+ eventAttrib.message.ascii = message;
+ return nvtxDomainRangeStartEx(domain.nativeHandle(), &eventAttrib);
+ }
+
+ static constexpr nvtxRangeId_t nvtxInvalidRangeId = ~0ul;
+
+ static constexpr std::array colorMap = {{
+ 0x00000000, // Black
+ // Red family
+ 0x00880000, // Red_Dark2
+ 0x00cc0000, // Red_Dark1
+ 0x00ff0000, // Red
+ 0x00ff8080, // Red_Light1
+ 0x00ffcccc, // Red_Light2
+ // Green family
+ 0x00004400, // Green_Dark2
+ 0x00009900, // Green_Dark1
+ 0x0000ff00, // Green
+ 0x0099ff99, // Green_Light1
+ 0x00ccffcc, // Green_Light2
+ // Blue family
+ 0x00000077, // Blue_Dark2
+ 0x000000bb, // Blue_Dark1
+ 0x000000ff, // Blue
+ 0x009999ff, // Blue_Light1
+ 0x00ccccff, // Blue_Light2
+ // Amber family
+ 0x00886600, // Amber_Dark2
+ 0x00cc9900, // Amber_Dark1
+ 0x00ffbf00, // Amber
+ 0x00ffd966, // Amber_Light1
+ 0x00fff2cc, // Amber_Light2
+ 0x00ffffff, // White
+ // Grey family
+ 0x00404040, // Grey_Dark2
+ 0x00606060, // Grey_Dark1
+ 0x00808080, // Grey
+ 0x00a0a0a0, // Grey_Light1
+ 0x00c0c0c0, // Grey_Light2
+ // Yellow family
+ 0x00888800, // Yellow_Dark2
+ 0x00cccc00, // Yellow_Dark1
+ 0x00ffff00, // Yellow
+ 0x00ffff66, // Yellow_Light1
+ 0x00ffffcc // Yellow_Light2
+ }};
+
+ public:
+ void startColorIn(const Domain& domain, const char* message, Color color, const char* where) {
+ std::scoped_lock lock(mtx_);
+#undef NVTX_RANGE_DEBUG
+#ifdef NVTX_RANGE_DEBUG
+ std::fprintf(stderr,
+ "[NVTX_RANGE] start this=%p domain=%p range=%lu msg=%s where=%s\n",
+ static_cast(this),
+ static_cast(domain_),
+ static_cast(range_),
+ message ? message : "(null)",
+ where ? where : "(null)");
+#endif
+ if (range_ != nvtxInvalidRangeId) {
+ std::string fullmsg =
+ fmt::sprintf("Warning: previous range not ended before starting a new one in %s for %s", where, message);
+ abort();
+ nvtxDomainMarkColor(domain_, fullmsg.c_str(), colorMap[to_underlying(Color::Red)]);
+ nvtxDomainRangeEnd(domain_, range_);
+ }
+ domain_ = domain.nativeHandle();
+ range_ = nvtxDomainRangeStartColor(domain, message, colorMap[to_underlying(color)]);
+ }
+
+ void endIn(const Domain& domain, const char* message, const char* where) {
+ std::scoped_lock lock(mtx_);
+#ifdef NVTX_RANGE_DEBUG
+ std::fprintf(stderr,
+ "[NVTX_RANGE] end this=%p domain=%p range=%lu msg=%s where=%s\n",
+ static_cast(this),
+ static_cast(domain_),
+ static_cast(range_),
+ message ? message : "(null)",
+ where ? where : "(null)");
+#endif
+ if (range_ != nvtxInvalidRangeId) {
+ nvtxDomainRangeEnd(domain_, range_);
+ range_ = nvtxInvalidRangeId;
+ domain_ = Domain::nvtxInvalidDomainId;
+ } else {
+ std::string fullmsg =
+ fmt::sprintf("Warning: trying to end a range that is not started in %s for %s", where, message);
+ abort();
+ nvtxDomainMarkColor(domain.nativeHandle(), fullmsg.c_str(), colorMap[to_underlying(Color::Red)]);
+ }
+ }
+
+ private:
+ nvtxRangeId_t range_ = nvtxInvalidRangeId;
+ nvtxDomainHandle_t domain_ = Domain::nvtxInvalidDomainId;
+ SpinLock mtx_ = SpinLock{};
+ };
+
+ static std::string shortName() { return "NV"; }
+ static std::string serviceComment() {
+ return R"(This Service provides CMSSW-aware annotations to nvprof/nvvm.
Notes on nvprof options:
- the option '--profile-from-start off' should be used if skipFirstEvent is True.
- - the option '--cpu-profiling on' currently results in cmsRun being stuck at the beginning of the job.
- - the option '--cpu-thread-tracing on' is not compatible with jemalloc, and should only be used with cmsRunGlibC.)");
-}
-
-void NVProfilerService::preallocate(edm::service::SystemBounds const& bounds) {
- std::stringstream out;
- out << "preallocate: " << bounds.maxNumberOfConcurrentRuns() << " concurrent runs, "
- << bounds.maxNumberOfConcurrentLuminosityBlocks() << " luminosity sections, " << bounds.maxNumberOfStreams()
- << " streams\nrunning on " << bounds.maxNumberOfThreads() << " threads";
- nvtxDomainMark(global_domain_, out.str().c_str());
-
- auto concurrentStreams = bounds.maxNumberOfStreams();
- // create the NVTX domains for per-EDM-stream transitions
- stream_domain_.resize(concurrentStreams);
- for (unsigned int sid = 0; sid < concurrentStreams; ++sid) {
- stream_domain_[sid] = nvtxDomainCreate(fmt::sprintf("EDM Stream %d", sid).c_str());
- }
-
- event_.resize(concurrentStreams);
- stream_modules_.resize(concurrentStreams);
- for (auto& modulesForOneStream : stream_modules_) {
- modulesForOneStream.resize(global_modules_.size(), nvtxInvalidRangeId);
- }
-
- if (skipFirstEvent_) {
- globalFirstEventDone_ = false;
- std::vector> tmp(concurrentStreams);
- for (auto& element : tmp)
- std::atomic_init(&element, false);
- streamFirstEventDone_ = std::move(tmp);
- }
-}
-
-void NVProfilerService::preBeginJob(edm::ProcessContext const& context) {
- nvtxDomainMark(global_domain_, "preBeginJob");
-}
-
-void NVProfilerService::postBeginJob() {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainMark(global_domain_, "postBeginJob");
- }
-}
-
-void NVProfilerService::lookupInitializationComplete(edm::PathsAndConsumesOfModulesBase const&,
- edm::ProcessContext const&) {
- nvtxDomainMark(global_domain_, "lookupInitializationComplete");
-}
-
-void NVProfilerService::postEndJob() {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainMark(global_domain_, "postEndJob");
- }
-}
-
-void NVProfilerService::preSourceEvent(edm::StreamID sid) {
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePush(stream_domain_[sid], "source");
- }
-}
-
-void NVProfilerService::postSourceEvent(edm::StreamID sid) {
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePop(stream_domain_[sid]);
- }
-}
-
-void NVProfilerService::preSourceLumi(edm::LuminosityBlockIndex index) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "source lumi");
- }
-}
-
-void NVProfilerService::postSourceLumi(edm::LuminosityBlockIndex index) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preSourceRun(edm::RunIndex index) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "source run");
- }
-}
-
-void NVProfilerService::postSourceRun(edm::RunIndex index) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preOpenFile(std::string const& lfn) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, ("open file "s + lfn).c_str());
- }
-}
-
-void NVProfilerService::postOpenFile(std::string const& lfn) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preCloseFile(std::string const& lfn) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, ("close file "s + lfn).c_str());
- }
-}
-
-void NVProfilerService::postCloseFile(std::string const& lfn) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preModuleBeginStream(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " begin stream";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleBeginStream(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleEndStream(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " end stream";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleEndStream(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preGlobalBeginRun(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "global begin run");
- }
-}
-
-void NVProfilerService::postGlobalBeginRun(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preGlobalEndRun(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "global end run");
- }
-}
-
-void NVProfilerService::postGlobalEndRun(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preStreamBeginRun(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePush(stream_domain_[sid], "stream begin run");
- }
-}
-
-void NVProfilerService::postStreamBeginRun(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePop(stream_domain_[sid]);
- }
-}
-
-void NVProfilerService::preStreamEndRun(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePush(stream_domain_[sid], "stream end run");
- }
-}
-
-void NVProfilerService::postStreamEndRun(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePop(stream_domain_[sid]);
- }
-}
-
-void NVProfilerService::preGlobalBeginLumi(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "global begin lumi");
- }
-}
-
-void NVProfilerService::postGlobalBeginLumi(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preGlobalEndLumi(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePush(global_domain_, "global end lumi");
- }
-}
-
-void NVProfilerService::postGlobalEndLumi(edm::GlobalContext const& gc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- nvtxDomainRangePop(global_domain_);
- }
-}
-
-void NVProfilerService::preStreamBeginLumi(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePush(stream_domain_[sid], "stream begin lumi");
- }
-}
-
-void NVProfilerService::postStreamBeginLumi(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePop(stream_domain_[sid]);
- }
-}
-
-void NVProfilerService::preStreamEndLumi(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- nvtxDomainRangePush(stream_domain_[sid], "stream end lumi");
-}
-
-void NVProfilerService::postStreamEndLumi(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangePop(stream_domain_[sid]);
- }
-}
-
-void NVProfilerService::preEvent(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- event_[sid] = nvtxDomainRangeStartColor(stream_domain_[sid], "event", nvtxDarkGreen);
- }
-}
-
-void NVProfilerService::postEvent(edm::StreamContext const& sc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainRangeEnd(stream_domain_[sid], event_[sid]);
- event_[sid] = nvtxInvalidRangeId;
- } else {
- streamFirstEventDone_[sid] = true;
- auto identity = [](bool x) { return x; };
- if (std::all_of(streamFirstEventDone_.begin(), streamFirstEventDone_.end(), identity)) {
- bool expected = false;
- if (globalFirstEventDone_.compare_exchange_strong(expected, true))
- cudaProfilerStart();
+ - the option '--cpu-profiling on' currently results in cmsRun being stuck at the beginning of the job.
+ - the option '--cpu-thread-tracing on' is not compatible with jemalloc, and should only be used with cmsRunGlibC.)";
}
- }
-}
-
-void NVProfilerService::prePathEvent(edm::StreamContext const& sc, edm::PathContext const& pc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainMark(global_domain_, ("before path "s + pc.pathName()).c_str());
- }
-}
-
-void NVProfilerService::postPathEvent(edm::StreamContext const& sc,
- edm::PathContext const& pc,
- edm::HLTPathStatus const& hlts) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- nvtxDomainMark(global_domain_, ("after path "s + pc.pathName()).c_str());
- }
-}
-
-void NVProfilerService::preModuleEventPrefetching(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " prefetching";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColorLight(label));
- }
-}
-
-void NVProfilerService::postModuleEventPrefetching(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleConstruction(edm::ModuleDescription const& desc) {
- auto mid = desc.id();
- global_modules_.grow_to_at_least(mid + 1);
-
- // This normally does nothing because stream_modules_ is empty when
- // called. But there is a rare case when a looper is used that replacement
- // modules can be constructed at end of loop. I'm not sure if that feature
- // is ever actually used but just to be safe...
- for (auto& modulesForOneStream : stream_modules_) {
- modulesForOneStream.resize(global_modules_.size(), nvtxInvalidRangeId);
- }
-
- if (not skipFirstEvent_) {
- auto const& label = desc.moduleLabel();
- auto const& msg = label + " construction";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleConstruction(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleDestruction(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- auto const& label = desc.moduleLabel();
- auto const& msg = label + " destruction";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleDestruction(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleBeginJob(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- auto const& label = desc.moduleLabel();
- auto const& msg = label + " begin job";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleBeginJob(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleEndJob(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = desc.id();
- auto const& label = desc.moduleLabel();
- auto const& msg = label + " end job";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleEndJob(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = desc.id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleEventAcquire(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " acquire";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleEventAcquire(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleEvent(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], label.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleEvent(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleEventDelayedGet(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- /* FIXME
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const & label = mcc.moduleDescription()->moduleLabel();
- auto const & msg = label + " delayed get";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], label.c_str(), labelColorLight(label));
- }
- */
-}
-
-void NVProfilerService::postModuleEventDelayedGet(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- /* FIXME
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
- */
-}
-
-void NVProfilerService::preEventReadFromSource(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- /* FIXME
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const & label = mcc.moduleDescription()->moduleLabel();
- auto const & msg = label + " read from source";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColorLight(label));
- }
- */
-}
-
-void NVProfilerService::postEventReadFromSource(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- /* FIXME
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
- */
-}
-
-void NVProfilerService::preModuleStreamBeginRun(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " stream begin run";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleStreamBeginRun(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleStreamEndRun(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " stream end run";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleStreamEndRun(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleStreamBeginLumi(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " stream begin lumi";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleStreamBeginLumi(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleStreamEndLumi(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " stream end lumi";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleStreamEndLumi(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleGlobalBeginRun(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " global begin run";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleGlobalBeginRun(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleGlobalEndRun(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " global end run";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleGlobalEndRun(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleGlobalBeginLumi(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " global begin lumi";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleGlobalBeginLumi(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleGlobalEndLumi(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " global end lumi";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleGlobalEndLumi(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) {
- if (not skipFirstEvent_ or globalFirstEventDone_) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preSourceConstruction(edm::ModuleDescription const& desc) {
- auto mid = desc.id();
- global_modules_.grow_to_at_least(mid + 1);
-
- if (not skipFirstEvent_) {
- auto const& label = desc.moduleLabel();
- auto const& msg = label + " construction";
- global_modules_[mid] = nvtxDomainRangeStartColor(global_domain_, msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postSourceConstruction(edm::ModuleDescription const& desc) {
- if (not skipFirstEvent_) {
- auto mid = desc.id();
- nvtxDomainRangeEnd(global_domain_, global_modules_[mid]);
- global_modules_[mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleTransformPrefetching(edm::StreamContext const& sc,
- edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " transform prefetching";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColorLight(label));
- }
-}
-
-void NVProfilerService::postModuleTransformPrefetching(edm::StreamContext const& sc,
- edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
-
-void NVProfilerService::preModuleTransformAcquiring(edm::StreamContext const& sc,
- edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " transform acquire";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
- }
-}
-
-void NVProfilerService::postModuleTransformAcquiring(edm::StreamContext const& sc,
- edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
+ };
-void NVProfilerService::preModuleTransform(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- auto const& label = mcc.moduleDescription()->moduleLabel();
- auto const& msg = label + " transform";
- assert(stream_modules_[sid][mid] == nvtxInvalidRangeId);
- stream_modules_[sid][mid] = nvtxDomainRangeStartColor(stream_domain_[sid], msg.c_str(), labelColor(label));
+ void NVTXBackend::mark(const NVTXBackend::Domain& domain, const char* message, Color color) {
+ NVTXBackend::nvtxDomainMarkColor(
+ domain.nativeHandle(), message, NVTXBackend::Range::colorMap[to_underlying(color)]);
}
-}
+} // namespace
-void NVProfilerService::postModuleTransform(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) {
- auto sid = sc.streamID();
- if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
- auto mid = mcc.moduleDescription()->id();
- nvtxDomainRangeEnd(stream_domain_[sid], stream_modules_[sid][mid]);
- stream_modules_[sid][mid] = nvtxInvalidRangeId;
- }
-}
+class NVProfilerService : public ProfilerService {
+public:
+ using ProfilerService::ProfilerService;
+};
#include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
DEFINE_FWK_SERVICE(NVProfilerService);
diff --git a/HeterogeneousCore/ROCmServices/plugins/BuildFile.xml b/HeterogeneousCore/ROCmServices/plugins/BuildFile.xml
index e51a93d031238..467a27f49c8c4 100644
--- a/HeterogeneousCore/ROCmServices/plugins/BuildFile.xml
+++ b/HeterogeneousCore/ROCmServices/plugins/BuildFile.xml
@@ -1,5 +1,6 @@
+
@@ -8,7 +9,8 @@
-
+
+
diff --git a/HeterogeneousCore/ROCmServices/plugins/ROCmProfilerService.cc b/HeterogeneousCore/ROCmServices/plugins/ROCmProfilerService.cc
new file mode 100644
index 0000000000000..0c3e4944b349d
--- /dev/null
+++ b/HeterogeneousCore/ROCmServices/plugins/ROCmProfilerService.cc
@@ -0,0 +1,137 @@
+#include
+
+#include "HeterogeneousCore/ROCmServices/interface/ROCmInterface.h"
+
+#include "PerfTools/ProfilerService/interface/ProfilerService.h"
+
+namespace {
+ /**
+ * \brief Backend for ROCm profiling.
+ * \note All APIs used are part of ROCTX. See documentation at
+ * https://rocm.docs.amd.com/projects/rocprofiler-sdk/en/latest/how-to/using-rocprofiler-sdk-roctx.html
+ */
+ class ROCmBackend {
+ public:
+ // Forward definitions
+ using Color = ProfilerServiceBase::Color;
+ using SpinLock = ProfilerServiceBase::SpinLock;
+ class Range;
+ class Domain;
+ static void mark(const Domain& domain, const char* message, Color color);
+ /**
+ * \note Latest doc is broken at time of writing, but older version is useful:
+ * https://rocm.docs.amd.com/projects/rocprofiler-sdk/en/docs-7.0.2/api-reference/rocprofiler-sdk-roctx_api/roctx_modules/profiler-control.html
+ */
+ static void profilerStart() {
+ // 0 for all threads.
+ roctxProfilerResume(0);
+ }
+ static void profilerStop() {
+ // 0 for all threads.
+ roctxProfilerPause(0);
+ }
+
+ public:
+ using EDMService = edm::Service;
+ class Domain {
+ public:
+ friend class Range;
+ friend void ROCmBackend::mark(const Domain& domain, const char* message, Color color);
+ Domain() = default;
+ ~Domain() = default;
+ void create(const std::string& name) {
+ // Assert the domain is created only once
+ assert(domain_.empty());
+ domain_ = name;
+ }
+ void destroy() { domain_.clear(); }
+
+ private:
+ const std::string& nativeHandle() const { return domain_; }
+ std::string domain_;
+ };
+
+ class Range {
+ public:
+ friend void ROCmBackend::mark(const Domain& domain, const char* message, Color color);
+ Range() = default;
+ // copy constructor deleted
+ Range(const Range&) = delete;
+ /// Move copy constructor: we take a lock and move the contents
+ /// We need it to resize vectors of unique_range_in
+ Range(Range&& o) noexcept {
+ std::scoped_lock lock(o.mtx_);
+ std::scoped_lock lock2(mtx_);
+ domain_ = o.domain_;
+ range_ = o.range_;
+ o.domain_.clear();
+ o.range_ = roctxInvalidRangeId;
+ }
+ ~Range() {
+ std::scoped_lock lock(mtx_);
+ if (range_ != roctxInvalidRangeId)
+ roctxRangeStop(range_);
+ }
+
+ private:
+ roctx_range_id_t roctxDomainRangeStartColor(const Domain& domain, const char* message, uint32_t color) {
+ return roctxRangeStartA((domain.nativeHandle() + "-" + message).c_str());
+ }
+
+ static constexpr roctx_range_id_t roctxInvalidRangeId = ~0ul;
+
+ public:
+ void startColorIn(const Domain& domain, const char* message, Color color, const char* where) {
+ std::scoped_lock lock(mtx_);
+ if (range_ != roctxInvalidRangeId) {
+ std::string fullmsg =
+ fmt::sprintf("Warning: previous range not ended before starting a new one in %s for %s", where, message);
+ roctxMarkA((domain_ + "-" + fullmsg).c_str());
+ roctxRangeStop(range_);
+ }
+ domain_ = domain.nativeHandle();
+ range_ = roctxRangeStartA((domain_ + "-" + message).c_str());
+ }
+
+ void endIn(const Domain& domain, const char* message, const char* where) {
+ std::scoped_lock lock(mtx_);
+ if (range_ != roctxInvalidRangeId) {
+ roctxRangeStop(range_);
+ range_ = roctxInvalidRangeId;
+ domain_.clear();
+ } else {
+ std::string fullmsg =
+ fmt::sprintf("Warning: trying to end a range that is not started in %s for %s", where, message);
+ roctxMarkA((domain_ + "-" + fullmsg).c_str());
+ }
+ }
+
+ private:
+ roctx_range_id_t range_ = roctxInvalidRangeId;
+ std::string domain_;
+ SpinLock mtx_ = SpinLock{};
+ };
+
+ static std::string shortName() { return "ROCm"; }
+ static std::string serviceComment() {
+ return R"(This Service provides CMSSW-aware annotations to nvprof/nvvm.
+
+Notes on nvprof options:
+ - the option '--profile-from-start off' should be used if skipFirstEvent is True.
+ - the option '--cpu-profiling on' currently results in cmsRun being stuck at the beginning of the job.
+ - the option '--cpu-thread-tracing on' is not compatible with jemalloc, and should only be used with cmsRunGlibC.)";
+ }
+ };
+
+ void ROCmBackend::mark(const ROCmBackend::Domain& domain, const char* message, Color color) {
+ roctxMark(("[" + domain.nativeHandle() + "]: " + std::string(message)).c_str());
+ }
+} // namespace
+
+class ROCmProfilerService : public ProfilerService {
+public:
+ using ProfilerService::ProfilerService;
+};
+
+#include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
+DEFINE_FWK_SERVICE(ROCmProfilerService);
diff --git a/PerfTools/ProfilerService/BuildFile.xml b/PerfTools/ProfilerService/BuildFile.xml
new file mode 100644
index 0000000000000..7f84153718df2
--- /dev/null
+++ b/PerfTools/ProfilerService/BuildFile.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/PerfTools/ProfilerService/interface/ProfilerService.h b/PerfTools/ProfilerService/interface/ProfilerService.h
new file mode 100644
index 0000000000000..747bc7b7bcf62
--- /dev/null
+++ b/PerfTools/ProfilerService/interface/ProfilerService.h
@@ -0,0 +1,1285 @@
+#ifndef __FWCore_Services_ProfilerService_h__
+#define __FWCore_Services_ProfilerService_h__
+
+#include "PerfTools/ProfilerService/interface/ProfilerServiceBase.h"
+
+#include
+
+#include "DataFormats/Provenance/interface/ModuleDescription.h"
+#include "FWCore/Framework/interface/ComponentDescription.h"
+#include "FWCore/Framework/interface/EventSetupRecordKey.h"
+#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
+#include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
+#include "FWCore/ServiceRegistry/interface/GlobalContext.h"
+#include "FWCore/ServiceRegistry/interface/ModuleCallingContext.h"
+#include "FWCore/ServiceRegistry/interface/ESModuleCallingContext.h"
+#include "FWCore/ServiceRegistry/interface/PathContext.h"
+#include "FWCore/ServiceRegistry/interface/Service.h"
+#include "FWCore/ServiceRegistry/interface/StreamContext.h"
+#include "FWCore/ServiceRegistry/interface/SystemBounds.h"
+
+/**
+ * Based template class for range/mark based profiling services, targeting
+ * NVidia NVTX, AMP ROCmTX, or VTune.
+ */
+
+/**
+ * Helper macros to declare signal handler pairs by parameter signature.
+ */
+#define DECLARE_SIGNAL_WATCHER_NOARGS(signal) \
+ void pre##signal(); \
+ void post##signal();
+
+#define DECLARE_SIGNAL_WATCHER_PROCESS_CONTEXT(signal) \
+ void pre##signal(edm::ProcessContext const&); \
+ void post##signal();
+
+#define DECLARE_SIGNAL_WATCHER_SOURCE_PROCESS_BLOCK(signal) \
+ void pre##signal(); \
+ void post##signal(std::string const&);
+
+#define DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(signal) \
+ void pre##signal(edm::StreamContext const&); \
+ void post##signal(edm::StreamContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(signal) \
+ void pre##signal(edm::GlobalContext const&); \
+ void post##signal(edm::GlobalContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_STREAM_ID(signal) \
+ void pre##signal(edm::StreamID); \
+ void post##signal(edm::StreamID);
+
+#define DECLARE_SIGNAL_WATCHER_LUMIBLOCK_INDEX(signal) \
+ void pre##signal(edm::LuminosityBlockIndex); \
+ void post##signal(edm::LuminosityBlockIndex);
+
+#define DECLARE_SIGNAL_WATCHER_RUN_INDEX(signal) \
+ void pre##signal(edm::RunIndex); \
+ void post##signal(edm::RunIndex);
+
+#define DECLARE_SIGNAL_WATCHER_STRING(signal) \
+ void pre##signal(std::string const&); \
+ void post##signal(std::string const&);
+
+#define DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(signal) \
+ void pre##signal(edm::ModuleDescription const&); \
+ void post##signal(edm::ModuleDescription const&);
+
+#define DECLARE_SIGNAL_WATCHER_COMPONENT_DESCRIPTION(signal) \
+ void pre##signal(edm::eventsetup::ComponentDescription const&); \
+ void post##signal(edm::eventsetup::ComponentDescription const&);
+
+#define DECLARE_SIGNAL_WATCHER_IOV_SYNC_VALUE(signal) \
+ void pre##signal(edm::IOVSyncValue const&); \
+ void post##signal(edm::IOVSyncValue const&);
+
+#define DECLARE_SIGNAL_WATCHER_EVENT_SETUP_RECORD_KEY_ES_MODULE_CALLING_CONTEXT(signal) \
+ void pre##signal(edm::eventsetup::EventSetupRecordKey const&, edm::ESModuleCallingContext const&); \
+ void post##signal(edm::eventsetup::EventSetupRecordKey const&, edm::ESModuleCallingContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT(signal) \
+ void pre##signal(edm::StreamContext const&, edm::PathContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT_HLT_STATUS(signal) \
+ void post##signal(edm::StreamContext const&, edm::PathContext const&, edm::HLTPathStatus const&);
+
+#define DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(signal) \
+ void pre##signal(edm::StreamContext const&, edm::ModuleCallingContext const&); \
+ void post##signal(edm::StreamContext const&, edm::ModuleCallingContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(signal) \
+ void pre##signal(edm::GlobalContext const&, edm::ModuleCallingContext const&); \
+ void post##signal(edm::GlobalContext const&, edm::ModuleCallingContext const&);
+
+#define DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_STREAM(signal) \
+ void pre##signal(edm::StreamContext const&, edm::TerminationOrigin);
+
+#define DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_GLOBAL(signal) \
+ void pre##signal(edm::GlobalContext const&, edm::TerminationOrigin);
+
+#define DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_SOURCE(signal) void pre##signal(edm::TerminationOrigin);
+
+/**
+ * @brief Base class for profiling services.
+ * @tparam Backend The backend implementation class.
+ * The backend will have to implement the actual range/mark operations, plus
+ * capture and domains management.
+ * Current expected classes and functions are:
+ * - Range class with:
+ * - startColorIn(domain, message, color, func)
+ * - endIn(domain, message, func)
+ * - markColorIn(domain, message, color, func)
+ * - Domain management class with:
+ * - domainCreate(name)
+ * - domainDestroy(domain) (maybe destructor will be enough)
+ * - Start/stop the underlying EDM service.
+ * - profilerStart()
+ * - profilerStop() (maybe wrapped into a class with the previous function)
+ */
+template
+class ProfilerService : public ProfilerServiceBase {
+public:
+ using Range = typename Backend::Range;
+ using Domain = typename Backend::Domain;
+
+ ProfilerService(const edm::ParameterSet&, edm::ActivityRegistry&);
+ ~ProfilerService();
+
+ static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
+
+ /******** Infrastructure/setup signal pairs *************************************/
+
+ void postServicesConstruction();
+
+ void preEventSetupModulesConstruction();
+ void postEventSetupModulesConstruction();
+
+ void preModulesAndSourceConstruction();
+ void postModulesAndSourceConstruction();
+
+ void preFinishSchedule();
+ void postFinishSchedule();
+
+ void prePrincipalsCreation();
+ void postPrincipalsCreation();
+
+ void preScheduleConsistencyCheck();
+ void postScheduleConsistencyCheck();
+
+ void preallocate(edm::service::SystemBounds const&);
+
+ void preEventSetupConfigurationFinalized();
+ void postEventSetupConfigurationFinalized();
+
+ void eventSetupConfiguration(edm::eventsetup::ESRecordsToProductResolverIndices const&, edm::ProcessContext const&);
+
+ void preModulesInitializationFinalized();
+ void postModulesInitializationFinalized();
+
+ DECLARE_SIGNAL_WATCHER_PROCESS_CONTEXT(BeginJob)
+
+ DECLARE_SIGNAL_WATCHER_NOARGS(EndJob)
+
+ void lookupInitializationComplete(edm::PathsAndConsumesOfModulesBase const&, edm::ProcessContext const&);
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(BeginStream)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(EndStream)
+
+ void jobFailure();
+
+ /******** Source transition signals *********************************************/
+
+ void preSourceNextTransition();
+ void postSourceNextTransition();
+
+ /******** Source stream context signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_ID(SourceEvent)
+
+ /******** Source lumi context signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_LUMIBLOCK_INDEX(SourceLumi)
+
+ /******** Source run context signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_RUN_INDEX(SourceRun)
+
+ /******** Source process block signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_SOURCE_PROCESS_BLOCK(SourceProcessBlock)
+
+ /******** File context signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STRING(OpenFile)
+ DECLARE_SIGNAL_WATCHER_STRING(CloseFile)
+
+ /******** Output file signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_NOARGS(OpenOutputFiles)
+ DECLARE_SIGNAL_WATCHER_NOARGS(CloseOutputFiles)
+
+ /******** Module stream context signals *********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleBeginStream)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEndStream)
+
+ /******** Process block signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(BeginProcessBlock)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(AccessInputProcessBlock)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(EndProcessBlock)
+
+ /******** Job-level single signals *********************************************/
+
+ void beginProcessing();
+ void endProcessing();
+
+ /******* Global context signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalBeginRun)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalEndRun)
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(WriteProcessBlock)
+
+ /******** Global write signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalWriteRun)
+
+ /******* Stream context signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(StreamBeginRun)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(StreamEndRun)
+
+ /******** Global context lumi signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalBeginLumi)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalEndLumi)
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT(GlobalWriteLumi)
+
+ /******** Stream context lumi signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(StreamBeginLumi)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(StreamEndLumi)
+
+ /******** Stream context events signal **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(Event)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT(ClearEvent)
+
+ /******** Path context event signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT(PathEvent)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT_HLT_STATUS(PathEvent)
+
+ /******** Early termination signals (Pre only, no Post) *****************************/
+
+ DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_STREAM(StreamEarlyTermination)
+ DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_GLOBAL(GlobalEarlyTermination)
+ DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_SOURCE(SourceEarlyTermination)
+
+ /******** ES module construction signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_COMPONENT_DESCRIPTION(ESModuleConstruction)
+
+ /******** ES IOV sync signals **********************************************/
+
+ void esSyncIOVQueuing(edm::IOVSyncValue const&);
+
+ DECLARE_SIGNAL_WATCHER_IOV_SYNC_VALUE(ESSyncIOV)
+
+ // Prefetching is optionally watched
+ // (see constructor)
+ DECLARE_SIGNAL_WATCHER_EVENT_SETUP_RECORD_KEY_ES_MODULE_CALLING_CONTEXT(ESModulePrefetching)
+ DECLARE_SIGNAL_WATCHER_EVENT_SETUP_RECORD_KEY_ES_MODULE_CALLING_CONTEXT(ESModule)
+ DECLARE_SIGNAL_WATCHER_EVENT_SETUP_RECORD_KEY_ES_MODULE_CALLING_CONTEXT(ESModuleAcquire)
+
+ /******** Module no-context signals *********************************************/
+
+ DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(ModuleConstruction)
+ DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(ModuleDestruction)
+ DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(ModuleBeginJob)
+ DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(ModuleEndJob)
+
+ /******** Module context signals *********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEventPrefetching)
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEvent)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEventAcquire)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleTransformPrefetching)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleTransform)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleTransformAcquiring)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEventDelayedGet)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(EventReadFromSource)
+
+ /******** Module stream prefetching signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleStreamPrefetching)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleStreamBeginRun)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleStreamEndRun)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleStreamBeginLumi)
+ DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT(ModuleStreamEndLumi)
+
+ /******** Module global/process block context signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleBeginProcessBlock)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleAccessInputProcessBlock)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleEndProcessBlock)
+
+ /******** Module global prefetching and process block signals **********************************************/
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleGlobalPrefetching)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleGlobalBeginRun)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleGlobalEndRun)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleGlobalBeginLumi)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleGlobalEndLumi)
+
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleWriteProcessBlock)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleWriteRun)
+ DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT(ModuleWriteLumi)
+
+ /******** Source module context signals *************************************/
+
+ DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION(SourceConstruction)
+#undef DECLARE_SIGNAL_WATCHER_NOARGS
+#undef DECLARE_SIGNAL_WATCHER_PROCESS_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_STREAM_ID
+#undef DECLARE_SIGNAL_WATCHER_LUMIBLOCK_INDEX
+#undef DECLARE_SIGNAL_WATCHER_RUN_INDEX
+#undef DECLARE_SIGNAL_WATCHER_STRING
+#undef DECLARE_SIGNAL_WATCHER_MODULE_DESCRIPTION
+#undef DECLARE_SIGNAL_WATCHER_COMPONENT_DESCRIPTION
+#undef DECLARE_SIGNAL_WATCHER_IOV_SYNC_VALUE
+#undef DECLARE_SIGNAL_WATCHER_EVENT_SETUP_RECORD_KEY_ES_MODULE_CALLING_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_PATH_CONTEXT_HLT_STATUS
+#undef DECLARE_SIGNAL_WATCHER_STREAM_CONTEXT_MODULE_CALLING_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_GLOBAL_CONTEXT_MODULE_CALLING_CONTEXT
+#undef DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_STREAM
+#undef DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_GLOBAL
+#undef DECLARE_SIGNAL_WATCHER_TERMINATION_ORIGIN_SOURCE
+
+private:
+ using SharedRangePool = ProfilerServiceBase::RangePool;
+ using GlobalInFlightRanges = ProfilerServiceBase::InFlightRanges;
+ using GlobalESInFlightRanges = ProfilerServiceBase::
+ InFlightRanges;
+ using StreamModuleInFlightRanges =
+ ProfilerServiceBase::InFlightRanges;
+ using TransformInFlightRanges =
+ ProfilerServiceBase::InFlightRanges;
+ using IndexInFlightRanges = ProfilerServiceBase::InFlightRanges;
+ using TwoIndexInFlightRanges =
+ ProfilerServiceBase::InFlightRanges;
+ using ThreeIndexInFlightRanges =
+ ProfilerServiceBase::InFlightRanges;
+ using PathInFlightRanges =
+ ProfilerServiceBase::InFlightRanges;
+
+ bool highlight(std::string const& label) const {
+ return (std::binary_search(highlightModules_.begin(), highlightModules_.end(), label));
+ }
+
+ // Highlight exception: if `label` names a highlighted module, remap `color` to the Amber family
+ // while preserving its shade (Dark2..Light2); otherwise return `color` unchanged.
+ Color highlightColor(Color color, std::string const& label) const {
+ return highlight(label) ? to_highlighted(color) : color;
+ }
+
+ std::vector highlightModules_;
+ const bool showModulePrefetching_;
+ const bool skipFirstEvent_;
+ const bool showDetailedInfo_;
+
+ std::atomic globalFirstEventDone_ = false;
+ std::vector> streamFirstEventDone_;
+ SharedRangePool range_pool_;
+ GlobalInFlightRanges global_in_flight_ranges_;
+ GlobalESInFlightRanges global_es_in_flight_ranges_;
+ StreamModuleInFlightRanges stream_modules_in_flight_ranges_;
+ StreamModuleInFlightRanges stream_modules_event_in_flight_ranges_;
+ StreamModuleInFlightRanges stream_modules_event_acquire_in_flight_ranges_;
+ TransformInFlightRanges transform_in_flight_ranges_;
+ IndexInFlightRanges event_in_flight_ranges_; // per-stream event ranges, keyed by stream id
+ IndexInFlightRanges source_in_flight_ranges_; // per-stream source ranges, keyed by stream id
+ PathInFlightRanges path_in_flight_ranges_; // per-stream, per-path ranges, keyed by (sid, pid, isEndPath)
+ IndexInFlightRanges global_modules_in_flight_ranges_; // global per-module ranges, keyed by module id
+ IndexInFlightRanges global_ES_modules_in_flight_ranges_; // global per-ES-module ranges, keyed by component id
+ IndexInFlightRanges global_run_in_flight_ranges_; // global per-run ranges, keyed by run number
+ TwoIndexInFlightRanges global_lumi_in_flight_ranges_; // global per-lumi ranges, keyed by (run, lumi)
+ TwoIndexInFlightRanges stream_run_in_flight_ranges_; // per-stream run ranges, keyed by (sid, run)
+ ThreeIndexInFlightRanges stream_lumi_in_flight_ranges_; // per-stream lumi ranges, keyed by (sid, run, lumi)
+
+ Domain global_domain_; // NVTX domain for global EDM transitions
+ std::vector stream_domain_; // NVTX domains for per-EDM-stream transitions
+};
+
+// This macro registers signal watchers pairs. Same for all.
+#define REGISTER_SIGNAL_WATCHER(signal) \
+ registry.watchPre##signal(this, &ProfilerService::pre##signal); \
+ registry.watchPost##signal(this, &ProfilerService::post##signal);
+
+template
+ProfilerService::ProfilerService(edm::ParameterSet const& config, edm::ActivityRegistry& registry)
+ : highlightModules_(config.getUntrackedParameter>("highlightModules")),
+ showModulePrefetching_(config.getUntrackedParameter("showModulePrefetching")),
+ skipFirstEvent_(config.getUntrackedParameter("skipFirstEvent")),
+ showDetailedInfo_(config.getUntrackedParameter("showDetailedInfo")),
+ range_pool_(),
+ global_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ global_es_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ stream_modules_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ stream_modules_event_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ stream_modules_event_acquire_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ transform_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ event_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ source_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ path_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ global_modules_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ global_ES_modules_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ global_run_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ global_lumi_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ stream_run_in_flight_ranges_(range_pool_, showDetailedInfo_),
+ stream_lumi_in_flight_ranges_(range_pool_, showDetailedInfo_) {
+ // make sure that CUDA is initialised, and that the CUDAInterface destructor is called after this service's destructor
+ typename Backend::EDMService service;
+ std::cout << Backend::shortName() << "ProfilerService: initializing..." << std::endl;
+ if (not service) {
+ std::cout << Backend::shortName() << "ProfilerService: EDM service not available, disabling profiling service"
+ << std::endl;
+ return;
+ }
+ if (not service or not service->enabled()) {
+ std::cout << Backend::shortName()
+ << "ProfilerService: EDM service failed to be enabled, disabling profiling service" << std::endl;
+ return;
+ }
+ std::cout << Backend::shortName()
+ << "ProfilerService: EDM service initialized successfully. Registering watchers to EDM." << std::endl;
+
+ std::sort(highlightModules_.begin(), highlightModules_.end());
+
+ // create the NVTX domain for global EDM transitions
+ global_domain_.create("EDM Global");
+
+ // enables profile collection; if profiling is already enabled it has no effect
+ // otherwise, make sure it is stopped.
+ if (not skipFirstEvent_) {
+ Backend::profilerStart();
+ } else {
+ Backend::profilerStop();
+ }
+
+ // Keep watcher registration order aligned with ActivityRegistry::watch* declarations.
+
+ registry.watchPostServicesConstruction(this, &ProfilerService::postServicesConstruction);
+
+ REGISTER_SIGNAL_WATCHER(EventSetupModulesConstruction)
+
+ REGISTER_SIGNAL_WATCHER(ModulesAndSourceConstruction)
+
+ REGISTER_SIGNAL_WATCHER(FinishSchedule)
+
+ REGISTER_SIGNAL_WATCHER(PrincipalsCreation)
+
+ REGISTER_SIGNAL_WATCHER(ScheduleConsistencyCheck)
+
+ registry.watchPreallocate(this, &ProfilerService::preallocate);
+
+ REGISTER_SIGNAL_WATCHER(EventSetupConfigurationFinalized)
+ registry.watchEventSetupConfiguration(this, &ProfilerService::eventSetupConfiguration);
+
+ REGISTER_SIGNAL_WATCHER(ModulesInitializationFinalized)
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(BeginJob)
+ REGISTER_SIGNAL_WATCHER(EndJob)
+
+ registry.watchLookupInitializationComplete(this, &ProfilerService::lookupInitializationComplete);
+
+ REGISTER_SIGNAL_WATCHER(BeginStream)
+ REGISTER_SIGNAL_WATCHER(EndStream)
+
+ REGISTER_SIGNAL_WATCHER(SourceNextTransition)
+
+ // these signal pair are guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(SourceEvent)
+ REGISTER_SIGNAL_WATCHER(SourceLumi)
+ REGISTER_SIGNAL_WATCHER(SourceRun)
+ REGISTER_SIGNAL_WATCHER(SourceProcessBlock)
+
+ REGISTER_SIGNAL_WATCHER(OpenFile)
+ REGISTER_SIGNAL_WATCHER(CloseFile)
+ REGISTER_SIGNAL_WATCHER(OpenOutputFiles)
+ REGISTER_SIGNAL_WATCHER(CloseOutputFiles)
+ /******** Module stream context signals *********************************************/
+ REGISTER_SIGNAL_WATCHER(ModuleBeginStream)
+ REGISTER_SIGNAL_WATCHER(ModuleEndStream)
+
+ // Process block signal pairs
+ REGISTER_SIGNAL_WATCHER(BeginProcessBlock)
+ REGISTER_SIGNAL_WATCHER(AccessInputProcessBlock)
+ REGISTER_SIGNAL_WATCHER(EndProcessBlock)
+
+ // Job-level single signals
+ registry.watchBeginProcessing(this, &ProfilerService::beginProcessing);
+ registry.watchEndProcessing(this, &ProfilerService::endProcessing);
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(GlobalBeginRun)
+ REGISTER_SIGNAL_WATCHER(GlobalEndRun)
+
+ REGISTER_SIGNAL_WATCHER(WriteProcessBlock)
+
+ // Global write signal pairs
+ REGISTER_SIGNAL_WATCHER(GlobalWriteRun)
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(StreamBeginRun)
+ REGISTER_SIGNAL_WATCHER(StreamEndRun)
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(GlobalBeginLumi)
+ REGISTER_SIGNAL_WATCHER(GlobalEndLumi)
+
+ REGISTER_SIGNAL_WATCHER(GlobalWriteLumi)
+
+ REGISTER_SIGNAL_WATCHER(StreamBeginLumi)
+ REGISTER_SIGNAL_WATCHER(StreamEndLumi)
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(Event)
+
+ REGISTER_SIGNAL_WATCHER(ClearEvent)
+
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(PathEvent)
+
+ // Early termination signals (Pre only)
+ registry.watchPreStreamEarlyTermination(this, &ProfilerService::preStreamEarlyTermination);
+ registry.watchPreGlobalEarlyTermination(this, &ProfilerService::preGlobalEarlyTermination);
+ registry.watchPreSourceEarlyTermination(this, &ProfilerService::preSourceEarlyTermination);
+
+ // these signal pair are guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(ESModuleConstruction)
+
+ // ES IOV sync signals
+ registry.watchESSyncIOVQueuing(this, &ProfilerService::esSyncIOVQueuing);
+ REGISTER_SIGNAL_WATCHER(ESSyncIOV)
+
+ if (showModulePrefetching_) {
+ REGISTER_SIGNAL_WATCHER(ESModulePrefetching)
+ }
+ REGISTER_SIGNAL_WATCHER(ESModule)
+ REGISTER_SIGNAL_WATCHER(ESModuleAcquire)
+
+ REGISTER_SIGNAL_WATCHER(ModuleConstruction)
+ REGISTER_SIGNAL_WATCHER(ModuleDestruction)
+
+ REGISTER_SIGNAL_WATCHER(ModuleBeginJob)
+ REGISTER_SIGNAL_WATCHER(ModuleEndJob)
+
+ if (showModulePrefetching_) {
+ // these signal pair are NOT guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(ModuleEventPrefetching)
+ }
+ REGISTER_SIGNAL_WATCHER(ModuleEvent)
+ REGISTER_SIGNAL_WATCHER(ModuleEventAcquire)
+ if (showModulePrefetching_) {
+ REGISTER_SIGNAL_WATCHER(ModuleTransformPrefetching)
+ }
+ REGISTER_SIGNAL_WATCHER(ModuleTransform)
+ REGISTER_SIGNAL_WATCHER(ModuleTransformAcquiring)
+ REGISTER_SIGNAL_WATCHER(ModuleEventDelayedGet)
+ REGISTER_SIGNAL_WATCHER(EventReadFromSource)
+
+ // Module stream prefetching signal pair
+ if (showModulePrefetching_) {
+ REGISTER_SIGNAL_WATCHER(ModuleStreamPrefetching)
+ }
+ REGISTER_SIGNAL_WATCHER(ModuleStreamBeginRun)
+ REGISTER_SIGNAL_WATCHER(ModuleStreamEndRun)
+ REGISTER_SIGNAL_WATCHER(ModuleStreamBeginLumi)
+ REGISTER_SIGNAL_WATCHER(ModuleStreamEndLumi)
+
+ REGISTER_SIGNAL_WATCHER(ModuleBeginProcessBlock)
+ REGISTER_SIGNAL_WATCHER(ModuleAccessInputProcessBlock)
+ REGISTER_SIGNAL_WATCHER(ModuleEndProcessBlock)
+
+ // Module global prefetching and process block signal pairs
+ if (showModulePrefetching_) {
+ REGISTER_SIGNAL_WATCHER(ModuleGlobalPrefetching)
+ }
+
+ // these signal pair are guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(ModuleGlobalBeginRun)
+ REGISTER_SIGNAL_WATCHER(ModuleGlobalEndRun)
+ REGISTER_SIGNAL_WATCHER(ModuleGlobalBeginLumi)
+ REGISTER_SIGNAL_WATCHER(ModuleGlobalEndLumi)
+
+ REGISTER_SIGNAL_WATCHER(ModuleWriteProcessBlock)
+ REGISTER_SIGNAL_WATCHER(ModuleWriteRun)
+ REGISTER_SIGNAL_WATCHER(ModuleWriteLumi)
+
+ // these signal pair are guaranteed to be called by the same thread
+ REGISTER_SIGNAL_WATCHER(SourceConstruction)
+}
+
+#undef REGISTER_SIGNAL_WATCHER
+
+template
+ProfilerService::~ProfilerService() {
+ for (unsigned int sid = 0; sid < stream_domain_.size(); ++sid) {
+ stream_domain_[sid].destroy();
+ }
+ global_domain_.destroy();
+ Backend::profilerStop();
+}
+
+template
+void ProfilerService::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
+ edm::ParameterSetDescription desc;
+ desc.addUntracked>("highlightModules", {})->setComment("");
+ desc.addUntracked("showModulePrefetching", false)
+ ->setComment("Show the stack of dependencies that requested to run a module.");
+ desc.addUntracked("skipFirstEvent", false)
+ ->setComment(
+ "Start profiling after the first event has completed.\nWith multiple streams, ignore transitions belonging "
+ "to events started in parallel to the first event.\nRequires running nvprof with the '--profile-from-start "
+ "off' option.");
+ desc.addUntracked("showDetailedInfo", false)
+ ->setComment(
+ "Show values of the module, path, and event transitions parameters.\n"
+ "When enabled, show many details in the profiler timeline.\n"
+ "When disabled, enables per-module, etc... statistics.");
+ descriptions.add(Backend::shortName() + "ProfilerService", desc);
+ descriptions.setComment(Backend::serviceComment());
+ // For reference, here is a possible extended comment for nvprof/nvvm backends:
+ // descriptions.setComment(R"(This Service provides CMSSW-aware annotations to nvprof/nvvm.
+
+ // Notes on nvprof options:
+ // - the option '--profile-from-start off' should be used if skipFirstEvent is True.
+ // - the option '--cpu-profiling on' currently results in cmsRun being stuck at the beginning of the job.
+ // - the option '--cpu-thread-tracing on' is not compatible with jemalloc, and should only be used with cmsRunGlibC.)");
+}
+
+/******** Signal-watcher implementation macros (expanded in the sections below) ********/
+
+// ES module signal ranges are keyed dynamically to avoid collisions from overlapping calls.
+#define DEFINE_ES_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::eventsetup::EventSetupRecordKey const& iKey, \
+ edm::ESModuleCallingContext const& esmcc) { \
+ auto mid = esmcc.componentDescription()->id_; \
+ auto const& label = esmcc.componentDescription()->label_; \
+ auto const& type = esmcc.componentDescription()->type_; \
+ auto const& state = esmcc.state(); \
+ auto const callId = esmcc.callID(); \
+ std::string detail = label.empty() ? (type + "(type)") : (label + " type=" + type); \
+ global_es_in_flight_ranges_.start(global_domain_, \
+ color, \
+ __func__, \
+ std::string(#signal) + " " + detail, \
+ "", \
+ "mid record state callId", \
+ mid, \
+ iKey.name(), \
+ state, \
+ callId); \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::eventsetup::EventSetupRecordKey const& iKey, \
+ edm::ESModuleCallingContext const& esmcc) { \
+ auto mid = esmcc.componentDescription()->id_; \
+ auto const& state = esmcc.state(); \
+ auto const callId = esmcc.callID(); \
+ global_es_in_flight_ranges_.end( \
+ global_domain_, __func__, #signal, "mid record state callId", mid, iKey.name(), state, callId); \
+ }
+
+// Macro for per-stream module (StreamContext, ModuleCallingContext) signal pairs, keyed by (sid, mid).
+#define DEFINE_MODULE_STREAM_SIGNAL_WATCHER(signal, inFlightRanges, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ auto const& label = mcc.moduleDescription()->moduleLabel(); \
+ std::string detail = label + " type=" + mcc.moduleDescription()->moduleName(); \
+ inFlightRanges.start(stream_domain_[sid], \
+ highlightColor(color, label), \
+ __func__, \
+ std::string(#signal) + " " + detail, \
+ "", \
+ "sid mid", \
+ sid, \
+ mid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ inFlightRanges.end(stream_domain_[sid], __func__, #signal, "sid mid", sid, mid); \
+ } \
+ }
+
+// Macro for module transform (StreamContext, ModuleCallingContext) signal pairs, keyed by (sid, mid, callId).
+#define DEFINE_MODULE_TRANSFORM_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ auto const& label = mcc.moduleDescription()->moduleLabel(); \
+ auto const callId = mcc.callID(); \
+ std::string detail = label + " type=" + mcc.moduleDescription()->moduleName(); \
+ transform_in_flight_ranges_.start(stream_domain_[sid], \
+ highlightColor(color, label), \
+ __func__, \
+ std::string(#signal) + " " + detail, \
+ "", \
+ "sid mid callId", \
+ sid, \
+ mid, \
+ callId); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamContext const& sc, edm::ModuleCallingContext const& mcc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ auto const callId = mcc.callID(); \
+ transform_in_flight_ranges_.end(stream_domain_[sid], __func__, #signal, "sid mid callId", sid, mid, callId); \
+ } \
+ }
+
+// Macro for global-module (GlobalContext, ModuleCallingContext) signal pairs, keyed by module id.
+#define DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ auto const& label = mcc.moduleDescription()->moduleLabel(); \
+ std::string detail = label + " type=" + mcc.moduleDescription()->moduleName(); \
+ global_modules_in_flight_ranges_.start( \
+ global_domain_, highlightColor(color, label), __func__, std::string(#signal) + " " + detail, "", "mid", mid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::GlobalContext const& gc, edm::ModuleCallingContext const& mcc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto mid = mcc.moduleDescription()->id(); \
+ global_modules_in_flight_ranges_.end(global_domain_, __func__, #signal, "mid", mid); \
+ } \
+ }
+
+// Macro for module (ModuleDescription) signal pairs, keyed by module id. `guard` selects when to record.
+#define DEFINE_MODULE_DESC_SIGNAL_WATCHER(signal, color, guard) \
+ template \
+ void ProfilerService::pre##signal(edm::ModuleDescription const& desc) { \
+ if (guard) { \
+ auto mid = desc.id(); \
+ auto const& label = desc.moduleLabel(); \
+ std::string detail = label + " type=" + desc.moduleName(); \
+ global_modules_in_flight_ranges_.start( \
+ global_domain_, highlightColor(color, label), __func__, std::string(#signal) + " " + detail, "", "mid", mid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::ModuleDescription const& desc) { \
+ if (guard) { \
+ auto mid = desc.id(); \
+ global_modules_in_flight_ranges_.end(global_domain_, __func__, #signal, "mid", mid); \
+ } \
+ }
+
+// Macro for ES module construction (ComponentDescription) signal pairs, keyed by component id.
+#define DEFINE_ES_CONSTRUCTION_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::eventsetup::ComponentDescription const& desc) { \
+ if (not skipFirstEvent_) { \
+ auto mid = desc.id_; \
+ auto const& label = desc.label_; \
+ auto const& type = desc.type_; \
+ std::string detail = label.empty() ? (type + "(type)") : (label + " type=" + type); \
+ global_ES_modules_in_flight_ranges_.start( \
+ global_domain_, color, __func__, std::string(#signal) + " " + detail, "", "mid", mid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::eventsetup::ComponentDescription const& desc) { \
+ if (not skipFirstEvent_) { \
+ auto mid = desc.id_; \
+ global_ES_modules_in_flight_ranges_.end(global_domain_, __func__, #signal, "mid", mid); \
+ } \
+ }
+
+// Macro for per-stream (StreamContext) signal pairs, keyed by stream id via event_in_flight_ranges_.
+#define DEFINE_STREAM_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ event_in_flight_ranges_.start(stream_domain_[sid], color, __func__, #signal, "", "sid", sid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ event_in_flight_ranges_.end(stream_domain_[sid], __func__, #signal, "sid", sid); \
+ } \
+ }
+
+// Macro for per-stream id (StreamID) signal pairs, keyed by stream id via source_in_flight_ranges_.
+#define DEFINE_STREAM_ID_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamID sid) { \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ source_in_flight_ranges_.start(stream_domain_[sid], color, __func__, #signal, "", "sid", sid); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamID sid) { \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ source_in_flight_ranges_.end(stream_domain_[sid], __func__, #signal, "sid", sid); \
+ } \
+ }
+
+// Macro for per-stream run (StreamContext) signal pairs, keyed by (sid, run).
+#define DEFINE_STREAM_RUN_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto run = sc.eventID().run(); \
+ std::string detail = "runSlot=" + std::to_string(sc.runIndex().value()); \
+ stream_run_in_flight_ranges_.start(stream_domain_[sid], color, __func__, #signal, detail, "sid run", sid, run); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto run = sc.eventID().run(); \
+ stream_run_in_flight_ranges_.end(stream_domain_[sid], __func__, #signal, "sid run", sid, run); \
+ } \
+ }
+
+// Macro for per-stream lumi (StreamContext) signal pairs, keyed by (sid, run, lumi).
+#define DEFINE_STREAM_LUMI_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto run = sc.eventID().run(); \
+ auto lumi = sc.eventID().luminosityBlock(); \
+ std::string detail = "runSlot=" + std::to_string(sc.runIndex().value()) + \
+ " lumiSlot=" + std::to_string(sc.luminosityBlockIndex().value()); \
+ stream_lumi_in_flight_ranges_.start( \
+ stream_domain_[sid], color, __func__, #signal, detail, "sid run lumi", sid, run, lumi); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::StreamContext const& sc) { \
+ auto sid = sc.streamID(); \
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) { \
+ auto run = sc.eventID().run(); \
+ auto lumi = sc.eventID().luminosityBlock(); \
+ stream_lumi_in_flight_ranges_.end(stream_domain_[sid], __func__, #signal, "sid run lumi", sid, run, lumi); \
+ } \
+ }
+
+// Macro for global (GlobalContext) signal pairs, keyed by the signal name via global_in_flight_ranges_.
+#define DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::GlobalContext const&) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ global_in_flight_ranges_.start(global_domain_, color, __func__, #signal, "", "signal", #signal); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::GlobalContext const&) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ global_in_flight_ranges_.end(global_domain_, __func__, #signal, "signal", #signal); \
+ } \
+ }
+
+// Macro for global run (GlobalContext) signal pairs, keyed by run number.
+#define DEFINE_GLOBAL_RUN_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::GlobalContext const& gc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto run = gc.luminosityBlockID().run(); \
+ std::string detail = "runSlot=" + std::to_string(gc.runIndex().value()); \
+ global_run_in_flight_ranges_.start(global_domain_, color, __func__, #signal, detail, "run", run); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::GlobalContext const& gc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto run = gc.luminosityBlockID().run(); \
+ global_run_in_flight_ranges_.end(global_domain_, __func__, #signal, "run", run); \
+ } \
+ }
+
+// Macro for global lumi (GlobalContext) signal pairs, keyed by (run, lumi).
+#define DEFINE_GLOBAL_LUMI_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(edm::GlobalContext const& gc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto run = gc.luminosityBlockID().run(); \
+ auto lumi = gc.luminosityBlockID().luminosityBlock(); \
+ std::string detail = "runSlot=" + std::to_string(gc.runIndex().value()) + \
+ " lumiSlot=" + std::to_string(gc.luminosityBlockIndex().value()); \
+ global_lumi_in_flight_ranges_.start(global_domain_, color, __func__, #signal, detail, "run lumi", run, lumi); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(edm::GlobalContext const& gc) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ auto run = gc.luminosityBlockID().run(); \
+ auto lumi = gc.luminosityBlockID().luminosityBlock(); \
+ global_lumi_in_flight_ranges_.end(global_domain_, __func__, #signal, "run lumi", run, lumi); \
+ } \
+ }
+
+// Macro for global no-argument signal pairs, keyed by the signal name. `guard` selects when to record.
+#define DEFINE_GLOBAL_SIGNAL_WATCHER(signal, color, guard) \
+ template \
+ void ProfilerService::pre##signal() { \
+ if (guard) { \
+ global_in_flight_ranges_.start(global_domain_, color, __func__, #signal, "", "signal", #signal); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal() { \
+ if (guard) { \
+ global_in_flight_ranges_.end(global_domain_, __func__, #signal, "signal", #signal); \
+ } \
+ }
+
+// Macro for global signal pairs taking (and ignoring) a single argument. `guard` selects when to record.
+#define DEFINE_GLOBAL_ARG_SIGNAL_WATCHER(signal, color, guard, argType) \
+ template \
+ void ProfilerService::pre##signal(argType) { \
+ if (guard) { \
+ global_in_flight_ranges_.start(global_domain_, color, __func__, #signal, "", "signal", #signal); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(argType) { \
+ if (guard) { \
+ global_in_flight_ranges_.end(global_domain_, __func__, #signal, "signal", #signal); \
+ } \
+ }
+
+// Macro for global signal pairs taking a file name (std::string), used as the range detail.
+#define DEFINE_GLOBAL_STRING_SIGNAL_WATCHER(signal, color) \
+ template \
+ void ProfilerService::pre##signal(std::string const& lfn) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ global_in_flight_ranges_.start(global_domain_, color, __func__, #signal, lfn, "signal", #signal); \
+ } \
+ } \
+ template \
+ void ProfilerService::post##signal(std::string const&) { \
+ if (not skipFirstEvent_ or globalFirstEventDone_) { \
+ global_in_flight_ranges_.end(global_domain_, __func__, #signal, "signal", #signal); \
+ } \
+ }
+
+// Macro for a single no-argument global mark (instantaneous annotation, not a range).
+#define DEFINE_GLOBAL_MARK(name, color) \
+ template \
+ void ProfilerService::name() { \
+ Backend::mark(global_domain_, #name, color); \
+ }
+
+template
+void ProfilerService::preallocate(edm::service::SystemBounds const& bounds) {
+ std::stringstream out;
+ out << "preallocate: " << bounds.maxNumberOfConcurrentRuns() << " concurrent runs, "
+ << bounds.maxNumberOfConcurrentLuminosityBlocks() << " luminosity sections, " << bounds.maxNumberOfStreams()
+ << " streams\nrunning on " << bounds.maxNumberOfThreads() << " threads";
+ Backend::mark(global_domain_, out.str().c_str(), Color::Grey);
+
+ auto concurrentStreams = bounds.maxNumberOfStreams();
+ // create the NVTX domains for per-EDM-stream transitions
+ stream_domain_.resize(concurrentStreams);
+ for (unsigned int sid = 0; sid < concurrentStreams; ++sid) {
+ stream_domain_[sid].create(fmt::sprintf("EDM Stream %d", sid));
+ }
+
+ if (skipFirstEvent_) {
+ globalFirstEventDone_ = false;
+ std::vector> tmp(concurrentStreams);
+ for (auto& element : tmp)
+ std::atomic_init(&element, false);
+ streamFirstEventDone_ = std::move(tmp);
+ }
+}
+
+DEFINE_GLOBAL_MARK(postServicesConstruction, Color::Grey)
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(EventSetupConfigurationFinalized, Color::Blue, not skipFirstEvent_)
+
+template
+void ProfilerService::eventSetupConfiguration(edm::eventsetup::ESRecordsToProductResolverIndices const&,
+ edm::ProcessContext const&) {
+ if (not skipFirstEvent_) {
+ Backend::mark(global_domain_, "eventSetupConfiguration", Color::Blue);
+ }
+}
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(EventSetupModulesConstruction, Color::Blue_Light2, not skipFirstEvent_)
+DEFINE_GLOBAL_SIGNAL_WATCHER(ModulesAndSourceConstruction, Color::Green_Light2, not skipFirstEvent_)
+
+/******** Job begin/end signal implementations *************************************/
+
+template
+void ProfilerService::preBeginJob(edm::ProcessContext const&) {
+ global_in_flight_ranges_.start(global_domain_, Color::Grey, __func__, "BeginJob", "", "signal", "BeginJob");
+}
+
+template
+void ProfilerService::postBeginJob() {
+ global_in_flight_ranges_.end(global_domain_, __func__, "BeginJob", "signal", "BeginJob");
+}
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(EndJob, Color::Grey, true)
+
+template
+void ProfilerService::lookupInitializationComplete(edm::PathsAndConsumesOfModulesBase const&,
+ edm::ProcessContext const&) {
+ Backend::mark(global_domain_, "lookupInitializationComplete", Color::Grey);
+}
+
+/******** Stream begin/end signal implementations *************************************/
+
+DEFINE_STREAM_SIGNAL_WATCHER(BeginStream, Color::Grey)
+DEFINE_STREAM_SIGNAL_WATCHER(EndStream, Color::Grey)
+
+/******** Source transition signal implementations *************************************/
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(SourceNextTransition, Color::Yellow, true)
+
+DEFINE_STREAM_ID_SIGNAL_WATCHER(SourceEvent, Color::Yellow)
+
+DEFINE_GLOBAL_ARG_SIGNAL_WATCHER(SourceLumi,
+ Color::Yellow,
+ not skipFirstEvent_ or globalFirstEventDone_,
+ edm::LuminosityBlockIndex)
+
+DEFINE_GLOBAL_ARG_SIGNAL_WATCHER(SourceRun, Color::Yellow, not skipFirstEvent_ or globalFirstEventDone_, edm::RunIndex)
+
+/******** Source process block signal implementations *************************************/
+
+template
+void ProfilerService::preSourceProcessBlock() {
+ if (not skipFirstEvent_ or globalFirstEventDone_) {
+ global_in_flight_ranges_.start(
+ global_domain_, Color::Yellow, __func__, "SourceProcessBlock", "", "signal", "SourceProcessBlock");
+ }
+}
+
+template
+void ProfilerService::postSourceProcessBlock(std::string const&) {
+ if (not skipFirstEvent_ or globalFirstEventDone_) {
+ global_in_flight_ranges_.end(global_domain_, __func__, "SourceProcessBlock", "signal", "SourceProcessBlock");
+ }
+}
+
+/******** File signal implementations *************************************/
+
+DEFINE_GLOBAL_STRING_SIGNAL_WATCHER(OpenFile, Color::Amber)
+
+DEFINE_GLOBAL_STRING_SIGNAL_WATCHER(CloseFile, Color::Amber)
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(OpenOutputFiles, Color::Amber, not skipFirstEvent_ or globalFirstEventDone_)
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(CloseOutputFiles, Color::Amber, not skipFirstEvent_ or globalFirstEventDone_)
+
+/******** Global run/lumi and process block signal implementations *************************************/
+
+DEFINE_GLOBAL_RUN_SIGNAL_WATCHER(GlobalBeginRun, Color::Grey)
+DEFINE_GLOBAL_RUN_SIGNAL_WATCHER(GlobalEndRun, Color::Grey)
+DEFINE_GLOBAL_LUMI_SIGNAL_WATCHER(GlobalBeginLumi, Color::Grey)
+DEFINE_GLOBAL_LUMI_SIGNAL_WATCHER(GlobalEndLumi, Color::Grey)
+DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER(BeginProcessBlock, Color::Grey)
+DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER(EndProcessBlock, Color::Grey)
+DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER(AccessInputProcessBlock, Color::Grey)
+DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER(WriteProcessBlock, Color::Amber)
+DEFINE_GLOBAL_RUN_SIGNAL_WATCHER(GlobalWriteRun, Color::Amber)
+DEFINE_GLOBAL_LUMI_SIGNAL_WATCHER(GlobalWriteLumi, Color::Amber)
+
+DEFINE_STREAM_RUN_SIGNAL_WATCHER(StreamBeginRun, Color::Grey)
+DEFINE_STREAM_RUN_SIGNAL_WATCHER(StreamEndRun, Color::Grey)
+DEFINE_STREAM_LUMI_SIGNAL_WATCHER(StreamBeginLumi, Color::Grey)
+DEFINE_STREAM_LUMI_SIGNAL_WATCHER(StreamEndLumi, Color::Grey)
+
+DEFINE_GLOBAL_MARK(beginProcessing, Color::Grey)
+
+DEFINE_GLOBAL_MARK(endProcessing, Color::Grey)
+
+/******** Event signal implementations *************************************/
+
+template
+void ProfilerService::preEvent(edm::StreamContext const& sc) {
+ auto sid = sc.streamID();
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
+ std::string detail = fmt::sprintf(
+ "run=%d lumi=%d event=%d", sc.eventID().run(), sc.eventID().luminosityBlock(), sc.eventID().event());
+ event_in_flight_ranges_.start(stream_domain_[sid], Color::Yellow_Dark1, __func__, "Event", detail, "sid", sid);
+ }
+}
+
+template
+void ProfilerService::postEvent(edm::StreamContext const& sc) {
+ auto sid = sc.streamID();
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
+ event_in_flight_ranges_.end(stream_domain_[sid], __func__, "Event", "sid", sid);
+ } else {
+ streamFirstEventDone_[sid] = true;
+ auto identity = [](bool x) { return x; };
+ if (std::all_of(streamFirstEventDone_.begin(), streamFirstEventDone_.end(), identity)) {
+ bool expected = false;
+ if (globalFirstEventDone_.compare_exchange_strong(expected, true)) {
+ Backend::profilerStart();
+ Backend::mark(global_domain_, "profiling started", Color::White);
+ }
+ }
+ }
+}
+
+DEFINE_STREAM_SIGNAL_WATCHER(ClearEvent, Color::Yellow_Dark2)
+
+template
+void ProfilerService::prePathEvent(edm::StreamContext const& sc, edm::PathContext const& pc) {
+ auto sid = sc.streamID();
+ auto pid = pc.pathID();
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
+ path_in_flight_ranges_.start(stream_domain_[sid],
+ Color::Grey,
+ __func__,
+ "PathEvent",
+ pc.pathName(),
+ "sid pid isEndPath",
+ sid,
+ pid,
+ pc.isEndPath());
+ }
+}
+
+template
+void ProfilerService::postPathEvent(edm::StreamContext const& sc,
+ edm::PathContext const& pc,
+ edm::HLTPathStatus const&) {
+ auto sid = sc.streamID();
+ auto pid = pc.pathID();
+ if (not skipFirstEvent_ or streamFirstEventDone_[sid]) {
+ path_in_flight_ranges_.end(
+ stream_domain_[sid], __func__, "PathEvent", "sid pid isEndPath", sid, pid, pc.isEndPath());
+ }
+}
+
+/******** Module construction / job signal implementations *************************************/
+
+DEFINE_MODULE_DESC_SIGNAL_WATCHER(ModuleConstruction, Color::Green_Light2, not skipFirstEvent_)
+DEFINE_MODULE_DESC_SIGNAL_WATCHER(ModuleDestruction, Color::Green_Light2, not skipFirstEvent_)
+DEFINE_MODULE_DESC_SIGNAL_WATCHER(ModuleBeginJob, Color::Green_Dark1, not skipFirstEvent_)
+DEFINE_MODULE_DESC_SIGNAL_WATCHER(SourceConstruction, Color::Yellow_Light2, not skipFirstEvent_)
+
+DEFINE_MODULE_DESC_SIGNAL_WATCHER(ModuleEndJob, Color::Green_Dark1, not skipFirstEvent_ or globalFirstEventDone_)
+
+/******** Module stream context signal implementations *********************************************/
+
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleBeginStream, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleEndStream, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleEventPrefetching, stream_modules_in_flight_ranges_, Color::Green_Light1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleEvent, stream_modules_event_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleEventAcquire, stream_modules_event_acquire_in_flight_ranges_, Color::Green)
+DEFINE_MODULE_TRANSFORM_SIGNAL_WATCHER(ModuleTransformPrefetching, Color::Green)
+DEFINE_MODULE_TRANSFORM_SIGNAL_WATCHER(ModuleTransform, Color::Green_Dark2)
+DEFINE_MODULE_TRANSFORM_SIGNAL_WATCHER(ModuleTransformAcquiring, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleEventDelayedGet, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(EventReadFromSource, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleStreamPrefetching, stream_modules_in_flight_ranges_, Color::Green_Light1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleStreamBeginRun, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleStreamEndRun, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleStreamBeginLumi, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+DEFINE_MODULE_STREAM_SIGNAL_WATCHER(ModuleStreamEndLumi, stream_modules_in_flight_ranges_, Color::Green_Dark1)
+
+/******** Module global run/lumi and process block signal implementations *********************************/
+
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleGlobalBeginRun, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleGlobalEndRun, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleGlobalBeginLumi, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleGlobalEndLumi, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleBeginProcessBlock, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleAccessInputProcessBlock, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleEndProcessBlock, Color::Green_Dark1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleGlobalPrefetching, Color::Green_Light1)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleWriteProcessBlock, Color::Amber)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleWriteRun, Color::Amber)
+DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER(ModuleWriteLumi, Color::Amber)
+
+/******** ES module signal implementations *************************************/
+
+DEFINE_ES_CONSTRUCTION_SIGNAL_WATCHER(ESModuleConstruction, Color::Blue_Light2)
+
+template
+void ProfilerService::preESModulePrefetching(edm::eventsetup::EventSetupRecordKey const& iKey,
+ edm::ESModuleCallingContext const& esmcc) {
+ preESModuleAcquire(iKey, esmcc);
+}
+
+template
+void ProfilerService::postESModulePrefetching(edm::eventsetup::EventSetupRecordKey const& iKey,
+ edm::ESModuleCallingContext const& esmcc) {
+ postESModuleAcquire(iKey, esmcc);
+}
+
+DEFINE_ES_SIGNAL_WATCHER(ESModule, Color::Blue_Dark1)
+DEFINE_ES_SIGNAL_WATCHER(ESModuleAcquire, Color::Blue)
+
+/******** ES IOV sync signal implementations *************************************/
+
+template
+void ProfilerService::esSyncIOVQueuing(edm::IOVSyncValue const&) {
+ Backend::mark(global_domain_, "esSyncIOVQueuing", Color::Blue);
+}
+
+DEFINE_GLOBAL_ARG_SIGNAL_WATCHER(ESSyncIOV, Color::Blue, true, edm::IOVSyncValue const&)
+
+/******** Infrastructure/setup signal implementations *************************************/
+
+DEFINE_GLOBAL_SIGNAL_WATCHER(FinishSchedule, Color::Grey, not skipFirstEvent_)
+DEFINE_GLOBAL_SIGNAL_WATCHER(PrincipalsCreation, Color::Grey, not skipFirstEvent_)
+DEFINE_GLOBAL_SIGNAL_WATCHER(ScheduleConsistencyCheck, Color::Grey, not skipFirstEvent_)
+DEFINE_GLOBAL_SIGNAL_WATCHER(ModulesInitializationFinalized, Color::Grey, not skipFirstEvent_)
+
+/******** Early termination signal implementations *****************************/
+
+template
+void ProfilerService::preStreamEarlyTermination(edm::StreamContext const& sc, edm::TerminationOrigin) {
+ auto sid = sc.streamID();
+ Backend::mark(stream_domain_[sid], "early termination", Color::Red);
+}
+
+template
+void ProfilerService::preGlobalEarlyTermination(edm::GlobalContext const&, edm::TerminationOrigin) {
+ Backend::mark(global_domain_, "global early termination", Color::Red);
+}
+
+template
+void ProfilerService::preSourceEarlyTermination(edm::TerminationOrigin) {
+ Backend::mark(global_domain_, "source early termination", Color::Red);
+}
+
+#undef DEFINE_ES_SIGNAL_WATCHER
+#undef DEFINE_MODULE_STREAM_SIGNAL_WATCHER
+#undef DEFINE_MODULE_TRANSFORM_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_MODULE_SIGNAL_WATCHER
+#undef DEFINE_MODULE_DESC_SIGNAL_WATCHER
+#undef DEFINE_STREAM_SIGNAL_WATCHER
+#undef DEFINE_STREAM_RUN_SIGNAL_WATCHER
+#undef DEFINE_STREAM_LUMI_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_CONTEXT_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_RUN_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_LUMI_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_ARG_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_STRING_SIGNAL_WATCHER
+#undef DEFINE_ES_CONSTRUCTION_SIGNAL_WATCHER
+#undef DEFINE_STREAM_ID_SIGNAL_WATCHER
+#undef DEFINE_GLOBAL_MARK
+
+#endif // __FWCore_Services_ProfilerService_h__
diff --git a/PerfTools/ProfilerService/interface/ProfilerServiceBase.h b/PerfTools/ProfilerService/interface/ProfilerServiceBase.h
new file mode 100644
index 0000000000000..73eae68508c69
--- /dev/null
+++ b/PerfTools/ProfilerService/interface/ProfilerServiceBase.h
@@ -0,0 +1,260 @@
+#ifndef FWCore_Services_ProfilerServiceBase_h__
+#define FWCore_Services_ProfilerServiceBase_h__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "FWCore/ServiceRegistry/interface/ESModuleCallingContext.h"
+
+/// @brief Base class for profiling services.
+/// @note This class contains the undelying utility classes.
+class ProfilerServiceBase {
+public:
+ enum class Color : std::size_t {
+ // Black, no variants
+ Black = 0,
+ // Red family (dark to light)
+ Red_Dark2,
+ Red_Dark1,
+ Red,
+ Red_Light1,
+ Red_Light2,
+ // Green family (dark to light)
+ Green_Dark2,
+ Green_Dark1,
+ Green,
+ Green_Light1,
+ Green_Light2,
+ // Blue family (dark to light)
+ Blue_Dark2,
+ Blue_Dark1,
+ Blue,
+ Blue_Light1,
+ Blue_Light2,
+ // Amber family (dark to light)
+ Amber_Dark2,
+ Amber_Dark1,
+ Amber,
+ Amber_Light1,
+ Amber_Light2,
+ // White, no variants
+ White,
+ // Grey family (dark to light)
+ Grey_Dark2,
+ Grey_Dark1,
+ Grey,
+ Grey_Light1,
+ Grey_Light2,
+ // Yellow family (dark to light)
+ Yellow_Dark2,
+ Yellow_Dark1,
+ Yellow,
+ Yellow_Light1,
+ Yellow_Light2
+ };
+
+ static std::size_t to_underlying(Color c) noexcept { return static_cast(c); }
+
+ // Switch color to amber, but keep the same relative darkness/lightness level.
+ static Color to_highlighted(Color c) noexcept {
+ for (auto const start : {to_underlying(Color::Red_Dark2),
+ to_underlying(Color::Green_Dark2),
+ to_underlying(Color::Blue_Dark2),
+ to_underlying(Color::Amber_Dark2),
+ to_underlying(Color::Grey_Dark2),
+ to_underlying(Color::Yellow_Dark2)}) {
+ auto const v = to_underlying(c);
+ if (v >= start and v < start + 5)
+ return static_cast(to_underlying(Color::Amber_Dark2) + (v - start));
+ }
+ return Color::Amber; // singletons (Black / White)
+ }
+
+ using SpinLock = tbb::spin_mutex;
+ using RWSpinLock = tbb::spin_rw_mutex;
+
+ template
+ class RangePool {
+ public:
+ RangePool() : next_allocation_size_(kInitialAllocationSize) { allocateUnlocked_(kInitialAllocationSize); }
+
+ std::size_t acquireSlot() {
+ std::size_t slot = 0;
+ bool got_slot = free_slots_.try_pop(slot);
+ while (not got_slot) {
+ std::lock_guard guard(mutex_);
+ allocateUnlocked_(next_allocation_size_);
+ next_allocation_size_ *= 2;
+ got_slot = free_slots_.try_pop(slot);
+ }
+ return slot;
+ }
+
+ void releaseSlot(std::size_t slot) { free_slots_.push(slot); }
+
+ Range& at(std::size_t slot) { return ranges_[slot]; }
+
+ private:
+ static constexpr std::size_t kInitialAllocationSize = 16;
+
+ void allocateUnlocked_(std::size_t count) {
+ auto const begin = ranges_.size();
+ ranges_.grow_by(count);
+ for (std::size_t index = begin; index < begin + count; ++index) {
+ free_slots_.push(index);
+ }
+ }
+
+ SpinLock mutex_;
+ std::size_t next_allocation_size_;
+ tbb::concurrent_vector ranges_;
+ tbb::concurrent_queue free_slots_;
+ };
+
+ template
+ class InFlightRanges {
+ public:
+ using Key = std::tuple...>;
+
+ explicit InFlightRanges(RangePool& range_pool, bool show_detailed_info = true)
+ : range_pool_(range_pool), show_detailed_info_(show_detailed_info) {}
+
+ // The range message is built automatically as the signal name followed by every key parameter
+ // used for range indexing (see makeMessage_), so callers only pass the color, function, signal
+ // and key arguments.
+ void start(Domain& domain,
+ Color color,
+ char const* func,
+ std::string_view signal,
+ std::string_view detail,
+ std::string_view keyNames,
+ KeyArgs const&... keyArgs) {
+ auto const msg = makeMessage_(signal, detail, keyNames, keyArgs...);
+ auto const key = makeKey_(keyArgs...);
+ auto const slot = range_pool_.acquireSlot();
+ auto [found, inserted] = [&]() {
+ std::shared_lock guard(mutex_);
+ return in_flight_.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(slot));
+ }();
+ if (not inserted) {
+ range_pool_.releaseSlot(slot);
+ auto fullmsg = std::string("Warning: previous range not ended before starting a new one in ") + func +
+ " name=" + msg + " signal=" + std::string(signal);
+ Backend::mark(domain, fullmsg.c_str(), Color::Red);
+ std::cout << fullmsg << std::endl;
+ return;
+ }
+ range_pool_.at(slot).startColorIn(domain, msg.c_str(), color, func);
+ }
+
+ void end(Domain& domain,
+ char const* func,
+ std::string_view signal,
+ std::string_view keyNames,
+ KeyArgs const&... keyArgs) {
+ auto const key = makeKey_(keyArgs...);
+ auto extracted = [&]() {
+ std::lock_guard guard(mutex_);
+ return in_flight_.unsafe_extract(key);
+ }();
+ auto const msg = makeMessage_(signal, std::string_view{}, keyNames, keyArgs...);
+ if (not extracted) {
+ auto fullmsg = std::string("Warning: trying to end a range that is not started in ") + func + " name=" + msg +
+ " signal=" + std::string(signal);
+ Backend::mark(domain, fullmsg.c_str(), Color::Red);
+ std::cout << fullmsg << std::endl;
+ return;
+ }
+ auto const slot = extracted.mapped();
+ range_pool_.at(slot).endIn(domain, msg.c_str(), func);
+ range_pool_.releaseSlot(slot);
+ }
+
+ private:
+ static Key makeKey_(KeyArgs const&... keyArgs) { return Key{std::decay_t(keyArgs)...}; }
+
+ // Stringify a single key argument: strings verbatim, enums via their underlying value, and any
+ // other arithmetic type via std::to_string.
+ template
+ static std::string keyToString_(T const& value) {
+ using U = std::decay_t;
+ if constexpr (std::is_same_v) {
+ return value;
+ } else if constexpr (std::is_enum_v) {
+ return std::to_string(static_cast>(value));
+ } else {
+ return std::to_string(value);
+ }
+ }
+
+ // Render the ES module calling state as a readable word rather than its numeric value.
+ static std::string keyToString_(edm::ESModuleCallingContext::State state) {
+ return state == edm::ESModuleCallingContext::State::kRunning ? "running" : "prefetching";
+ }
+
+ // Build the range message as the signal name, an optional human-readable detail (module label,
+ // file name, path name, ...), and each indexing key parameter rendered as "=".
+ // `keyNames` is a space-separated list matching the key arguments in order; an empty name emits
+ // the bare value. A key that stringifies to the signal itself (e.g. the signal-string keyed
+ // global ranges) is not repeated.
+ std::string makeMessage_(std::string_view signal,
+ std::string_view detail,
+ std::string_view keyNames,
+ KeyArgs const&... keyArgs) {
+ std::string msg{signal};
+ if (not show_detailed_info_) {
+ return msg;
+ }
+ if (not detail.empty()) {
+ msg += ' ';
+ msg += detail;
+ }
+ std::string_view rest = keyNames;
+ auto nextName = [&]() -> std::string_view {
+ auto const sp = rest.find(' ');
+ auto const tok = rest.substr(0, sp);
+ rest = (sp == std::string_view::npos) ? std::string_view{} : rest.substr(sp + 1);
+ return tok;
+ };
+ auto append = [&](auto const& value) {
+ auto const name = nextName();
+ auto const s = keyToString_(value);
+ if (s != signal) {
+ msg += ' ';
+ if (not name.empty()) {
+ msg += name;
+ msg += '=';
+ }
+ msg += s;
+ }
+ };
+ (append(keyArgs), ...);
+ return msg;
+ }
+
+ RWSpinLock mutex_;
+ RangePool& range_pool_;
+ bool show_detailed_info_;
+ tbb::concurrent_unordered_map> in_flight_;
+ };
+};
+
+#endif // FWCore_Services_ProfilerServiceBase_h__