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__