Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
df042_ThreadSafeRNG.hxx
Go to the documentation of this file.
1#ifndef ROOT_TUTORIALS_ANALYSIS_DATAFRAME_DF042
2#define ROOT_TUTORIALS_ANALYSIS_DATAFRAME_DF042
3
4#include <random>
5
6// NOTE: these globals are intentionally NOT protected by a mutex.
7// This function is only safe to call from a single thread (used as reference).
8inline std::random_device globalRandomDevice{};
9inline std::mt19937 globalGenerator(globalRandomDevice());
10inline std::normal_distribution<double> globalGaus(0., 1.);
11
16
17// One generator per slot — initialized once before the event loop
18// An alternative to these global vectors could be to have thread_local
19// variables within a function scope and then call that function from RDataFrame
20inline std::vector<std::mt19937> generators;
21inline std::vector<std::normal_distribution<double>> gaussians;
22
24{
25 std::random_device rd;
26 generators.resize(nSlots);
27 for (auto &gen : generators)
28 gen.seed(rd());
29 gaussians.resize(nSlots, std::normal_distribution<double>(0., 1.));
30}
31
36
37double GetNormallyDistributedNumberPerSlotGeneratorForEntry(unsigned int slot, unsigned long long entry)
38{
39 // We want to generate a random number distributed according to a normal distribution in a thread-safe way and such
40 // that it is reproducible across different RDataFrame runs, i.e. given the same input to the generator it will
41 // produce the same value. This is one way to do it. It assumes that the input argument represents a unique entry ID,
42 // such that any thread processing an RDataFrame task will see this number once throughout the entire execution of
43 // the computation graph
44 // Calling both `reset` and `seed` methods is fundamental here to ensure reproducibility: without them the same
45 // generator could be seeded by a different entry (depending on which is the first entry ID seen by a thread) or
46 // could be at a different step of the sequence (depending how many entries this particular thread is processing).
47 // Alternatively, if both the generator and the distribution objects were recreated from scratch at every function
48 // call (i.e. by removing the global std::vector stores), then the next two method calls would not be necessary, at
49 // the cost of a possible performance degradation.
50 gaussians[slot].reset();
51 generators[slot].seed(entry);
52 return gaussians[slot](generators[slot]);
53}
54
55#endif // ROOT_TUTORIALS_ANALYSIS_DATAFRAME_DF041
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
std::normal_distribution< double > globalGaus(0., 1.)
double GetNormallyDistributedNumberPerSlotGeneratorForEntry(unsigned int slot, unsigned long long entry)
double GetNormallyDistributedNumberFromGlobalGenerator()
std::mt19937 globalGenerator(globalRandomDevice())
std::vector< std::mt19937 > generators
void ReinitializeGenerators(unsigned int nSlots)
std::random_device globalRandomDevice
double GetNormallyDistributedNumberPerSlotGenerator(unsigned int slot)
std::vector< std::normal_distribution< double > > gaussians