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>
auto myCanvas = std::make_unique<TCanvas>(
"myCanvas",
"myCanvas", 1000, 500);
void df042_ThreadSafeRNG()
{
auto h1 =
df1.Histo1D({
"h1",
"Single thread (no MT)", 1000, -4, 4}, {
"x"});
unsigned int nSlots{std::max(2u, std::thread::hardware_concurrency() / 4u)};
auto h2 =
df2.Histo1D({
"h2",
"Thread-safe (MT, non-deterministic)", 1000, -4, 4}, {
"x"});
h2->Draw();
auto h3 =
df3.Histo1D({
"h3",
"Thread-safe (MT, deterministic)", 1000, -4, 4}, {
"x"});
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() <<
" +- "
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).
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.
void Draw(Option_t *option="") override
Draw this histogram with options.
double GetNormallyDistributedNumberPerSlotGeneratorForEntry(unsigned int slot, unsigned long long entry)
double GetNormallyDistributedNumberFromGlobalGenerator()
void ReinitializeGenerators(unsigned int nSlots)
double GetNormallyDistributedNumberPerSlotGenerator(unsigned int slot)
void EnableImplicitMT(UInt_t numthreads=0)
Enable ROOT's implicit multi-threading for all objects and methods that provide an internal paralleli...
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.