Mt 1 0 1_Fill Ntuples

Fill n-tuples in distinct workers. This tutorial illustrates the basics of how it's possible with ROOT to offload heavy operations on multiple threads and how it's possible to write simultaneously multiple files. The operation performed in this case is the creation of random gaussian numbers. NOTE: this code can be executed in a macro, ACLiC'ed or not, but not yet at the command line prompt.

Author: Danilo Piparo
This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Thursday, February 25, 2021 at 09:33 AM.

Some useful constants and functions

Total amount of numbers

In [1]:
const UInt_t nNumbers = 20000000U;

The number of workers

In [2]:
const UInt_t nWorkers = 4U;

We split the work in equal parts

In [3]:
const auto workSize = nNumbers / nWorkers;

A simple function to fill ntuples randomly

In [4]:
%%cpp -d
void fillRandom(TNtuple &ntuple, TRandom3 &rndm, UInt_t n)
{
   for (auto i : ROOT::TSeqI(n))
      ntuple.Fill(rndm.Gaus());
}

No nuisance for batch execution

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

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

Create a random generator and and ntuple to hold the numbers

In [6]:
TRandom3 rndm(1);
TFile ofile("mt101_singleCore.root", "RECREATE");
TNtuple randomNumbers("singleCore", "Random Numbers", "r");
fillRandom(randomNumbers, rndm, nNumbers);
randomNumbers.Write();
ofile.Close();

We now go mt! ------------------------------------------------------------

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

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

We define our work item

In [8]:
auto workItem = [](UInt_t workerID) {
   // One generator, file and ntuple per worker
   TRandom3 workerRndm(workerID); // Change the seed
   TFile ofile(Form("mt101_multiCore_%u.root", workerID), "RECREATE");
   TNtuple workerRandomNumbers("multiCore", "Random Numbers", "r");
   fillRandom(workerRandomNumbers, workerRndm, workSize);
   workerRandomNumbers.Write();
   return 0;
};

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

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

Fill the "pool" with workers

In [10]:
for (auto workerID : ROOT::TSeqI(nWorkers)) {
   workers.emplace_back(workItem, workerID);
}

Now join them

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

return 0;

Draw all canvases

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