diff --git a/roofit/roofitcore/src/RooAbsReal.cxx b/roofit/roofitcore/src/RooAbsReal.cxx index 979a079c9c715..bfc84e86fa76d 100644 --- a/roofit/roofitcore/src/RooAbsReal.cxx +++ b/roofit/roofitcore/src/RooAbsReal.cxx @@ -98,6 +98,8 @@ #include #include #include +#include +#include #include #include @@ -4186,12 +4188,18 @@ void RooAbsReal::doEval(RooFit::EvalContext & ctx) const std::vector& _servers; } restoreState{ourServers}; - // Advising to implement the batch interface makes only sense if the batch was not a scalar. - // Otherwise, there would be no speedup benefit. + // Otherwise, there would be no speedup benefit. Warn only once per class, because doEval() is + // called for every evaluation of the computation graph, e.g. in every minimizer iteration. if(output.size() > 1 && RooMsgService::instance().isActive(this, RooFit::FastEvaluations, RooFit::INFO)) { - coutI(FastEvaluations) << "The class " << ClassName() << " does not implement the faster batch evaluation interface." - << " Consider requesting or implementing it to benefit from a speed up." << std::endl; + static std::set warnedClasses; + static std::mutex warnedClassesMutex; + std::lock_guard guard{warnedClassesMutex}; + if (warnedClasses.insert(ClassName()).second) { + coutI(FastEvaluations) << "The class " << ClassName() + << " does not implement the faster batch evaluation interface." + << " Consider requesting or implementing it to benefit from a speed up." << std::endl; + } } diff --git a/roofit/roofitcore/src/RooFit/Evaluator.cxx b/roofit/roofitcore/src/RooFit/Evaluator.cxx index 06ced37a724a4..fc1e9fe4dabc8 100644 --- a/roofit/roofitcore/src/RooFit/Evaluator.cxx +++ b/roofit/roofitcore/src/RooFit/Evaluator.cxx @@ -44,7 +44,9 @@ RooAbsPdf::fitTo() is called and gets destroyed when the fitting ends. #include #include +#include #include +#include #include #include @@ -91,6 +93,28 @@ void logArchitectureInfo(bool useGPU) } } +/// Advise the user to implement CUDA support for a class that had to be +/// evaluated on the CPU even though the CUDA backend was requested. Just like +/// for the analogous message about the missing batch evaluation interface in +/// RooAbsReal::doEval(), the message is only printed once per class, because +/// the computation graph is evaluated many times, e.g. in every minimizer +/// iteration. +void logMissingCudaSupport(RooAbsArg const &arg) +{ + if (!RooMsgService::instance().isActive(&arg, RooFit::FastEvaluations, RooFit::INFO)) { + return; + } + static std::set warnedClasses; + static std::mutex warnedClassesMutex; + std::lock_guard guard{warnedClassesMutex}; + if (warnedClasses.insert(arg.ClassName()).second) { + oocoutI(&arg, FastEvaluations) << "The class " << arg.ClassName() + << " could not be evaluated on the GPU because it doesn't support it." + << " Consider requesting or implementing it to benefit from a speed up." + << std::endl; + } +} + } // namespace /// A struct used by the Evaluator to store information on the RooAbsArgs in @@ -357,12 +381,10 @@ void Evaluator::computeCPUNode(const RooAbsArg *node, NodeInfo &info) _evalContextCUDA.set(node, {buffer, nOut}); } } else { + // Advising to implement the CUDA evaluation makes only sense if the batch was not a scalar. + // Otherwise, there would be no speedup benefit. if (!info.hasLogged && _useGPU) { - RooAbsArg const &arg = *info.absArg; - oocoutI(&arg, FastEvaluations) << "The argument " << arg.ClassName() << "::" << arg.GetName() - << " could not be evaluated on the GPU because the class doesn't support it. " - "Consider requesting or implementing it to benefit from a speed up." - << std::endl; + logMissingCudaSupport(*info.absArg); info.hasLogged = true; } if (!info.buffer) { diff --git a/roofit/roofitcore/src/RooMsgService.cxx b/roofit/roofitcore/src/RooMsgService.cxx index be613858c892d..1ded1a5661a9c 100644 --- a/roofit/roofitcore/src/RooMsgService.cxx +++ b/roofit/roofitcore/src/RooMsgService.cxx @@ -27,8 +27,9 @@ RooMsgService allows to filter and redirect messages into streams according to message level, topic, (base) class of originating object, name of originating object and based on attribute labels attached to individual objects. The current default configuration creates streams for all messages at WARNING level -or higher (e.g. ERROR and FATAL) and for all INFO message on topics Generation,Plotting, -Integration and Minimization and redirects them to stdout. Users can create additional streams +or higher (e.g. ERROR and FATAL) and for INFO messages on most topics (among others +Generation, Plotting, Minimization, and FastEvaluations) and redirects them to stdout. +Users can create additional streams for logging of e.g. DEBUG messages on particular topics or objects and/or redirect streams to C++ streams or files. @@ -47,7 +48,6 @@ RooHelpers::HijackMessageStream allows to fully capture a message stream in a st RooFit messages can be evaluated or suppressed. **/ - #include "RooMsgService.h" #include @@ -123,7 +123,9 @@ void RooMsgService::reset() { // Old-style streams _streams.clear(); addStream(RooFit::PROGRESS, Topic(RooFit::HistFactory - 1));//All before HistFactory - addStream(RooFit::INFO,Topic(RooFit::Eval|RooFit::Plotting|RooFit::Fitting|RooFit::Minimization|RooFit::Caching|RooFit::ObjectHandling|RooFit::NumericIntegration|RooFit::InputArguments|RooFit::DataHandling)) ; + addStream(RooFit::INFO, Topic(RooFit::Eval | RooFit::Plotting | RooFit::Fitting | RooFit::Minimization | + RooFit::Caching | RooFit::ObjectHandling | RooFit::NumericIntegration | + RooFit::InputArguments | RooFit::DataHandling | RooFit::FastEvaluations)); addStream(RooFit::INFO, Topic(RooFit::HistFactory)); }