Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
df042_ThreadSafeRNG.C
Go to the documentation of this file.
1/// \file
2/// \ingroup tutorial_dataframe
3/// \notebook -nodraw
4/// Usage of multithreading mode with random generators.
5///
6/// This example illustrates how to define functions that generate random numbers and use them in an RDataFrame
7/// computation graph in a thread-safe way.
8///
9/// Using only one random number generator in an application running with ROOT::EnableImplicitMT() is a common pitfall.
10/// This pitfall creates race conditions resulting in a distorted random distribution. In the example, this issue is
11/// solved by creating one random number generator per RDataFrame processing slot, thus allowing for parallel and
12/// thread-safe access. The example also illustrates the difference between non-deterministic and deterministic random
13/// number generation.
14///
15/// \macro_code
16/// \macro_image
17/// \macro_output
18///
19/// \date February 2026
20/// \author Bohdan Dudar (JGU Mainz), Fernando Hueso-González (IFIC, CSIC-UV), Vincenzo Eduardo Padulano (CERN)
21
22#include <algorithm>
23#include <iostream>
24#include <memory>
25#include <thread>
26#include <TCanvas.h>
27#include <ROOT/RDataFrame.hxx>
28
30
31// Canvas that should survive the running of this macro
32auto myCanvas = std::make_unique<TCanvas>("myCanvas", "myCanvas", 1000, 500);
33
34void df042_ThreadSafeRNG()
35{
36 myCanvas->Divide(3, 1);
37
38 unsigned int nEntries{10000000};
39
40 // 1. Single thread for reference
42 auto h1 = df1.Histo1D({"h1", "Single thread (no MT)", 1000, -4, 4}, {"x"});
43 myCanvas->cd(1);
44 h1->Draw();
45
46 // 2. One generator per RDataFrame slot, with random_device seeding
47 // Notes and Caveats:
48 // - How many numbers are drawn from each generator is not deterministic
49 // and the result is not deterministic between runs.
50 unsigned int nSlots{std::max(2u, std::thread::hardware_concurrency() / 4u)};
52 // Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
53 // be used accordingly during the execution.
56 auto h2 = df2.Histo1D({"h2", "Thread-safe (MT, non-deterministic)", 1000, -4, 4}, {"x"});
57 myCanvas->cd(2);
58 h2->Draw();
59
60 // 3. One generator per RDataFrame slot, with entry seeding
61 // Notes and Caveats:
62 // - With RDataFrame(INTEGER_NUMBER) constructor (as in the example),
63 // the result is deterministic and identical on every run
64 // - With RDataFrame(TTree) constructor, the result is not guaranteed to be deterministic.
65 // To make it deterministic, use something from the dataset to act as the event identifier
66 // instead of rdfentry_, and use it as a seed.
67
68 // Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
69 // be used accordingly during the execution.
72 auto h3 = df3.Histo1D({"h3", "Thread-safe (MT, deterministic)", 1000, -4, 4}, {"x"});
73 myCanvas->cd(3);
74 h3->Draw();
75
76 std::cout << std::fixed << std::setprecision(3) << "Final distributions : " << "Mean " << " +- "
77 << "StdDev" << std::endl;
78 std::cout << std::fixed << std::setprecision(3) << "Theoretical : " << "0.000" << " +- "
79 << "1.000" << std::endl;
80 std::cout << std::fixed << std::setprecision(3) << "Single thread (no MT) : " << h1->GetMean() << " +- "
81 << h1->GetStdDev() << std::endl;
82 std::cout << std::fixed << std::setprecision(3) << "Thread-safe (MT, non-deterministic): " << h2->GetMean() << " +- "
83 << h2->GetStdDev() << std::endl;
84 std::cout << std::fixed << std::setprecision(3) << "Thread-safe (MT, deterministic) : " << h3->GetMean() << " +- "
85 << h3->GetStdDev() << std::endl;
86}
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