Files
geant4/source/tasking/src/G4TaskRunManager.cc
T
2021-06-25 16:12:29 +02:00

797 lines
23 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
//
#include "G4TaskRunManager.hh"
#include "G4AutoLock.hh"
#include "G4EnvironmentUtils.hh"
#include "G4ProductionCutsTable.hh"
#include "G4Run.hh"
#include "G4ScoringManager.hh"
#include "G4StateManager.hh"
#include "G4Task.hh"
#include "G4TaskGroup.hh"
#include "G4TaskManager.hh"
#include "G4TaskRunManagerKernel.hh"
#include "G4ThreadPool.hh"
#include "G4Threading.hh"
#include "G4TiMemory.hh"
#include "G4Timer.hh"
#include "G4TransportationManager.hh"
#include "G4UImanager.hh"
#include "G4UserRunAction.hh"
#include "G4UserTaskInitialization.hh"
#include "G4UserTaskThreadInitialization.hh"
#include "G4VUserActionInitialization.hh"
#include "G4WorkerTaskRunManager.hh"
#include "G4WorkerThread.hh"
#include "G4UserTaskQueue.hh"
#include "G4TiMemory.hh"
#include "G4ThreadLocalSingleton.hh"
#include <cstdlib>
#include <cstring>
#include <iterator>
//============================================================================//
namespace
{
G4Mutex scorerMergerMutex;
G4Mutex runMergerMutex;
G4Mutex setUpEventMutex;
} // namespace
//============================================================================//
G4TaskRunManagerKernel* G4TaskRunManager::GetMTMasterRunManagerKernel()
{
return GetMasterRunManager()->MTkernel;
}
//============================================================================//
G4TaskRunManager::G4TaskRunManager(G4VUserTaskQueue* task_queue, G4bool useTBB,
G4int grainsize)
: G4MTRunManager()
, PTL::TaskRunManager(useTBB)
, eventGrainsize(grainsize)
, numberOfEventsPerTask(-1)
, numberOfTasks(-1)
, masterRNGEngine(nullptr)
, workTaskGroup(nullptr)
{
if(task_queue)
taskQueue = task_queue;
// override default of 2 from G4MTRunManager
nworkers = G4Threading::G4GetNumberOfCores();
fMasterRM = this;
MTkernel = static_cast<G4TaskRunManagerKernel*>(kernel);
G4int numberOfStaticAllocators = kernel->GetNumberOfStaticAllocators();
if(numberOfStaticAllocators > 0)
{
G4ExceptionDescription msg1;
msg1 << "There are " << numberOfStaticAllocators
<< " static G4Allocator objects detected.\n"
<< "In multi-threaded mode, all G4Allocator objects must "
<< "be dynamicly instantiated.";
G4Exception("G4TaskRunManager::G4TaskRunManager", "Run1035", FatalException,
msg1);
}
G4UImanager::GetUIpointer()->SetMasterUIManager(true);
masterScM = G4ScoringManager::GetScoringManagerIfExist();
// use default RandomNumberGenerator if created by user, or create default
masterRNGEngine = G4Random::getTheEngine();
numberOfEventToBeProcessed = 0;
randDbl = new G4double[nSeedsPerEvent * nSeedsMax];
//------------------------------------------------------------------------//
// handle threading
//------------------------------------------------------------------------//
G4String _nthread_env = G4GetEnv<G4String>("G4FORCENUMBEROFTHREADS", "");
for(auto& itr : _nthread_env)
itr = tolower(itr);
if(_nthread_env == "max")
forcedNwokers = G4Threading::G4GetNumberOfCores();
else if(!_nthread_env.empty())
{
std::stringstream ss;
G4int _nthread_val = -1;
ss << _nthread_env;
ss >> _nthread_val;
if(_nthread_val > 0)
forcedNwokers = _nthread_val;
if(forcedNwokers > 0)
nworkers = forcedNwokers;
}
//------------------------------------------------------------------------//
// option for forcing TBB
//------------------------------------------------------------------------//
#ifdef GEANT4_USE_TBB
G4int _useTBB = G4GetEnv<G4int>("G4FORCE_TBB", (G4int) useTBB);
if(_useTBB > 0)
useTBB = true;
#else
if(useTBB)
{
G4ExceptionDescription msg;
msg << "TBB was requested but Geant4 was not built with TBB support";
G4Exception("G4TaskRunManager::G4TaskRunManager(...)", "Run0131",
JustWarning, msg);
}
useTBB = false;
#endif
// handle TBB
G4ThreadPool::set_use_tbb(useTBB);
}
//============================================================================//
G4TaskRunManager::G4TaskRunManager(G4bool useTBB)
: G4TaskRunManager(nullptr, useTBB, 0)
{}
//============================================================================//
G4TaskRunManager::~G4TaskRunManager()
{
// finalize profiler before shutting down the threads
G4Profiler::Finalize();
// terminate all the workers
G4TaskRunManager::TerminateWorkers();
// trigger all G4AutoDelete instances
G4ThreadLocalSingleton<void>::Clear();
// delete the task-group
delete workTaskGroup;
workTaskGroup = nullptr;
// destroy the thread-pool
if(threadPool)
threadPool->destroy_threadpool();
PTL::TaskRunManager::Terminate();
}
//============================================================================//
G4ThreadId G4TaskRunManager::GetMasterThreadId()
{
return G4MTRunManager::GetMasterThreadId();
}
//============================================================================//
void G4TaskRunManager::StoreRNGStatus(const G4String& fn)
{
std::ostringstream os;
os << randomNumberStatusDir << "G4Master_" << fn << ".rndm";
G4Random::saveEngineStatus(os.str().c_str());
}
//============================================================================//
void G4TaskRunManager::SetNumberOfThreads(G4int n)
{
if(forcedNwokers > 0)
{
std::stringstream ss;
ss << "\n### Number of threads is forced to " << forcedNwokers
<< " by G4FORCENUMBEROFTHREADS environment variable. G4TaskRunManager::"
<< __FUNCTION__ << "(" << n << ") ignored ###";
if(verboseLevel > 1)
{
G4ExceptionDescription msg;
msg << ss.str();
G4Exception("G4TaskRunManager::SetNumberOfThreads(G4int)", "Run0132",
JustWarning, msg);
}
else
{
G4cout << ss.str() << "\n" << G4endl;
}
nworkers = forcedNwokers;
}
else
{
nworkers = n;
if(poolInitialized)
{
std::stringstream ss;
ss << "\n### Thread-pool already initialized. Resizing to " << nworkers
<< "threads ###";
G4cout << ss.str() << "\n" << G4endl;
GetThreadPool()->resize(n);
}
}
}
//============================================================================//
void G4TaskRunManager::Initialize()
{
G4bool firstTime = (!threadPool);
if(firstTime)
InitializeThreadPool();
G4RunManager::Initialize();
// make sure all worker threads are set up.
G4RunManager::BeamOn(0);
if(firstTime)
G4RunManager::SetRunIDCounter(0);
// G4UImanager::GetUIpointer()->SetIgnoreCmdNotFound(true);
}
//============================================================================//
void G4TaskRunManager::InitializeThreadPool()
{
if(poolInitialized && threadPool && workTaskGroup)
{
G4Exception("G4TaskRunManager::InitializeThreadPool", "Run1040",
JustWarning, "Threadpool already initialized. Ignoring...");
return;
}
std::stringstream ss;
ss.fill('=');
ss << std::setw(90) << "";
G4cout << "\n" << ss.str() << G4endl;
PTL::TaskRunManager::SetVerbose(GetVerbose());
PTL::TaskRunManager::Initialize(nworkers);
// create the joiners
if(threadPool->is_tbb_threadpool())
{
G4cout << "G4TaskRunManager :: Using TBB..." << G4endl;
}
else
{
G4cout << "G4TaskRunManager :: Using G4ThreadPool..." << G4endl;
}
if(!workTaskGroup)
workTaskGroup = new RunTaskGroup(threadPool);
G4cout << ss.str() << "\n" << G4endl;
}
//============================================================================//
void G4TaskRunManager::ProcessOneEvent(G4int)
{
// Nothing to do
}
//============================================================================//
void G4TaskRunManager::TerminateOneEvent()
{
// Nothing to do
}
//============================================================================//
void G4TaskRunManager::ComputeNumberOfTasks()
{
G4int grainSize = (eventGrainsize == 0) ? threadPool->size() : eventGrainsize;
grainSize =
G4GetEnv<G4int>("G4FORCE_GRAINSIZE", grainSize, "Forcing grainsize...");
if(grainSize == 0)
grainSize = 1;
G4int nEvtsPerTask = (numberOfEventToBeProcessed > grainSize)
? (numberOfEventToBeProcessed / grainSize)
: 1;
if(eventModuloDef > 0)
{
eventModulo = eventModuloDef;
}
else
{
eventModulo = G4int(std::sqrt(G4double(numberOfEventToBeProcessed)));
if(eventModulo < 1)
eventModulo = 1;
}
if(eventModulo > nEvtsPerTask)
{
G4int oldMod = eventModulo;
eventModulo = nEvtsPerTask;
G4ExceptionDescription msgd;
msgd << "Event modulo is reduced to " << eventModulo << " (was " << oldMod
<< ")"
<< " to distribute events to all threads.";
G4Exception("G4TaskRunManager::InitializeEventLoop()", "Run10035",
JustWarning, msgd);
}
nEvtsPerTask = eventModulo;
if(fakeRun)
nEvtsPerTask = G4GetEnv<G4int>(
"G4FORCE_EVENTS_PER_TASK", nEvtsPerTask,
"Forcing number of events per task (overrides grainsize)...");
else
nEvtsPerTask = G4GetEnv<G4int>("G4FORCE_EVENTS_PER_TASK", nEvtsPerTask);
if(nEvtsPerTask < 1)
nEvtsPerTask = 1;
numberOfTasks = numberOfEventToBeProcessed / nEvtsPerTask;
numberOfEventsPerTask = nEvtsPerTask;
eventModulo = numberOfEventsPerTask;
if(fakeRun && verboseLevel > 1)
{
std::stringstream msg;
msg << "--> G4TaskRunManager::ComputeNumberOfTasks() --> " << numberOfTasks
<< " tasks with " << numberOfEventsPerTask << " events/task...";
std::stringstream ss;
ss.fill('=');
ss << std::setw(msg.str().length()) << "";
G4cout << "\n"
<< ss.str() << "\n"
<< msg.str() << "\n"
<< ss.str() << "\n"
<< G4endl;
}
}
//============================================================================//
void G4TaskRunManager::CreateAndStartWorkers()
{
// Now loop on requested number of workers
// This will also start the workers
// Currently we do not allow to change the
// number of threads: threads area created once
// Instead of pthread based workers, create tbbTask
static bool initializeStarted = false;
ComputeNumberOfTasks();
if(fakeRun)
{
if(initializeStarted)
{
auto initCmdStack = GetCommandStack();
if(!initCmdStack.empty())
{
threadPool->execute_on_all_threads([initCmdStack]() {
for(auto& itr : initCmdStack)
G4UImanager::GetUIpointer()->ApplyCommand(itr);
G4WorkerTaskRunManager::GetWorkerRunManager()->DoWork();
});
}
}
else
{
std::stringstream msg;
msg << "--> G4TaskRunManager::CreateAndStartWorkers() --> "
<< "Initializing workers...";
std::stringstream ss;
ss.fill('=');
ss << std::setw(msg.str().length()) << "";
G4cout << "\n"
<< ss.str() << "\n"
<< msg.str() << "\n"
<< ss.str() << "\n"
<< G4endl;
G4TaskRunManagerKernel::InitCommandStack() = GetCommandStack();
threadPool->execute_on_all_threads(
[]() { G4TaskRunManagerKernel::InitializeWorker(); });
}
initializeStarted = true;
}
else
{
auto initCmdStack = GetCommandStack();
if(!initCmdStack.empty())
{
threadPool->execute_on_all_threads([initCmdStack]() {
for(auto& itr : initCmdStack)
G4UImanager::GetUIpointer()->ApplyCommand(itr);
});
}
// cleans up a previous run and events in case a thread
// does not execute any tasks
threadPool->execute_on_all_threads(
[]() { G4TaskRunManagerKernel::ExecuteWorkerInit(); });
{
std::stringstream msg;
msg << "--> G4TaskRunManager::CreateAndStartWorkers() --> "
<< "Creating " << numberOfTasks << " tasks with "
<< numberOfEventsPerTask << " events/task...";
std::stringstream ss;
ss.fill('=');
ss << std::setw(msg.str().length()) << "";
G4cout << "\n"
<< ss.str() << "\n"
<< msg.str() << "\n"
<< ss.str() << "\n"
<< G4endl;
}
G4int remaining = numberOfEventToBeProcessed;
for(G4int nt = 0; nt < numberOfTasks + 1; ++nt)
{
if(remaining > 0)
AddEventTask(nt);
remaining -= numberOfEventsPerTask;
}
workTaskGroup->wait();
}
}
//============================================================================//
void G4TaskRunManager::AddEventTask(G4int nt)
{
if(verboseLevel > 1)
G4cout << "Adding task " << nt << " to task-group..." << G4endl;
workTaskGroup->exec([]() { G4TaskRunManagerKernel::ExecuteWorkerTask(); });
}
//============================================================================//
void G4TaskRunManager::RefillSeeds()
{
G4RNGHelper* helper = G4RNGHelper::GetInstance();
G4int nFill = 0;
switch(SeedOncePerCommunication())
{
case 0:
nFill = numberOfEventToBeProcessed - nSeedsFilled;
break;
case 1:
nFill = numberOfEventsPerTask - nSeedsFilled;
break;
case 2:
default:
nFill = (numberOfEventToBeProcessed - nSeedsFilled * eventModulo) /
eventModulo +
1;
}
// Generates up to nSeedsMax seed pairs only.
if(nFill > nSeedsMax)
nFill = nSeedsMax;
masterRNGEngine->flatArray(nSeedsPerEvent * nFill, randDbl);
helper->Refill(randDbl, nFill);
nSeedsFilled += nFill;
}
//============================================================================//
void G4TaskRunManager::InitializeEventLoop(G4int n_event, const char* macroFile,
G4int n_select)
{
MTkernel->SetUpDecayChannels();
numberOfEventToBeProcessed = n_event;
numberOfEventProcessed = 0;
if(!fakeRun)
{
nSeedsUsed = 0;
nSeedsFilled = 0;
if(verboseLevel > 0)
timer->Start();
n_select_msg = n_select;
if(macroFile != nullptr)
{
if(n_select_msg < 0)
n_select_msg = n_event;
msgText = "/control/execute ";
msgText += macroFile;
selectMacro = macroFile;
}
else
{
n_select_msg = -1;
selectMacro = "";
}
ComputeNumberOfTasks();
// initialize seeds
// If user did not implement InitializeSeeds,
// use default: nSeedsPerEvent seeds per event
if(n_event > 0)
{
G4bool _overload = InitializeSeeds(n_event);
G4bool _functor = false;
if(!_overload)
_functor = initSeedsCallback(n_event, nSeedsPerEvent, nSeedsFilled);
if(_overload == false && _functor == false)
{
G4RNGHelper* helper = G4RNGHelper::GetInstance();
switch(SeedOncePerCommunication())
{
case 0:
nSeedsFilled = n_event;
break;
case 1:
nSeedsFilled = numberOfEventsPerTask;
break;
case 2:
nSeedsFilled = n_event / eventModulo + 1;
break;
default:
G4ExceptionDescription msgd;
msgd << "Parameter value <" << SeedOncePerCommunication()
<< "> of seedOncePerCommunication is invalid. It is reset "
"to 0.";
G4Exception("G4TaskRunManager::InitializeEventLoop()", "Run10036",
JustWarning, msgd);
SetSeedOncePerCommunication(0);
nSeedsFilled = n_event;
}
// Generates up to nSeedsMax seed pairs only.
if(nSeedsFilled > nSeedsMax)
nSeedsFilled = nSeedsMax;
masterRNGEngine->flatArray(nSeedsPerEvent * nSeedsFilled, randDbl);
helper->Fill(randDbl, nSeedsFilled, n_event, nSeedsPerEvent);
}
}
}
// Now initialize workers. Check if user defined a WorkerThreadInitialization
if(userWorkerThreadInitialization == nullptr)
userWorkerThreadInitialization = new G4UserTaskThreadInitialization();
// Prepare UI commands for threads
PrepareCommandsStack();
// Start worker threads
CreateAndStartWorkers();
}
//============================================================================//
void G4TaskRunManager::RunTermination()
{
// Wait for all worker threads to have finished the run
// i.e. wait for them to return from RunTermination()
// This guarantee that userrunaction for workers has been called
// Wait now for all threads to finish event-loop
WaitForEndEventLoopWorkers();
// Now call base-class methof
G4RunManager::TerminateEventLoop();
G4RunManager::RunTermination();
}
//============================================================================//
void G4TaskRunManager::ConstructScoringWorlds()
{
masterScM = G4ScoringManager::GetScoringManagerIfExist();
// Call base class stuff...
G4RunManager::ConstructScoringWorlds();
masterWorlds.clear();
size_t nWorlds =
G4TransportationManager::GetTransportationManager()->GetNoWorlds();
std::vector<G4VPhysicalVolume*>::iterator itrW =
G4TransportationManager::GetTransportationManager()->GetWorldsIterator();
for(size_t iWorld = 0; iWorld < nWorlds; ++iWorld)
{
addWorld(iWorld, *itrW);
++itrW;
}
}
//============================================================================//
void G4TaskRunManager::MergeScores(const G4ScoringManager* localScoringManager)
{
G4AutoLock l(&scorerMergerMutex);
if(masterScM)
masterScM->Merge(localScoringManager);
}
//============================================================================//
void G4TaskRunManager::MergeRun(const G4Run* localRun)
{
G4AutoLock l(&runMergerMutex);
if(currentRun)
currentRun->Merge(localRun);
}
//============================================================================//
G4bool G4TaskRunManager::SetUpAnEvent(G4Event* evt, G4long& s1, G4long& s2,
G4long& s3, G4bool reseedRequired)
{
G4AutoLock l(&setUpEventMutex);
if(numberOfEventProcessed < numberOfEventToBeProcessed)
{
evt->SetEventID(numberOfEventProcessed);
if(reseedRequired)
{
G4RNGHelper* helper = G4RNGHelper::GetInstance();
G4int idx_rndm = nSeedsPerEvent * nSeedsUsed;
s1 = helper->GetSeed(idx_rndm);
s2 = helper->GetSeed(idx_rndm + 1);
if(nSeedsPerEvent == 3)
s3 = helper->GetSeed(idx_rndm + 2);
++nSeedsUsed;
if(nSeedsUsed == nSeedsFilled)
RefillSeeds();
}
numberOfEventProcessed++;
return true;
}
return false;
}
//============================================================================//
G4int G4TaskRunManager::SetUpNEvents(G4Event* evt, G4SeedsQueue* seedsQueue,
G4bool reseedRequired)
{
G4AutoLock l(&setUpEventMutex);
if(numberOfEventProcessed < numberOfEventToBeProcessed && !runAborted)
{
G4int nevt = numberOfEventsPerTask;
G4int nmod = eventModulo;
if(numberOfEventProcessed + nevt > numberOfEventToBeProcessed)
{
nevt = numberOfEventToBeProcessed - numberOfEventProcessed;
nmod = numberOfEventToBeProcessed - numberOfEventProcessed;
}
evt->SetEventID(numberOfEventProcessed);
if(reseedRequired)
{
G4RNGHelper* helper = G4RNGHelper::GetInstance();
G4int nevRnd = nmod;
if(SeedOncePerCommunication() > 0)
nevRnd = 1;
for(G4int i = 0; i < nevRnd; ++i)
{
seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed));
seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed + 1));
if(nSeedsPerEvent == 3)
seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed + 2));
nSeedsUsed++;
if(nSeedsUsed == nSeedsFilled)
RefillSeeds();
}
}
numberOfEventProcessed += nevt;
return nevt;
}
return 0;
}
//============================================================================//
void G4TaskRunManager::TerminateWorkers()
{
// Force workers to execute (if any) all UI commands left in the stack
RequestWorkersProcessCommandsStack();
if(workTaskGroup)
{
workTaskGroup->join();
if(!fakeRun)
threadPool->execute_on_all_threads(
[]() { G4TaskRunManagerKernel::TerminateWorker(); });
}
}
//============================================================================//
void G4TaskRunManager::AbortRun(G4bool softAbort)
{
// This method is valid only for GeomClosed or EventProc state
G4ApplicationState currentState =
G4StateManager::GetStateManager()->GetCurrentState();
if(currentState == G4State_GeomClosed || currentState == G4State_EventProc)
{
runAborted = true;
MTkernel->BroadcastAbortRun(softAbort);
}
else
{
G4cerr << "Run is not in progress. AbortRun() ignored." << G4endl;
}
}
//============================================================================//
void G4TaskRunManager::AbortEvent()
{
// nothing to do in the master thread
}
//============================================================================//
void G4TaskRunManager::WaitForEndEventLoopWorkers()
{
if(workTaskGroup)
{
workTaskGroup->join();
if(!fakeRun)
threadPool->execute_on_all_threads(
[]() { G4TaskRunManagerKernel::TerminateWorkerRunEventLoop(); });
}
}
//============================================================================//
void G4TaskRunManager::RequestWorkersProcessCommandsStack()
{
PrepareCommandsStack();
auto process_commands_stack = []() {
G4MTRunManager* mrm = G4MTRunManager::GetMasterRunManager();
if(mrm)
{
auto cmds = mrm->GetCommandStack();
for(const auto& itr : cmds)
G4UImanager::GetUIpointer()->ApplyCommand(itr); // TLS instance
mrm->ThisWorkerProcessCommandsStackDone();
}
};
if(threadPool)
threadPool->execute_on_all_threads(process_commands_stack);
}
//============================================================================//
void G4TaskRunManager::ThisWorkerProcessCommandsStackDone() {}
//============================================================================//