From 8ac0da4e00b75c2c99e5cf0eb7070b6fce946b88 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sat, 29 Aug 2026 14:37:31 +0000 Subject: [PATCH 1/2] [RF] Emit FastEvaluations messages only once per class The FastEvaluations topic hosts two messages that advise implementing a faster evaluation interface for a class: one from the generic RooAbsReal::doEval() implementation, which falls back to scalar evaluate() calls in a loop, and one from RooFit::Evaluator, which reports nodes that had to be evaluated on the CPU because their class doesn't support CUDA. Both are emitted while the computation graph is evaluated, e.g. in every minimizer iteration, so with the FastEvaluations topic active a single fit would print them hundreds of times. Deduplicate both messages on the class name, such that each class is reported exactly once. For the GPU message, this replaces the per-node "hasLogged" guard, which only suppressed repetitions for a given node of a given Evaluator and therefore still repeated the same advice for every instance of a class in the computation graph, and again in every fit. The flag is kept as a cheap guard so that the hot evaluation path doesn't take the lookup more than once per node, and the message is reworded to refer to the class instead of the individual argument. As before, the messages are only emitted if the evaluated node is not a scalar, because there would be no speedup benefit otherwise. This is a prerequisite for making the FastEvaluations topic visible by default. --- roofit/roofitcore/src/RooAbsReal.cxx | 16 ++++++++--- roofit/roofitcore/src/RooFit/Evaluator.cxx | 32 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) 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) { From 68743d2729c830a978af3a2f0a57baa240d4c38e Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sat, 29 Aug 2026 14:37:40 +0000 Subject: [PATCH 2/2] [RF] Show INFO messages on the FastEvaluations topic by default The FastEvaluations messages inform the user that a class does not implement the batch evaluation interface (doEval()), falling back to scalar evaluate() calls. Since the vectorizing CPU backend is now the default for likelihood evaluations, this information is relevant to regular users with custom classes, and it helps tracing down remaining RooFit classes without a doEval() implementation (see the corresponding item in issue #6557). So far, the messages were effectively invisible: the topic was not part of any default stream, so nobody saw them unless they manually added a message stream. Add the topic to the default INFO stream and update the stale class documentation to match the actual default stream configuration. --- roofit/roofitcore/src/RooMsgService.cxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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)); }