Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Evaluator.cxx
Go to the documentation of this file.
1/*
2 * Project: RooFit
3 * Authors:
4 * Jonas Rembser, CERN 2021
5 * Emmanouil Michalainas, CERN 2021
6 *
7 * Copyright (c) 2021, CERN
8 *
9 * Redistribution and use in source and binary forms,
10 * with or without modification, are permitted according to the terms
11 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
12 */
13
14/**
15\file Evaluator.cxx
16\class RooFit::Evaluator
17\ingroup Roofitcore
18
19Evaluates a RooAbsReal object in other ways than recursive graph
20traversal. Currently, it is being used for evaluating a RooAbsReal object and
21supplying the value to the minimizer, during a fit. The class scans the
22dependencies and schedules the computations in a secure and efficient way. The
23computations take place in the RooBatchCompute library and can be carried off
24by either the CPU or a CUDA-supporting GPU. The Evaluator class takes care
25of data transfers. An instance of this class is created every time
26RooAbsPdf::fitTo() is called and gets destroyed when the fitting ends.
27**/
28
29#include <RooFit/Evaluator.h>
30
31#include <RooAbsCategory.h>
32#include <RooAbsData.h>
33#include <RooAbsReal.h>
34#include <RooRealVar.h>
35#include <RooBatchCompute.h>
36#include <RooMsgService.h>
37#include <RooNameReg.h>
38#include <RooSimultaneous.h>
39
40#include <RooBatchCompute.h>
41
43#include "RooFitImplHelpers.h"
44
45#include <atomic>
46#include <iomanip>
47#include <numeric>
48#include <unordered_set>
49
50namespace RooFit {
51
52namespace {
53
54// To avoid deleted move assignment.
55template <class T>
56void assignSpan(std::span<T> &to, std::span<T> const &from)
57{
58 to = from;
59}
60
62{
63 // We have to exit early if the message stream is not active. Otherwise it's
64 // possible that this function skips logging because it thinks it has
65 // already logged, but actually it didn't.
66 if (!RooMsgService::instance().isActive(nullptr, RooFit::Fitting, RooFit::INFO)) {
67 return;
68 }
69
70 // Don't repeat logging architecture info if the useGPU option didn't change
71 {
72 // Second element of pair tracks whether this function has already been called
73 static std::pair<bool, bool> lastUseGPU;
74 if (lastUseGPU.second && lastUseGPU.first == useGPU)
75 return;
76 lastUseGPU = {useGPU, true};
77 }
78
79 auto log = [](std::string_view message) {
80 oocxcoutI(static_cast<RooAbsArg *>(nullptr), Fitting) << message << std::endl;
81 };
82
84 log("using generic CPU library compiled with no vectorizations");
85 } else {
86 log(std::string("using CPU computation library compiled with -m") + RooBatchCompute::cpuArchitectureName());
87 }
88 if (useGPU) {
89 log("using CUDA computation library");
90 }
91}
92
93} // namespace
94
95/// A struct used by the Evaluator to store information on the RooAbsArgs in
96/// the computation graph.
97struct NodeInfo {
98
99 bool isScalar() const { return outputSize == 1; }
100
101 RooAbsArg *absArg = nullptr;
103
104 std::shared_ptr<RooBatchCompute::AbsBuffer> buffer;
105 std::size_t iNode = 0;
106 int remClients = 0;
108 bool fromArrayInput = false;
109 bool isVariable = false;
110 bool isDirty = true;
111 bool isCategory = false;
112 bool hasLogged = false;
113 bool computeInGPU = false;
114 bool isValueServer = false; // if this node is a value server to the top node
115 std::size_t outputSize = 1;
116 std::size_t lastSetValCount = std::numeric_limits<std::size_t>::max();
117 int lastCatVal = std::numeric_limits<int>::max();
118 double scalarBuffer = 0.0;
119 std::vector<NodeInfo *> serverInfos;
120 std::vector<NodeInfo *> clientInfos;
121
122 /// Check the servers of a node that has been computed and release its
123 /// resources if they are no longer needed. Buffers of nodes whose results
124 /// are copied between host and device (copyAfterEvaluation) must not be
125 /// released eagerly: their pinned host memory can still be the source of
126 /// an asynchronous copy that was enqueued on the CUDA stream, and a new
127 /// owner would overwrite it from the CPU without any stream ordering.
128 /// Those buffers are released at the beginning of the next evaluation
129 /// instead, after the stream was synchronized at the end of this one.
131 {
132 if (--remClients == 0 && !fromArrayInput && !copyAfterEvaluation) {
133 buffer.reset();
134 }
135 }
136};
137
138/// Construct a new Evaluator. The constructor analyzes and saves metadata about the graph,
139/// useful for the evaluation of it that will be done later. In case the CUDA mode is selected,
140/// there's also some CUDA-related initialization.
141///
142/// \param[in] absReal The RooAbsReal object that sits on top of the
143/// computation graph that we want to evaluate.
144/// \param[in] useGPU Whether the evaluation should be preferably done on the GPU.
146 : _topNode{const_cast<RooAbsReal &>(absReal)}, _useGPU{useGPU}
147{
149 if (useGPU && RooBatchCompute::initCUDA() != 0) {
150 throw std::runtime_error("Can't create Evaluator in CUDA mode because RooBatchCompute CUDA could not be loaded!");
151 }
152 // Some checks and logging of used architectures
154
157
160
162 if (useGPU) {
164 }
165
166 std::map<RooFit::Detail::DataKey, NodeInfo *> nodeInfos;
167
168 // Fill the ordered nodes list and initialize the node info structs.
169 _nodes.reserve(serverSet.size());
170 std::size_t iNode = 0;
171 for (RooAbsArg *arg : serverSet) {
172
173 _nodes.emplace_back();
174 auto &nodeInfo = _nodes.back();
175 _nodesMap[arg->namePtr()] = &nodeInfo;
176
177 nodeInfo.absArg = arg;
178 nodeInfo.originalOperMode = arg->operMode();
179 nodeInfo.iNode = iNode;
180 nodeInfos[arg] = &nodeInfo;
181
182 if (dynamic_cast<RooRealVar const *>(arg)) {
183 nodeInfo.isVariable = true;
184 } else {
185 arg->setDataToken(iNode);
186 }
187 if (dynamic_cast<RooAbsCategory const *>(arg)) {
188 nodeInfo.isCategory = true;
189 }
190
191 ++iNode;
192 }
193
194 for (NodeInfo &info : _nodes) {
195 info.serverInfos.reserve(info.absArg->servers().size());
196 for (RooAbsArg *server : info.absArg->servers()) {
197 if (server->isValueServer(*info.absArg)) {
198 auto *serverInfo = nodeInfos.at(server);
199 info.serverInfos.emplace_back(serverInfo);
200 serverInfo->clientInfos.emplace_back(&info);
201 }
202 }
203 }
204
205 // Figure out which nodes are value servers to the top node
206 _nodes.back().isValueServer = true; // the top node itself
207 for (auto iter = _nodes.rbegin(); iter != _nodes.rend(); ++iter) {
208 if (!iter->isValueServer)
209 continue;
210 for (auto &serverInfo : iter->serverInfos) {
211 serverInfo->isValueServer = true;
212 }
213 }
214
216
217 if (_useGPU) {
218 // Create the single CUDA stream on which all GPU computations and data
219 // transfers of this Evaluator are enqueued. The graph is evaluated in
220 // topological order, so ordering the operations by the stream is enough
221 // to guarantee correct results.
225 for (auto &info : _nodes) {
226 _evalContextCUDA.setConfig(info.absArg, cfg);
227 }
228 }
229}
230
231/// If there are servers with the same name that got de-duplicated in the
232/// `_nodes` list, we need to set their data tokens too. We find such nodes by
233/// visiting the servers of every known node.
235{
236 for (NodeInfo &info : _nodes) {
237 std::size_t iValueServer = 0;
238 for (RooAbsArg *server : info.absArg->servers()) {
239 if (server->isValueServer(*info.absArg)) {
240 auto *knownServer = info.serverInfos[iValueServer]->absArg;
241 if (knownServer->hasDataToken()) {
242 server->setDataToken(knownServer->dataToken());
243 }
244 ++iValueServer;
245 }
246 }
247 }
248}
249
250void Evaluator::setInput(std::string const &name, std::span<const double> inputArray, bool isOnDevice)
251{
252 if (isOnDevice && !_useGPU) {
253 throw std::runtime_error("Evaluator can only take device array as input in CUDA mode!");
254 }
255
256 // Check if "name" is used in the computation graph. If yes, add the span to
257 // the data map and set the node info accordingly.
258
259 auto found = _nodesMap.find(RooNameReg::ptr(name.c_str()));
260
261 if (found == _nodesMap.end())
262 return;
263
265
266 // Invalidate the caches that reducer nodes key on the input data, like the
267 // cached sum of event weights in RooNLLVarNew. The counter is global so
268 // that generation values can never alias between different Evaluators.
269 {
270 static std::atomic<std::size_t> nextInputGeneration{1};
271 const std::size_t gen = ++nextInputGeneration;
274 }
275
276 NodeInfo &info = *found->second;
277
278 info.fromArrayInput = true;
279 info.absArg->setDataToken(info.iNode);
280 info.outputSize = inputArray.size();
281
282 if (!_useGPU) {
284 return;
285 }
286
287 if (info.outputSize <= 1) {
288 // Empty or scalar observables from the data don't need to be
289 // copied to the GPU.
292 return;
293 }
294
295 // For simplicity, we put the data on both host and device for
296 // now. This could be optimized by inspecting the clients of the
297 // variable.
298 if (isOnDevice) {
300 auto gpuSpan = _evalContextCUDA.at(info.absArg);
301 info.buffer = _bufferManager->makeCpuBuffer(gpuSpan.size());
302 info.buffer->assignFromDevice(gpuSpan);
303 _evalContextCPU.set(info.absArg, {info.buffer->hostReadPtr(), gpuSpan.size()});
304 } else {
306 auto cpuSpan = _evalContextCPU.at(info.absArg);
307 info.buffer = _bufferManager->makeGpuBuffer(cpuSpan.size());
308 info.buffer->assignFromHost(cpuSpan);
309 _evalContextCUDA.set(info.absArg, {info.buffer->deviceReadPtr(), cpuSpan.size()});
310 }
311}
312
314{
315 std::map<RooFit::Detail::DataKey, std::size_t> sizeMap;
316 for (auto &info : _nodes) {
317 if (info.fromArrayInput) {
318 sizeMap[info.absArg] = info.outputSize;
319 } else {
320 // any buffer for temporary results is invalidated by resetting the output sizes
321 info.buffer.reset();
322 }
323 }
324
325 auto outputSizeMap =
326 RooFit::BatchModeDataHelpers::determineOutputSizes(_topNode, [&](RooFit::Detail::DataKey key) -> int {
327 auto found = sizeMap.find(key);
328 return found != sizeMap.end() ? found->second : -1;
329 });
330
331 for (auto &info : _nodes) {
332 info.outputSize = outputSizeMap.at(info.absArg);
333 info.isDirty = true;
334 }
335
336 if (_useGPU) {
337 markGPUNodes();
338 }
339
341}
342
344{
345 for (auto &info : _nodes) {
346 if (!info.isVariable) {
347 info.absArg->resetDataToken();
348 }
349 }
350 if (_cudaStream) {
352 }
353}
354
356{
357 using namespace Detail;
358
359 const std::size_t nOut = info.outputSize;
360
361 double *buffer = nullptr;
362 if (nOut == 1) {
363 buffer = &info.scalarBuffer;
364 if (_useGPU) {
365 _evalContextCUDA.set(node, {buffer, nOut});
366 }
367 } else {
368 if (!info.hasLogged && _useGPU) {
369 RooAbsArg const &arg = *info.absArg;
370 oocoutI(&arg, FastEvaluations) << "The argument " << arg.ClassName() << "::" << arg.GetName()
371 << " could not be evaluated on the GPU because the class doesn't support it. "
372 "Consider requesting or implementing it to benefit from a speed up."
373 << std::endl;
374 info.hasLogged = true;
375 }
376 if (!info.buffer) {
377 info.buffer = info.copyAfterEvaluation ? _bufferManager->makePinnedBuffer(nOut, _cudaStream)
378 : _bufferManager->makeCpuBuffer(nOut);
379 }
380 buffer = info.buffer->hostWritePtr();
381 }
383 _evalContextCPU.set(node, {buffer, nOut});
384 if (nOut > 1) {
386 }
387 if (info.isCategory) {
388 auto nodeAbsCategory = static_cast<RooAbsCategory const *>(node);
389 if (nOut == 1) {
390 buffer[0] = nodeAbsCategory->getCurrentIndex();
391 } else {
392 throw std::runtime_error("RooFit::Evaluator - non-scalar category values are not supported!");
393 }
394 } else {
395 auto nodeAbsReal = static_cast<RooAbsReal const *>(node);
397 }
400 if (info.copyAfterEvaluation) {
401 // The deviceReadPtr() call triggers the copy of the result to the GPU.
402 // The copy is ordered by the CUDA stream, so GPU clients enqueued later
403 // will see the result without any further synchronization.
404 _evalContextCUDA.set(node, {info.buffer->deviceReadPtr(), nOut});
405 }
406}
407
408/// Process a variable in the computation graph. This is a separate non-inlined
409/// function such that we can see in performance profiles how long this takes.
411{
412 RooAbsArg *node = nodeInfo.absArg;
413 auto *var = static_cast<RooRealVar const *>(node);
414 if (nodeInfo.lastSetValCount != var->valueResetCounter()) {
415 nodeInfo.lastSetValCount = var->valueResetCounter();
416 for (NodeInfo *clientInfo : nodeInfo.clientInfos) {
417 clientInfo->isDirty = true;
418 }
420 nodeInfo.isDirty = false;
421 }
422}
423
424/// Process a category in the computation graph. This is a separate non-inlined
425/// function such that we can see in performance profiles how long this takes.
427{
428 RooAbsArg *node = nodeInfo.absArg;
429 auto *cat = static_cast<RooAbsCategory const *>(node);
430 if (nodeInfo.lastCatVal != cat->getCurrentIndex()) {
431 nodeInfo.lastCatVal = cat->getCurrentIndex();
432 for (NodeInfo *clientInfo : nodeInfo.clientInfos) {
433 clientInfo->isDirty = true;
434 }
436 nodeInfo.isDirty = false;
437 }
438}
439
440/// Flags all the clients of a given node dirty. This is a separate non-inlined
441/// function such that we can see in performance profiles how long this takes.
443{
444 for (NodeInfo *clientInfo : nodeInfo.clientInfos) {
445 clientInfo->isDirty = true;
446 }
447}
448
449/// Returns the value of the top node in the computation graph
450std::span<const double> Evaluator::run()
451{
454
456
457 // Discard leftover deferred actions in case a previous evaluation was
458 // aborted by an exception.
461
462 if (_useGPU) {
463 return getValHeterogeneous();
464 }
465
466 for (auto &nodeInfo : _nodes) {
467 if (!nodeInfo.fromArrayInput) {
468 if (nodeInfo.isVariable) {
470 } else if (nodeInfo.isCategory) {
472 } else {
473 if (nodeInfo.isDirty) {
476 nodeInfo.isDirty = false;
477 }
478 }
479 }
480 }
481
483 action();
484 }
486
487 // return the final output
488 return _evalContextCPU.at(&_topNode);
489}
490
491/// Returns the value of the top node in the computation graph
492std::span<const double> Evaluator::getValHeterogeneous()
493{
494 for (auto &info : _nodes) {
495 info.remClients = info.clientInfos.size();
496 if (info.buffer && !info.fromArrayInput) {
497 info.buffer.reset();
498 }
499 }
500
501 // Iterate over the nodes in topological order. Nodes that are computed on
502 // the GPU only enqueue their computation on the single CUDA stream and
503 // return immediately, so independent CPU nodes that come later in the
504 // ordering naturally overlap with the GPU computations. Ordering by the
505 // stream guarantees that GPU nodes see the results of their GPU servers,
506 // and host-side reads of GPU results synchronize on the stream in the
507 // buffer implementation.
508 try {
509 for (auto &info : _nodes) {
510 if (!info.fromArrayInput) {
511 if (info.computeInGPU) {
513 } else {
514 computeCPUNode(info.absArg, info);
515 }
516 }
517
518 // Release the buffers of server nodes that are no longer needed. For
519 // device-only buffers this is safe to do right away even if GPU work
520 // is still in flight, because any reuse of a released device buffer
521 // happens through operations that are enqueued later on the same
522 // stream. Pinned buffers are exempted from the eager release, see
523 // the comment in NodeInfo::decrementRemainingClients().
524 for (auto *serverInfo : info.serverInfos) {
525 serverInfo->decrementRemainingClients();
526 }
527 }
528 } catch (...) {
529 // The evaluation was aborted, but readbacks that compute() calls
530 // deferred may still be armed. Deliver them now, while the destination
531 // memory in the nodes of the computation graph is guaranteed to be
532 // alive, so that no armed readback survives into a later evaluation.
533 try {
535 } catch (...) {
536 // The stream is in an unrecoverable error state. The deferred
537 // readbacks are dropped together with the scratch memory when the
538 // stream gets deleted.
539 }
542 throw;
543 }
544
545 // Ensure that all enqueued GPU work has completed when run() returns. For
546 // the usual likelihood evaluations this is mostly a no-op, because the
547 // final reduction has synchronized the stream already. It also guarantees
548 // that recycling the buffers at the beginning of the next evaluation is
549 // safe, and it delivers the deferred readbacks like the evaluation error
550 // counters.
552
553 // Run the deferred actions now that all results have arrived on the host,
554 // e.g. the logging of evaluation errors that were counted on the GPU.
555 // Nodes evaluated on the CPU register their actions in the CPU context,
556 // so both contexts are drained.
557 for (auto *ctx : {&_evalContextCUDA, &_evalContextCPU}) {
558 for (auto &action : ctx->_deferredActions) {
559 action();
560 }
561 ctx->_deferredActions.clear();
562 }
563
564 // return the final value
566}
567
568/// Enqueue the computation of a node on the GPU.
570{
571 using namespace Detail;
572
573 auto node = static_cast<RooAbsReal const *>(info.absArg);
574
575 const std::size_t nOut = info.outputSize;
576
577 double *buffer = nullptr;
578 if (nOut == 1) {
579 buffer = &info.scalarBuffer;
580 _evalContextCPU.set(node, {buffer, nOut});
581 } else {
582 info.buffer = info.copyAfterEvaluation ? _bufferManager->makePinnedBuffer(nOut, _cudaStream)
583 : _bufferManager->makeGpuBuffer(nOut);
584 buffer = info.buffer->deviceWritePtr();
585 }
587 _evalContextCUDA.set(node, {buffer, nOut});
588 node->doEval(_evalContextCUDA);
589 if (info.copyAfterEvaluation) {
590 // The hostReadPtr() call triggers the copy of the result to the host,
591 // which waits for the enqueued computation via the CUDA stream.
592 _evalContextCPU.set(node, {info.buffer->hostReadPtr(), nOut});
593 }
594}
595
596/// Decides which nodes are assigned to the GPU in a CUDA fit.
598{
599 // Decide which nodes get evaluated on the GPU: we select nodes that support
600 // CUDA evaluation and have at least one input of size greater than one.
601 for (auto &info : _nodes) {
602 info.computeInGPU = false;
603 if (!info.absArg->canComputeBatchWithCuda()) {
604 continue;
605 }
606 for (NodeInfo const *serverInfo : info.serverInfos) {
607 if (serverInfo->outputSize > 1) {
608 info.computeInGPU = true;
609 break;
610 }
611 }
612 }
613
614 // In a second pass, figure out which nodes need to copy over their results.
615 for (auto &info : _nodes) {
616 info.copyAfterEvaluation = false;
617 // scalar nodes don't need copying
618 if (!info.isScalar()) {
619 for (auto *clientInfo : info.clientInfos) {
620 if (info.computeInGPU != clientInfo->computeInGPU) {
621 info.copyAfterEvaluation = true;
622 break;
623 }
624 }
625 }
626 }
627}
628
629/// \brief Sets the number of threads to use for the evaluation of a single node.
630///
631/// With a value greater than one, the computation functions and reductions of
632/// the CPU backend process large batches multi-threaded, using up to the
633/// given number of threads. Nodes evaluated on the CPU with fewer events than
634/// an internal threshold are still evaluated single-threaded, so requesting
635/// multiple threads never introduces scheduling overhead for small fits.
636void Evaluator::setNThreads(int nThreads)
637{
638 for (auto &info : _nodes) {
639 if (info.isVariable) {
640 continue;
641 }
643 cfg.setNThreads(nThreads);
644 _evalContextCPU.setConfig(info.absArg, cfg);
645 }
646}
647
648/// Temporarily change the operation mode of a RooAbsArg until the
649/// Evaluator gets deleted.
651{
652 if (!_operModeChanges)
653 _operModeChanges = std::make_unique<ChangeOperModeRAII>();
654 _operModeChanges->change(arg, opMode);
655}
656
657// Change the operation modes of all RooAbsArgs in the computation graph.
658// The changes are reset when the returned RAII object goes out of scope.
659//
660// We also walk transitively through value clients of the nodes to cover any
661// node that RooAbsReal::doEval (the fallback scalar implementation) might
662// inadvertently propagate the ADirty mode to via its recursive restore: that
663// helper sets servers temporarily to AClean and then calls
664// setOperMode(oldOperMode) to restore, which recurses to value clients when
665// oldOperMode is ADirty. If we did not protect those clients here, any node
666// outside the computation graph that shares a fundamental (e.g. a parameter
667// like a RooRealVar) would be left permanently in ADirty after the first
668// minimization, dramatically slowing down later scalar evaluations (for
669// example on pdfs held by the legacy test statistics' internal cache).
670std::unique_ptr<ChangeOperModeRAII> Evaluator::setOperModes(RooAbsArg::OperMode opMode)
671{
672 auto out = std::make_unique<ChangeOperModeRAII>();
673 std::unordered_set<RooAbsArg *> visited;
674
675 std::vector<RooAbsArg *> queue;
676 queue.reserve(_nodes.size());
677 for (auto &info : _nodes) {
678 queue.push_back(info.absArg);
679 }
680
681 while (!queue.empty()) {
682 RooAbsArg *node = queue.back();
683 queue.pop_back();
684 if (!visited.insert(node).second)
685 continue;
686
687 out->change(node, opMode);
688
689 // Only follow value-client links: that is exactly the propagation path
690 // used by RooAbsArg::setOperMode with mode==ADirty.
691 if (opMode == RooAbsArg::ADirty) {
692 for (auto *client : node->valueClients()) {
693 queue.push_back(client);
694 }
695 }
696 }
697 return out;
698}
699
700void Evaluator::print(std::ostream &os)
701{
702 std::cout << "--- RooFit BatchMode evaluation ---\n";
703
704 std::vector<int> widths{9, 37, 20, 9, 10, 20};
705
706 auto printElement = [&](int iCol, auto const &t) {
707 const char separator = ' ';
708 os << separator << std::left << std::setw(widths[iCol]) << std::setfill(separator) << t;
709 os << "|";
710 };
711
712 auto printHorizontalRow = [&]() {
713 int n = 0;
714 for (int w : widths) {
715 n += w + 2;
716 }
717 for (int i = 0; i < n; i++) {
718 os << '-';
719 }
720 os << "|\n";
721 };
722
724
725 os << "|";
726 printElement(0, "Index");
727 printElement(1, "Name");
728 printElement(2, "Class");
729 printElement(3, "Size");
730 printElement(4, "From Data");
731 printElement(5, "1st value");
732 std::cout << "\n";
733
735
736 for (std::size_t iNode = 0; iNode < _nodes.size(); ++iNode) {
737 auto &nodeInfo = _nodes[iNode];
738 RooAbsArg *node = nodeInfo.absArg;
739
740 auto span = _evalContextCPU.at(node);
741
742 os << "|";
743 printElement(0, iNode);
744 printElement(1, node->GetName());
745 printElement(2, node->ClassName());
746 printElement(3, nodeInfo.outputSize);
747 printElement(4, nodeInfo.fromArrayInput);
748 printElement(5, span[0]);
749
750 std::cout << "\n";
751 }
752
754}
755
756/// Gets all the parameters of the RooAbsReal. This is in principle not
757/// necessary, because we can always ask the RooAbsReal itself, but the
758/// Evaluator has the cached information to get the answer quicker.
759/// Therefore, this is not meant to be used in general, just where it matters.
760/// \warning If we find another solution to get the parameters efficiently,
761/// this function might be removed without notice.
763{
764 RooArgSet parameters;
765 for (auto &nodeInfo : _nodes) {
766 if (nodeInfo.isValueServer && nodeInfo.absArg->isFundamental()) {
767 parameters.add(*nodeInfo.absArg);
768 }
769 }
770 // Just like in RooAbsArg::getParameters(), we sort the parameters alphabetically.
771 parameters.sort();
772 return parameters;
773}
774
775/// \brief Sets the offset mode for evaluation.
776///
777/// This function sets the offset mode for evaluation to the specified mode.
778/// It updates the offset mode for both CPU and CUDA evaluation contexts.
779///
780/// \param mode The offset mode to be set.
781///
782/// \note This function marks reducer nodes as dirty if the offset mode is
783/// changed, because only reducer nodes can use offsetting.
785{
787 return;
788
791
792 for (auto &nodeInfo : _nodes) {
793 if (nodeInfo.absArg->isReducerNode()) {
794 nodeInfo.isDirty = true;
795 }
796 }
797}
798
799} // namespace RooFit
#define oocoutI(o, a)
#define oocxcoutI(o, a)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:142
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
const RefCountList_t & valueClients() const
List of all value clients of this object. Value clients receive value updates.
Definition RooAbsArg.h:139
A space to attach TBranches.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
void sort(bool reverse=false)
Sort collection using std::sort and name comparison.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Minimal configuration struct to steer the evaluation of a single node with the RooBatchCompute librar...
void setCudaStream(CudaInterface::CudaStream *cudaStream)
void setNThreads(int nThreads)
Number of threads to use for CPU batch computations and reductions.
virtual void synchronizeCudaStream(CudaInterface::CudaStream *) const =0
Wait until all work that was enqueued on the stream has completed.
virtual std::unique_ptr< AbsBufferManager > createBufferManager() const =0
virtual CudaInterface::CudaStream * newCudaStream() const =0
virtual void deleteCudaStream(CudaInterface::CudaStream *) const =0
std::size_t _inputGeneration
std::vector< std::function< void()> > _deferredActions
void set(RooAbsArg const *arg, std::span< const double > const &span)
Definition EvalContext.h:92
std::span< const double > at(RooAbsArg const *arg, RooAbsArg const *caller=nullptr)
void enableVectorBuffers(bool enable)
OffsetMode _offsetMode
RooBatchCompute::Config config(RooAbsArg const *arg) const
void setConfig(RooAbsArg const *arg, RooBatchCompute::Config const &config)
std::span< double > _currentOutput
void resize(std::size_t n)
void print(std::ostream &os)
void setClientsDirty(NodeInfo &nodeInfo)
Flags all the clients of a given node dirty.
std::unique_ptr< ChangeOperModeRAII > setOperModes(RooAbsArg::OperMode opMode)
RooArgSet getParameters() const
Gets all the parameters of the RooAbsReal.
void setOffsetMode(RooFit::EvalContext::OffsetMode)
Sets the offset mode for evaluation.
void syncDataTokens()
If there are servers with the same name that got de-duplicated in the _nodes list,...
const bool _useGPU
Definition Evaluator.h:68
std::unordered_map< TNamed const *, NodeInfo * > _nodesMap
Definition Evaluator.h:74
std::unique_ptr< ChangeOperModeRAII > _operModeChanges
Definition Evaluator.h:75
std::vector< NodeInfo > _nodes
Definition Evaluator.h:73
bool _needToUpdateOutputSizes
Definition Evaluator.h:70
std::span< const double > getValHeterogeneous()
Returns the value of the top node in the computation graph.
std::span< const double > run()
Returns the value of the top node in the computation graph.
Evaluator(const RooAbsReal &absReal, bool useGPU=false)
Construct a new Evaluator.
void setNThreads(int nThreads)
Sets the number of threads to use for the evaluation of a single node.
void processVariable(NodeInfo &nodeInfo)
Process a variable in the computation graph.
void processCategory(NodeInfo &nodeInfo)
Process a category in the computation graph.
RooBatchCompute::CudaInterface::CudaStream * _cudaStream
Definition Evaluator.h:77
std::unique_ptr< RooBatchCompute::AbsBufferManager > _bufferManager
Definition Evaluator.h:66
void markGPUNodes()
Decides which nodes are assigned to the GPU in a CUDA fit.
void assignToGPU(NodeInfo &info)
Enqueue the computation of a node on the GPU.
void setInput(std::string const &name, std::span< const double > inputArray, bool isOnDevice)
RooFit::EvalContext _evalContextCUDA
Definition Evaluator.h:72
RooFit::EvalContext _evalContextCPU
Definition Evaluator.h:71
void computeCPUNode(const RooAbsArg *node, NodeInfo &info)
void setOperMode(RooAbsArg *arg, RooAbsArg::OperMode opMode)
Temporarily change the operation mode of a RooAbsArg until the Evaluator gets deleted.
RooAbsReal & _topNode
Definition Evaluator.h:67
static RooMsgService & instance()
Return reference to singleton instance.
static const TNamed * ptr(const char *stringPtr)
Return a unique TNamed pointer for given C++ string.
Variable that can be changed from the outside.
Definition RooRealVar.h:37
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
RVec< PromoteType< T > > log(const RVec< T > &v)
Definition RVec.hxx:1821
const Int_t n
Definition legend1.C:16
R__EXTERN RooBatchComputeInterface * dispatchCUDA
std::string cpuArchitectureName()
R__EXTERN RooBatchComputeInterface * dispatchCPU
This dispatch pointer points to an implementation of the compute library, provided one has been loade...
Architecture cpuArchitecture()
int initCPU()
Inspect hardware capabilities, and load the optimal library for RooFit computations.
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73
@ FastEvaluations
void getSortedComputationGraph(RooAbsArg const &func, RooArgSet &out)
A struct used by the Evaluator to store information on the RooAbsArgs in the computation graph.
Definition Evaluator.cxx:97
RooAbsArg * absArg
bool isScalar() const
Definition Evaluator.cxx:99
std::size_t iNode
std::size_t lastSetValCount
std::vector< NodeInfo * > serverInfos
RooAbsArg::OperMode originalOperMode
std::size_t outputSize
std::vector< NodeInfo * > clientInfos
std::shared_ptr< RooBatchCompute::AbsBuffer > buffer
void decrementRemainingClients()
Check the servers of a node that has been computed and release its resources if they are no longer ne...