ROOT
master
Reference Guide
Loading...
Searching...
No Matches
df042_ThreadSafeRNG.py
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
import
os
23
24
import
ROOT
25
26
27
def
df042_ThreadSafeRNG():
28
29
header_path =
os.path.join
(str(
ROOT.gROOT.GetTutorialDir
()),
"analysis"
,
"dataframe"
,
"df042_ThreadSafeRNG.hxx"
)
30
if
not
os.path.exists
(header_path):
31
raise
RuntimeError
(f
'Could not find required header file "{header_path}".'
)
32
33
# First, we declare the functions needed by the RDataFrame computation graph to the interpreter
34
if
not
ROOT.gInterpreter.Declare
(f
'#include "{header_path}"'
):
35
raise
RuntimeError
(
"Failed to declare the functions needed by the RDataFrame computation graph."
)
36
37
myCanvas =
ROOT.TCanvas
(
"myCanvas"
,
"myCanvas"
, 1000, 500)
38
myCanvas.Divide
(3, 1)
39
40
nEntries = 10000000
41
42
# 1. Single thread for reference
43
df1 =
ROOT.RDataFrame
(nEntries).Define(
"x"
,
ROOT.GetNormallyDistributedNumberFromGlobalGenerator
)
44
h1 =
df1.Histo1D
((
"h1"
,
"Single thread (no MT)"
, 1000, -4, 4),
"x"
)
45
myCanvas.cd
(1)
46
h1.Draw
()
47
48
# 2. One generator per RDataFrame slot, with random_device seeding
49
# Notes and Caveats:
50
# - How many numbers are drawn from each generator is not deterministic
51
# and the result is not deterministic between runs.
52
nSlots = max(2,
os.cpu_count
() // 4)
53
ROOT.EnableImplicitMT
(nSlots)
54
# Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
55
# be used accordingly during the execution.
56
ROOT.ReinitializeGenerators
(nSlots)
57
df2 =
ROOT.RDataFrame
(nEntries).DefineSlot(
"x"
,
ROOT.GetNormallyDistributedNumberPerSlotGenerator
)
58
h2 =
df2.Histo1D
((
"h2"
,
"Thread-safe (MT, non-deterministic)"
, 1000, -4, 4),
"x"
)
59
myCanvas.cd
(2)
60
h2.Draw
()
61
62
# 3. One generator per RDataFrame slot, with entry seeding
63
# Notes and Caveats:
64
# - With RDataFrame(INTEGER_NUMBER) constructor (as in the example),
65
# the result is deterministic and identical on every run
66
# - With RDataFrame(TTree) constructor, the result is not guaranteed to be deterministic.
67
# To make it deterministic, use something from the dataset to act as the event identifier
68
# instead of rdfentry_, and use it as a seed.
69
# Before running the RDataFrame computation graph, we reinitialize the generators (one per slot), so they can
70
# be used accordingly during the execution.
71
ROOT.ReinitializeGenerators
(nSlots)
72
df3 =
ROOT.RDataFrame
(nEntries).DefineSlotEntry(
"x"
,
ROOT.GetNormallyDistributedNumberPerSlotGeneratorForEntry
)
73
h3 =
df3.Histo1D
((
"h3"
,
"Thread-safe (MT, deterministic)"
, 1000, -4, 4),
"x"
)
74
myCanvas.cd
(3)
75
h3.Draw
()
76
77
print(f
"{'{:<40}'.format('Final distributions')}: Mean +- StdDev"
)
78
print(f
"{'{:<40}'.format('Theoretical')}: 0.000 +- 1.000"
)
79
print(f
"{'{:<40}'.format('Single thread (no MT)')}: {h1.GetMean():.3f} +- {h1.GetStdDev():.3f}"
)
80
print(f
"{'{:<40}'.format('Thread-safe (MT, non-deterministic)')}: {h2.GetMean():.3f} +- {h2.GetStdDev():.3f}"
)
81
print(f
"{'{:<40}'.format('Thread-safe (MT, deterministic)')}: {h3.GetMean():.3f} +- {h3.GetStdDev():.3f}"
)
82
83
# We draw the canvas with block=True to stop the execution before end of the
84
# function and to be able to interact with the canvas until necessary
85
myCanvas.Draw
(block=
True
)
86
87
88
if
__name__ ==
"__main__"
:
89
df042_ThreadSafeRNG()
TRangeDynCast
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Definition
TCollection.h:359
ROOT::Detail::TRangeCast
Definition
TCollection.h:312
ROOT::RDataFrame
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
Definition
RDataFrame.hxx:50
df042_ThreadSafeRNG
Definition
df042_ThreadSafeRNG.py:1
tutorials
analysis
dataframe
df042_ThreadSafeRNG.py
ROOTmaster - Reference Guide Generated on Sun Aug 2 2026 05:07:18 (GVA Time) using Doxygen 1.10.0