Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
df042_ThreadSafeRNG.C File Reference

Detailed Description

View in nbviewer Open in SWAN
Usage of multithreading mode with random generators.

This example illustrates how to define functions that generate random numbers and use them in an RDataFrame computation graph in a thread-safe way.

Using only one random number generator in an application running with ROOT::EnableImplicitMT() is a common pitfall. This pitfall creates race conditions resulting in a distorted random distribution. In the example, this issue is solved by creating one random number generator per RDataFrame processing slot, thus allowing for parallel and thread-safe access. The example also illustrates the difference between non-deterministic and deterministic random number generation.

#include <algorithm>
#include <iostream>
#include <memory>
#include <thread>
#include <TCanvas.h>
// Canvas that should survive the running of this macro
auto myCanvas = std::make_unique<TCanvas>("myCanvas", "myCanvas", 1000, 500);
void df042_ThreadSafeRNG()
{
myCanvas->Divide(3, 1);
unsigned int nEntries{10000000};
// 1. Single thread for reference
auto h1 = df1.Histo1D({"h1", "Single thread (no MT)", 1000, -4, 4}, {"x"});
myCanvas->cd(1);
h1->Draw();
// 2. One generator per RDataFrame slot, with random_device seeding
// Notes and Caveats:
// - How many numbers are drawn from each generator is not deterministic
// and the result is not deterministic between runs.
unsigned int nSlots{std::max(2u, std::thread::hardware_concurrency() / 4u)};
// Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
// be used accordingly during the execution.
auto h2 = df2.Histo1D({"h2", "Thread-safe (MT, non-deterministic)", 1000, -4, 4}, {"x"});
myCanvas->cd(2);
h2->Draw();
// 3. One generator per RDataFrame slot, with entry seeding
// Notes and Caveats:
// - With RDataFrame(INTEGER_NUMBER) constructor (as in the example),
// the result is deterministic and identical on every run
// - With RDataFrame(TTree) constructor, the result is not guaranteed to be deterministic.
// To make it deterministic, use something from the dataset to act as the event identifier
// instead of rdfentry_, and use it as a seed.
// Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
// be used accordingly during the execution.
auto h3 = df3.Histo1D({"h3", "Thread-safe (MT, deterministic)", 1000, -4, 4}, {"x"});
myCanvas->cd(3);
h3->Draw();
std::cout << std::fixed << std::setprecision(3) << "Final distributions : " << "Mean " << " +- "
<< "StdDev" << std::endl;
std::cout << std::fixed << std::setprecision(3) << "Theoretical : " << "0.000" << " +- "
<< "1.000" << std::endl;
std::cout << std::fixed << std::setprecision(3) << "Single thread (no MT) : " << h1->GetMean() << " +- "
<< h1->GetStdDev() << std::endl;
std::cout << std::fixed << std::setprecision(3) << "Thread-safe (MT, non-deterministic): " << h2->GetMean() << " +- "
<< h2->GetStdDev() << std::endl;
std::cout << std::fixed << std::setprecision(3) << "Thread-safe (MT, deterministic) : " << h3->GetMean() << " +- "
<< h3->GetStdDev() << std::endl;
}
RInterface< Proxied > DefineSlotEntry(std::string_view name, F expression, const ColumnNames_t &columns={})
Define a new column with a value dependent on the processing slot and the current entry.
RInterface< Proxied > DefineSlot(std::string_view name, F expression, const ColumnNames_t &columns={})
Define a new column with a value dependent on the processing slot.
RInterface< Proxied > Define(std::string_view name, F expression, const ColumnNames_t &columns={})
Define a new column.
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
virtual Double_t GetStdDev(Int_t axis=1) const
Returns the Standard Deviation (Sigma).
Definition TH1.cxx:7816
virtual Double_t GetMean(Int_t axis=1) const
For axis = 1,2 or 3 returns the mean value of the histogram along X,Y or Z axis.
Definition TH1.cxx:7744
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3193
double GetNormallyDistributedNumberPerSlotGeneratorForEntry(unsigned int slot, unsigned long long entry)
double GetNormallyDistributedNumberFromGlobalGenerator()
void ReinitializeGenerators(unsigned int nSlots)
double GetNormallyDistributedNumberPerSlotGenerator(unsigned int slot)
TH1F * h1
Definition legend1.C:5
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:613
Final distributions : Mean +- StdDev
Theoretical : 0.000 +- 1.000
Single thread (no MT) : 0.000 +- 0.999
Thread-safe (MT, non-deterministic): -0.000 +- 0.999
Thread-safe (MT, deterministic) : 0.000 +- 1.000
Date
February 2026
Author
Bohdan Dudar (JGU Mainz), Fernando Hueso-González (IFIC, CSIC-UV), Vincenzo Eduardo Padulano (CERN)

Definition in file df042_ThreadSafeRNG.C.