ROOT   Reference Guide
df013_InspectAnalysis.C File Reference

## Detailed Description

Use callbacks to update a plot and a progress bar during the event loop.

Showcase registration of callback functions that act on partial results while the event-loop is running using OnPartialResult and OnPartialResultSlot. This tutorial is not meant to run in batch mode.

using namespace ROOT; // RDataFrame lives in here
void df013_InspectAnalysis()
{
const auto nSlots = 0 == poolSize ? 1 : poolSize;
// ## Setup a simple RDataFrame
// We start by creating a RDataFrame with a good number of empty events
const auto nEvents = nSlots * 10000ull;
RDataFrame d(nEvents);
// heavyWork is a lambda that fakes some interesting computation and just returns a normally distributed double
auto heavyWork = [&r]() {
for (volatile int i = 0; i < 1000000; ++i)
;
return r.Gaus();
};
// Let's define a column "x" produced by invoking heavyWork for each event
// df stores a modified data-frame that contains "x"
auto df = d.Define("x", heavyWork);
// Now we register a histogram-filling action with the RDataFrame.
// h can be used just like a pointer to TH1D but it is actually a TResultProxy<TH1D>, a smart object that triggers
// an event-loop to fill the pointee histogram if needed.
auto h = df.Histo1D<double>({"browserHisto", "", 100, -2., 2.}, "x");
// ## Use the callback mechanism to draw the histogram on a TBrowser while it is being filled
// So far we have registered a column "x" to a data-frame with nEvents events and we registered the filling of a
// histogram with the values of column "x".
// In the following we will register three functions for execution during the event-loop:
// - one is to be executed once just before the loop and adds a partially-filled histogram to a TBrowser
// - the next is executed every 50 events and draws the partial histogram on the TBrowser's TPad
// - another callback is responsible of updating a simple progress bar from multiple threads
// First off we create a TBrowser that contains a "RDFResults" directory
auto dfDirectory = new TMemFile("RDFResults", "RECREATE");
auto browser = new TBrowser("b", dfDirectory);
// The global pad should now be set to the TBrowser's canvas, let's store its value in a local variable
// A useful feature of TResultProxy is its OnPartialResult method: it allows us to register a callback that is
// executed once per specified number of events during the event-loop, on "partial" versions of the result objects
// contained in the TResultProxy. In this case, the partial result is going to be a histogram filled with an
// increasing number of events.
// Instead of requesting the callback to be executed every N entries, this time we use the special value kOnce to
// request that it is executed once right before starting the event-loop.
// The callback is a C++11 lambda that registers the partial result object in dfDirectory.
h.OnPartialResult(h.kOnce, [dfDirectory](TH1D &h_) { dfDirectory->Add(&h_); });
// Note that we called OnPartialResult with a dot, ., since this is a method of TResultProxy itself.
// We do not want to call OnPartialResult on the pointee histogram!)
// Multiple callbacks can be registered on the same TResultProxy (they are executed one after the other in the
// same order as they were registered). We now request that the partial result is drawn and the TBrowser's TPad is
// updated every 50 events.
return; // in case root -b was invoked
hist.Draw();
// This call tells ROOT to process all pending GUI events
// It allows users to use the TBrowser as usual while the event-loop is running
});
// Finally, we would like to print a progress bar on the terminal to show how the event-loop is progressing.
// To take into account _all_ events we use OnPartialResultSlot: when Implicit Multi-Threading is enabled, in fact,
// OnPartialResult invokes the callback only in one of the worker threads, and always returns that worker threads'
// partial result. This is useful because it means we don't have to worry about concurrent execution and
// thread-safety of the callbacks if we are happy with just one threads' partial result.
// OnPartialResultSlot, on the other hand, invokes the callback in each one of the worker threads, every time a
// thread finishes processing a batch of everyN events. This is what we want for the progress bar, but we need to
// take care that two threads will not print to terminal at the same time: we need a std::mutex for synchronization.
std::string progressBar;
std::mutex barMutex; // Only one thread at a time can lock a mutex. Let's use this to avoid concurrent printing.
// Magic numbers that yield good progress bars for nSlots = 1,2,4,8
const auto everyN = nSlots == 8 ? 1000 : 100ull * nSlots;
const auto barWidth = nEvents / everyN;
h.OnPartialResultSlot(everyN, [&barWidth, &progressBar, &barMutex](unsigned int /*slot*/, TH1D & /*partialHist*/) {
std::lock_guard<std::mutex> l(barMutex); // lock_guard locks the mutex at construction, releases it at destruction
progressBar.push_back('#');
// re-print the line with the progress bar
std::cout << "\r[" << std::left << std::setw(barWidth) << progressBar << ']' << std::flush;
});
// ## Running the analysis
// So far we told RDataFrame what we want to happen during the event-loop, but we have not actually run any of those
// actions: the TBrowser is still empty, the progress bar has not been printed even once, and we haven't produced
// a single data-point!
// As usual with RDataFrame, the event-loop is triggered by accessing the contents of a TResultProxy for the first
// time. Let's run!
std::cout << "Analysis running..." << std::endl;
h->Draw(); // the final, complete result will be drawn after the event-loop has completed.
std::cout << "\nDone!" << std::endl;
// Finally, some book-keeping: in the TMemFile that we are using as TBrowser directory, we substitute the partial
// result with a clone of the final result (the "original" final result will be deleted at the end of the macro).
dfDirectory->Clear();
auto clone = static_cast<TH1D *>(h->Clone());
clone->SetDirectory(nullptr);
return; // in case root -b was invoked
clone->Draw();
}
Date
September 2017

Definition in file df013_InspectAnalysis.C.

l
auto * l
Definition: textangle.C:4
TBrowser
Using a TBrowser one can browse all ROOT objects.
Definition: TBrowser.h:37
r
ROOT::R::TRInterface & r
Definition: Object.C:4
TH1D
1-D histogram with a double per channel (see TH1 documentation)}
Definition: TH1.h:618
TMemFile
A TMemFile is like a normal TFile except that it reads and writes only from memory.
Definition: TMemFile.h:19
ROOT::RDataFrame
ROOT's RDataFrame offers a high level interface for analyses of data stored in TTree,...
Definition: RDataFrame.hxx:42
ROOT::EnableImplicitMT
Enable ROOT's implicit multi-threading for all objects and methods that provide an internal paralleli...
Definition: TROOT.cxx:524
TRandom
This is the base class for the ROOT Random number generators.
Definition: TRandom.h:27
h
#define h(i)
Definition: RSha256.hxx:106
Returns the size of ROOT's thread pool.
Definition: TROOT.cxx:562
TH1::SetDirectory
virtual void SetDirectory(TDirectory *dir)
By default when an histogram is created, it is added to the list of histogram objects in the current ...
Definition: TH1.cxx:8771
gSystem
R__EXTERN TSystem * gSystem
Definition: TSystem.h:559
d
#define d(i)
Definition: RSha256.hxx:102