Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
df013_InspectAnalysis.C
Go to the documentation of this file.
1/// \file
2/// \ingroup tutorial_dataframe
3/// \notebook -draw
4/// Use callbacks to update a plot and a progress bar during the event loop.
5///
6/// Showcase registration of callback functions that act on partial results while
7/// the event-loop is running using `OnPartialResult` and `OnPartialResultSlot`.
8/// This tutorial is not meant to run in batch mode.
9///
10/// \macro_code
11///
12/// \date September 2017
13/// \author Enrico Guiraud (CERN)
14
15using namespace ROOT; // RDataFrame lives in here
16
17void df013_InspectAnalysis()
18{
20 const auto poolSize = ROOT::GetThreadPoolSize();
21 const auto nSlots = 0 == poolSize ? 1 : poolSize;
22
23 // ## Setup a simple RDataFrame
24 // We start by creating a RDataFrame with a good number of empty events
25 const auto nEvents = nSlots * 10000ull;
26 RDataFrame d(nEvents);
27
28 // `heavyWork` is a lambda that fakes some interesting computation and just returns a normally distributed double
29 TRandom r;
30 auto heavyWork = [&r]() {
31 for (volatile int i = 0; i < 1000000; ++i)
32 ;
33 return r.Gaus();
34 };
35
36 // Let's define a column "x" produced by invoking `heavyWork` for each event
37 // `df` stores a modified data-frame that contains "x"
38 auto df = d.Define("x", heavyWork);
39
40 // Now we register a histogram-filling action with the RDataFrame.
41 // `h` can be used just like a pointer to TH1D but it is actually a TResultProxy<TH1D>, a smart object that triggers
42 // an event-loop to fill the pointee histogram if needed.
43 auto h = df.Histo1D<double>({"browserHisto", "", 100, -2., 2.}, "x");
44
45 // ## Use the callback mechanism to draw the histogram on a TBrowser while it is being filled
46 // So far we have registered a column "x" to a data-frame with `nEvents` events and we registered the filling of a
47 // histogram with the values of column "x".
48 // In the following we will register three functions for execution during the event-loop:
49 // - one is to be executed once just before the loop and adds a partially-filled histogram to a TBrowser
50 // - the next is executed every 50 events and draws the partial histogram on the TBrowser's TPad
51 // - another callback is responsible of updating a simple progress bar from multiple threads
52
53 // First off we create a TBrowser that contains a "RDFResults" directory
54 auto dfDirectory = new TMemFile("RDFResults", "RECREATE");
55 auto browser = new TBrowser("b", dfDirectory);
56 // The global pad should now be set to the TBrowser's canvas, let's store its value in a local variable
57 auto browserPad = gPad;
58
59 // A useful feature of `TResultProxy` is its `OnPartialResult` method: it allows us to register a callback that is
60 // executed once per specified number of events during the event-loop, on "partial" versions of the result objects
61 // contained in the `TResultProxy`. In this case, the partial result is going to be a histogram filled with an
62 // increasing number of events.
63 // Instead of requesting the callback to be executed every N entries, this time we use the special value `kOnce` to
64 // request that it is executed once right before starting the event-loop.
65 // The callback is a C++11 lambda that registers the partial result object in `dfDirectory`.
66 h.OnPartialResult(h.kOnce, [dfDirectory](TH1D &h_) { dfDirectory->Add(&h_); });
67 // Note that we called `OnPartialResult` with a dot, `.`, since this is a method of `TResultProxy` itself.
68 // We do not want to call `OnPartialResult` on the pointee histogram!)
69
70 // Multiple callbacks can be registered on the same `TResultProxy` (they are executed one after the other in the
71 // same order as they were registered). We now request that the partial result is drawn and the TBrowser's TPad is
72 // updated every 50 events.
73 h.OnPartialResult(50, [&browserPad](TH1D &hist) {
74 if (!browserPad)
75 return; // in case root -b was invoked
76 browserPad->cd();
77 hist.Draw();
78 browserPad->Update();
79 // This call tells ROOT to process all pending GUI events
80 // It allows users to use the TBrowser as usual while the event-loop is running
82 });
83
84 // Finally, we would like to print a progress bar on the terminal to show how the event-loop is progressing.
85 // To take into account _all_ events we use `OnPartialResultSlot`: when Implicit Multi-Threading is enabled, in fact,
86 // `OnPartialResult` invokes the callback only in one of the worker threads, and always returns that worker threads'
87 // partial result. This is useful because it means we don't have to worry about concurrent execution and
88 // thread-safety of the callbacks if we are happy with just one threads' partial result.
89 // `OnPartialResultSlot`, on the other hand, invokes the callback in each one of the worker threads, every time a
90 // thread finishes processing a batch of `everyN` events. This is what we want for the progress bar, but we need to
91 // take care that two threads will not print to terminal at the same time: we need a std::mutex for synchronization.
92 std::string progressBar;
93 std::mutex barMutex; // Only one thread at a time can lock a mutex. Let's use this to avoid concurrent printing.
94 // Magic numbers that yield good progress bars for nSlots = 1,2,4,8
95 const auto everyN = nSlots == 8 ? 1000 : 100ull * nSlots;
96 const auto barWidth = nEvents / everyN;
97 h.OnPartialResultSlot(everyN, [&barWidth, &progressBar, &barMutex](unsigned int /*slot*/, TH1D & /*partialHist*/) {
98 std::lock_guard<std::mutex> l(barMutex); // lock_guard locks the mutex at construction, releases it at destruction
99 progressBar.push_back('#');
100 // re-print the line with the progress bar
101 std::cout << "\r[" << std::left << std::setw(barWidth) << progressBar << ']' << std::flush;
102 });
103
104 // ## Running the analysis
105 // So far we told RDataFrame what we want to happen during the event-loop, but we have not actually run any of those
106 // actions: the TBrowser is still empty, the progress bar has not been printed even once, and we haven't produced
107 // a single data-point!
108 // As usual with RDataFrame, the event-loop is triggered by accessing the contents of a TResultProxy for the first
109 // time. Let's run!
110 std::cout << "Analysis running..." << std::endl;
111 h->Draw(); // the final, complete result will be drawn after the event-loop has completed.
112 std::cout << "\nDone!" << std::endl;
113
114 // Finally, some book-keeping: in the TMemFile that we are using as TBrowser directory, we substitute the partial
115 // result with a clone of the final result (the "original" final result will be deleted at the end of the macro).
116 dfDirectory->Clear();
117 auto clone = static_cast<TH1D *>(h->Clone());
118 clone->SetDirectory(nullptr);
119 dfDirectory->Add(clone);
120 if (!browserPad)
121 return; // in case root -b was invoked
122 browserPad->cd();
123 clone->Draw();
124 browserPad->Update();
125}
#define d(i)
Definition RSha256.hxx:102
#define h(i)
Definition RSha256.hxx:106
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
#define gPad
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
1-D histogram with a double per channel (see TH1 documentation)}
Definition TH1.h:620
virtual void SetDirectory(TDirectory *dir)
By default, when a histogram is created, it is added to the list of histogram objects in the current ...
Definition TH1.cxx:8803
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3060
A TMemFile is like a normal TFile except that it reads and writes only from memory.
Definition TMemFile.h:19
This is the base class for the ROOT Random number generators.
Definition TRandom.h:27
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:419
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
void EnableImplicitMT(UInt_t numthreads=0)
Enable ROOT's implicit multi-threading for all objects and methods that provide an internal paralleli...
Definition TROOT.cxx:527
UInt_t GetThreadPoolSize()
Returns the size of ROOT's thread pool.
Definition TROOT.cxx:565
TLine l
Definition textangle.C:4