mt102_readNtuplesFillHistosAndFit

Read n-tuples in distinct workers, fill histograms, merge them and fit. Knowing that other facilities like TProcessExecutor might be more adequate for this operation, this tutorial complements mc101, reading and merging. We convey another message with this tutorial: the synergy of ROOT and STL algorithms is possible.

Author: Danilo Piparo
This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Sunday, October 02, 2022 at 09:33 AM.

No nuisance for batch execution

In [1]:
gROOT->SetBatch();

Perform the operation sequentially ---------------------------------------

In [2]:
TChain inputChain("multiCore");
inputChain.Add("mt101_multiCore_*.root");
TH1F outHisto("outHisto", "Random Numbers", 128, -4, 4);
inputChain.Draw("r >> outHisto");
outHisto.Fit("gaus");
Warning in <Fit>: Fit data is empty 

We now go MT! ------------------------------------------------------------

The first, fundamental operation to be performed in order to make ROOT thread-aware.

In [3]:
ROOT::EnableThreadSafety();

We adapt our parallelisation to the number of input files

In [4]:
const auto nFiles = inputChain.GetListOfFiles()->GetEntries();

We define the histograms we'll fill

In [5]:
std::vector<TH1F> histograms;
auto workerIDs = ROOT::TSeqI(nFiles);
histograms.reserve(nFiles);
for (auto workerID : workerIDs) {
   histograms.emplace_back(TH1F(Form("outHisto_%u", workerID), "Random Numbers", 128, -4, 4));
}

We define our work item

In [6]:
auto workItem = [&histograms](UInt_t workerID) {
   TFile f(Form("mt101_multiCore_%u.root", workerID));
   auto ntuple = f.Get<TNtuple>("multiCore");
   auto &histo = histograms.at(workerID);
   for (auto index : ROOT::TSeqL(ntuple->GetEntriesFast())) {
      ntuple->GetEntry(index);
      histo.Fill(ntuple->GetArgs()[0]);
   }
};

TH1F sumHistogram("SumHisto", "Random Numbers", 128, -4, 4);
input_line_105:2:20: error: 'histograms' cannot be captured because it does not have automatic storage duration
 auto workItem = [&histograms](UInt_t workerID) {
                   ^
input_line_104:2:20: note: 'histograms' declared here
 std::vector<TH1F> histograms;
                   ^

Create the collection which will hold the threads, our "pool"

In [7]:
std::vector<std::thread> workers;

Spawn workers Fill the "pool" with workers

In [8]:
for (auto workerID : workerIDs) {
   workers.emplace_back(workItem, workerID);
}

Now join them

In [9]:
for (auto &&worker : workers)
   worker.join();

And reduce with a simple lambda

In [10]:
std::for_each(std::begin(histograms), std::end(histograms),
              [&sumHistogram](const TH1F &h) { sumHistogram.Add(&h); });

sumHistogram.Fit("gaus", 0);

return 0;
input_line_109:3:17: error: use of undeclared identifier 'sumHistogram'
              [&sumHistogram](const TH1F &h) { sumHistogram.Add(&h); });
                ^
input_line_109:3:48: error: use of undeclared identifier 'sumHistogram'
              [&sumHistogram](const TH1F &h) { sumHistogram.Add(&h); });
                                               ^
input_line_109:5:1: error: use of undeclared identifier 'sumHistogram'
sumHistogram.Fit("gaus", 0);
^

Draw all canvases

In [11]:
gROOT->GetListOfCanvases()->Draw()