Skip to content

Commit f0fd35f

Browse files
sawenzelalcaliva
authored andcommitted
HybridGenerator: Asyncronous + parallel event generation (#13788)
This introduces: * asyncronous event generation * possibility for parallel event generation This is useful for: * hiding latency (IO) of certain generators * decoupling the actual work from the call sequence into HybridGenerator * collaboration from multiple clones of the same generator to generate a certain number of events The implementation relies on tbb::task_arena and input/output queues for decoupling the task_arena from the HybridGenerator thread. An example is added for parallel event gen. In addition, this commit has the following changes: - Small adjustments to seeding of Pythia8 in order to avoid same seeds in multiple parallel Pythia instances. - possibility to init external generator from an INI file (typically done or available in O2DPG) - use shared_ptr instead of unique_ptr to keep generators in order to avoid lifetime problems with async processing - preparion to run underlying generator Init() functions in async way (not active yet; needs checks if generators are thread safe) (cherry picked from commit f4d9b9c)
1 parent 8efa5d8 commit f0fd35f

15 files changed

+419
-54
lines changed

Generators/include/Generators/Generator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ class Generator : public FairGenerator
141141
/** lorentz boost data members **/
142142
Double_t mBoost;
143143

144+
// a unique generator instance counter
145+
// this can be used to make sure no two generator instances have the same seed etc.
146+
static std::atomic<int> InstanceCounter;
147+
int mThisInstanceID = 0;
148+
144149
private:
145150
void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const;
146151

Generators/include/Generators/GeneratorExternalParam.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct GeneratorExternalParam : public o2::conf::ConfigurableParamHelper<Generat
3737
struct ExternalGenConfig {
3838
std::string fileName = "";
3939
std::string funcName = "";
40+
std::string iniFile = ""; // if ini file is given, the configuration will be taken from this and the other 2 fields neglected
4041
};
4142

4243
} // end namespace eventgen

Generators/include/Generators/GeneratorHybrid.h

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@
3939
#include <rapidjson/writer.h>
4040
#include "TBufferJSON.h"
4141

42+
#include <tbb/concurrent_queue.h>
43+
#include <tbb/task_arena.h>
44+
#include <iostream>
45+
#include <thread>
46+
#include <atomic>
47+
4248
namespace o2
4349
{
4450
namespace eventgen
@@ -50,25 +56,29 @@ class GeneratorHybrid : public Generator
5056
public:
5157
GeneratorHybrid() = default;
5258
GeneratorHybrid(const std::string& inputgens);
53-
~GeneratorHybrid() = default;
59+
~GeneratorHybrid();
5460

5561
Bool_t Init() override;
5662
Bool_t generateEvent() override;
5763
Bool_t importParticles() override;
5864

65+
void setNEvents(int n) { mNEvents = n; }
66+
5967
Bool_t parseJSON(const std::string& path);
6068
template <typename T>
6169
std::string jsonValueToString(const T& value);
6270

6371
private:
6472
o2::eventgen::Generator* currentgen = nullptr;
65-
std::vector<std::unique_ptr<o2::eventgen::Generator>> gens;
73+
std::vector<std::shared_ptr<o2::eventgen::Generator>> gens;
6674
const std::vector<std::string> generatorNames = {"extkinO2", "evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"};
6775
std::vector<std::string> mInputGens;
6876
std::vector<std::string> mGens;
6977
std::vector<std::string> mConfigs;
7078
std::vector<std::string> mConfsPythia8;
7179

80+
std::vector<bool> mGenIsInitialized;
81+
7282
// Parameters configurations
7383
std::vector<std::unique_ptr<o2::eventgen::BoxGenConfig>> mBoxGenConfigs;
7484
std::vector<std::unique_ptr<o2::eventgen::Pythia8GenConfig>> mPythia8GenConfigs;
@@ -84,6 +94,28 @@ class GeneratorHybrid : public Generator
8494
int mseqCounter = 0;
8595
int mCurrentFraction = 0;
8696
int mIndex = 0;
97+
int mEventCounter = 0;
98+
int mTasksStarted = 0;
99+
100+
// Create a task arena with a specified number of threads
101+
std::thread mTBBTaskPoolRunner;
102+
tbb::concurrent_bounded_queue<int> mInputTaskQueue;
103+
std::vector<tbb::concurrent_bounded_queue<int>> mResultQueue;
104+
tbb::task_arena mTaskArena;
105+
std::atomic<bool> mStopFlag;
106+
bool mIsInitialized = false;
107+
108+
int mNEvents = -1; // the number of events to be done, if known (helps initiating cleanup)
109+
110+
enum class GenMode {
111+
kSeq,
112+
kParallel
113+
};
114+
115+
// hybrid gen operation mode - should be either 'sequential' or 'parallel'
116+
// parallel means that we have clones of the same generator collaborating on event generation
117+
// sequential means that events will be produced in the order given by fractions; async processing is still happening
118+
GenMode mGenerationMode = GenMode::kSeq; //!
87119
};
88120

89121
} // namespace eventgen

Generators/include/Generators/GeneratorHybridParam.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace eventgen
3131
struct GeneratorHybridParam : public o2::conf::ConfigurableParamHelper<GeneratorHybridParam> {
3232
std::string configFile = ""; // JSON configuration file for the generators
3333
bool randomize = false; // randomize the order of the generators, if not generator using fractions
34+
int num_workers = 1; // number of threads available for asyn/parallel event generation
3435
O2ParamDef(GeneratorHybridParam, "GeneratorHybrid");
3536
};
3637

Generators/include/Generators/GeneratorPythia8.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ class GeneratorPythia8 : public Generator
287287
// Value of -1 means unitialized; 0 will be time-dependent and values >1 <= MAX_SEED concrete reproducible seeding
288288
Pythia8GenConfig mGenConfig; // configuration object
289289

290+
static std::atomic<int> Pythia8InstanceCounter;
291+
int mThisPythia8InstanceID = 0;
292+
290293
constexpr static long MAX_SEED = 900000000;
291294

292295
ClassDefOverride(GeneratorPythia8, 1);

Generators/src/Generator.cxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@ namespace o2
2828
namespace eventgen
2929
{
3030

31+
std::atomic<int> Generator::InstanceCounter{0};
32+
3133
/*****************************************************************/
3234
/*****************************************************************/
3335

3436
Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"),
3537
mBoost(0.)
3638
{
3739
/** default constructor **/
40+
mThisInstanceID = Generator::InstanceCounter;
41+
Generator::InstanceCounter++;
3842
}
3943

4044
/*****************************************************************/
@@ -43,6 +47,8 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na
4347
mBoost(0.)
4448
{
4549
/** constructor **/
50+
mThisInstanceID = Generator::InstanceCounter;
51+
Generator::InstanceCounter++;
4652
}
4753

4854
/*****************************************************************/

Generators/src/GeneratorFactory.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair
285285
return;
286286
}
287287
auto hybrid = new o2::eventgen::GeneratorHybrid(config);
288+
hybrid->setNEvents(conf.getNEvents());
288289
primGen->AddGenerator(hybrid);
289290
#endif
290291
} else {

0 commit comments

Comments
 (0)