Files
geant4/source/tasking/.README.txt
T
2020-06-26 10:23:25 +02:00

359 lines
14 KiB
Plaintext

\hypertarget{geant4-tasking}{%
\section{Geant4 Tasking}\label{geant4-tasking}}
This directory contains a Geant4 run manager which uses a tasking system
for the G4Event loop. This tasking system is fully compatible with TBB
if \texttt{GEANT4\_USE\_TBB=ON} is specified when configuring CMake. The
default behavior, however, is to submit the tasks to an internal
thread-pool and task-queue.
\hypertarget{g4taskrunmanager}{%
\subsection{G4TaskRunManager}\label{g4taskrunmanager}}
\texttt{G4TaskRunManager} multiply inherits from \texttt{G4MTRunManager}
and \texttt{PTL::TaskRunManager}. \texttt{PTL::TaskRunManager} holds the
thread-pool instance, the size of the thread-pool, and the default
task-queue. The constructor of \texttt{G4TaskRunManager} takes a
\texttt{G4VUserTaskQueue} pointer (can be nullptr), a boolean for
whether to use TBB if available, and a grainsize.
\hypertarget{concepts}{%
\subsubsection{Concepts}\label{concepts}}
\hypertarget{grainsize}{%
\paragraph{Grainsize}\label{grainsize}}
\begin{quote}
Environment Variable: \texttt{G4FORCE\_GRAINSIZE=N}
\end{quote}
The grainsize is essentially the number of tasks. If set to 0, the
default grainsize will be \texttt{poolSize} and each thread will get
\texttt{numEvents\ /\ poolSize} events. If the grainsize is set to 1,
then \emph{all the events} will be submitted as one task (i.e.~be
processed serially by one thread in the pool). If the grainsize is set
to 50 and there are 500 events, then 50 tasks of 10 events will be
submitted.
\hypertarget{events-per-tasks}{%
\paragraph{Events Per Tasks}\label{events-per-tasks}}
\begin{quote}
Environment Variable: \texttt{G4FORCE\_EVENTS\_PER\_TASK=N}
\end{quote}
Sometimes is easier to specify the number of events in a task instead of
the grainsize. If the events-per-task is set to 10 and there are 500
events, then 50 tasks of 10 events will be submitted.
\hypertarget{default-constructor}{%
\subsubsection{Default Constructor}\label{default-constructor}}
\begin{Shaded}
\begin{Highlighting}[]
\NormalTok{ G4TaskRunManager(G4VUserTaskQueue* = }\KeywordTok{nullptr}\NormalTok{, }\DataTypeTok{bool}\NormalTok{ useTBB = }\KeywordTok{false}\NormalTok{, G4int grainsize = }\DecValTok{0}\NormalTok{);}
\end{Highlighting}
\end{Shaded}
\hypertarget{g4runmanagercreator}{%
\subsection{G4RunManagerCreator}\label{g4runmanagercreator}}
An enumeration \texttt{G4RunManagerType} and a function
\texttt{G4RunManagerCreator::CreateRunManager(...)} was added to
\texttt{"G4RunManagerCreator.hh"} to simplify the selection of the
various run managers. The first parameter is either one of the
enumerated \texttt{G4RunManagerType} or a string identifier
\begin{longtable}[]{@{}lll@{}}
\toprule
Enumeration & String ID & Class\tabularnewline
\midrule
\endhead
\texttt{G4RunManagerType::Serial} & \texttt{"Serial"} &
\texttt{G4RunManager}\tabularnewline
\texttt{G4RunManagerType::MT} & \texttt{"MT"} &
\texttt{G4MTRunManager}\tabularnewline
\texttt{G4RunManagerType::Tasking} & \texttt{"Tasking"} &
\texttt{G4TaskRunManager}\tabularnewline
\texttt{G4RunManagerType::TBB} & \texttt{"TBB"} &
\texttt{G4TaskRunManager}\tabularnewline
\texttt{G4RunManagerType::Default} & \texttt{"Default"} & Environment
setting\tabularnewline
\bottomrule
\end{longtable}
The \texttt{Default} enumeration value will defer to the following
environment variable \texttt{G4RUN\_MANAGER\_TYPE} if specified and will
default to \texttt{"Tasking"} if MT is supported and serial if MT is not
supported. If the \texttt{G4FORCE\_RUN\_MANAGER\_TYPE} environment
variable is set, this variable will override the value passed to the
\texttt{CreateRunManager} function.
\begin{longtable}[]{@{}lll@{}}
\toprule
\begin{minipage}[b]{0.16\columnwidth}\raggedright
Environment Variable\strut
\end{minipage} & \begin{minipage}[b]{0.24\columnwidth}\raggedright
Options\strut
\end{minipage} & \begin{minipage}[b]{0.52\columnwidth}\raggedright
Description\strut
\end{minipage}\tabularnewline
\midrule
\endhead
\begin{minipage}[t]{0.16\columnwidth}\raggedright
\texttt{G4RUN\_MANAGER\_TYPE}\strut
\end{minipage} & \begin{minipage}[t]{0.24\columnwidth}\raggedright
\texttt{"Serial"}, \texttt{"MT"}, \texttt{"Tasking"},
\texttt{"TBB"}\strut
\end{minipage} & \begin{minipage}[t]{0.52\columnwidth}\raggedright
Only applicable when \texttt{G4RunManagerType::Default} is used\strut
\end{minipage}\tabularnewline
\begin{minipage}[t]{0.16\columnwidth}\raggedright
\texttt{G4FORCE\_RUN\_MANAGER\_TYPE}\strut
\end{minipage} & \begin{minipage}[t]{0.24\columnwidth}\raggedright
\texttt{"Serial"}, \texttt{"MT"}, \texttt{"Tasking"},
\texttt{"TBB"}\strut
\end{minipage} & \begin{minipage}[t]{0.52\columnwidth}\raggedright
Will override explicitly specifed \texttt{G4RunManagerType} and fail if
type is not available\strut
\end{minipage}\tabularnewline
\bottomrule
\end{longtable}
\hypertarget{creating-the-g4runmanager}{%
\subsection{Creating the G4RunManager}\label{creating-the-g4runmanager}}
\begin{itemize}
\tightlist
\item
The \texttt{G4RunManagerCreator::CreateRunManager(...)} function takes
either \texttt{G4RunManagerType} enumerated type or string to specify
the desired G4RunManager
\begin{itemize}
\tightlist
\item
If a string is used, regex matching is used which is
case-insensitive
\item
Returns a \texttt{G4RunManager*}
\item
Various overloads exist which just reorder passing in:
\begin{itemize}
\tightlist
\item
\texttt{int\ numberOfThreads} - executes
\texttt{G4MTRunManager::SetNumberOfThreads(numberOfThreads)}
before returning if \textgreater{} 0
\begin{itemize}
\tightlist
\item
default: \texttt{0}
\end{itemize}
\item
\texttt{bool\ fail\_if\_unavail} - will cause a runtime failure if
requested type is not available with Geant4 build
\begin{itemize}
\tightlist
\item
default: \texttt{true}
\end{itemize}
\item
\texttt{G4VTaskQueue*} - a task-queue manager
\begin{itemize}
\tightlist
\item
default: \texttt{nullptr}
\end{itemize}
\end{itemize}
\end{itemize}
\end{itemize}
\begin{Shaded}
\begin{Highlighting}[]
\PreprocessorTok{\#include }\ImportTok{"G4RunManagerCreator.hh"}
\DataTypeTok{int}\NormalTok{ main()}
\NormalTok{\{}
\CommentTok{// specify \{Serial, MT, Tasking, TBB\} as the default, can be overridden}
\CommentTok{// with "G4FORCE\_RUN\_MANAGER\_TYPE" env variable}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager(G4RunManagerType::Serial);}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager(G4RunManagerType::MT);}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager(G4RunManagerType::Tasking);}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager(G4RunManagerType::TBB);}
\CommentTok{// defer to "G4RUN\_MANAGER\_TYPE" env variable and default to Tasking if}
\CommentTok{// env variable is not set}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager(G4RunManagerType::Default);}
\CommentTok{// same as above}
\KeywordTok{auto}\NormalTok{* runmanager = G4RunManagerCreator::CreateRunManager();}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}
\hypertarget{using-the-tasking-system}{%
\subsection{Using the Tasking System}\label{using-the-tasking-system}}
With G4TaskRunManager, Geant4 events will be launched asynchronously as
tasks. These tasks are placed into a queue until one of the thread in
the pool is available to execute the task. Users can take advantage of
this system to load-balance expensive sub-event calculations which might
have previously resulted in serial bottlenecks. For example, if an
application needs to do extensive event analysis on electrons and thread
\#1 ends up with 10x as many of these events, the other threads might
finish their G4Run significantly eariler and be idle while thread \#1
has a lot of work. Tasking allows these analysis calculations to be
offload back into the queue so that other threads can contribute to
their completion.
\hypertarget{option-1---submit-directly-to-thread-pool}{%
\subsubsection{Option 1 - Submit Directly to
Thread-Pool}\label{option-1---submit-directly-to-thread-pool}}
\begin{itemize}
\tightlist
\item
To execute the function \texttt{foo(int,\ double)} asynchronously:
\end{itemize}
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{// get the task manager}
\KeywordTok{auto}\NormalTok{* task\_manager = G4TaskRunManager::GetTaskManager();}
\CommentTok{// submit task to thread{-}pool and receive a future for when the result is need}
\BuiltInTok{std::}\NormalTok{future\textless{}}\DataTypeTok{void}\NormalTok{\textgreater{} \_fvoid = task\_manager{-}\textgreater{}async\textless{}}\DataTypeTok{void}\NormalTok{\textgreater{}(foo, }\DecValTok{1}\NormalTok{, }\FloatTok{1.0}\NormalTok{);}
\BuiltInTok{std::}\NormalTok{future\textless{}}\DataTypeTok{int}\NormalTok{\textgreater{} \_fint = task\_manager{-}\textgreater{}async\textless{}}\DataTypeTok{int}\NormalTok{\textgreater{}(bar, }\FloatTok{1.0}\NormalTok{);}
\CommentTok{// wait for task to execute}
\NormalTok{\_fvoid.wait();}
\NormalTok{\_fint.wait();}
\CommentTok{// get the result (if non{-}void)}
\KeywordTok{auto}\NormalTok{ result = \_fint.get();}
\end{Highlighting}
\end{Shaded}
\hypertarget{option-2---submit-to-task-group}{%
\subsubsection{Option 2 - Submit to
task-group}\label{option-2---submit-to-task-group}}
\begin{itemize}
\tightlist
\item
Obtain a pointer to the thread-pool instance
\item
Create a \texttt{task\_group\textless{}T\textgreater{}} object where
\texttt{T} is the return type of all the functions in the group
\begin{itemize}
\tightlist
\item
If \texttt{T} is non-void, you must provide a join functor who
return type and first argument are both references to the joined
type and the second argument is type \texttt{T},
e.g.~\texttt{task\_group\textless{}int\textgreater{}} can provide a
join functor with \texttt{vector\textless{}int\textgreater{}\&} as
the return type and \texttt{T} as the second argument or
\texttt{int\&} as the return and first argument and \texttt{int} as
the second argument
\item
If \texttt{T} is void, the join functor is optional and can be
treated as a final synchronization operation after all the tasks
have been completed.
\end{itemize}
\end{itemize}
\begin{quote}
NOTE: The join functor for task-groups are called sequentially on the
thread that is waiting on
\texttt{task\_group\textless{}T\textgreater{}::join()} member function.
\end{quote}
\hypertarget{global-definitions-for-examples}{%
\paragraph{Global Definitions for
Examples}\label{global-definitions-for-examples}}
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{// obtain thread{-}pool instance from task manager}
\AttributeTok{static} \KeywordTok{auto}\NormalTok{* thread\_pool = G4TaskRunManager::GetThreadPool();}
\CommentTok{// trivial int function which just returns value passed}
\DataTypeTok{int}\NormalTok{ foo(}\DataTypeTok{int}\NormalTok{ v) \{ }\ControlFlowTok{return}\NormalTok{ v; \}}
\CommentTok{// function which launches CUDA kernel}
\DataTypeTok{void}\NormalTok{ bar(}\DataTypeTok{int}\NormalTok{ v)}
\NormalTok{\{}
\NormalTok{ cuda\_bar\textless{}\textless{}\textless{}}\DecValTok{512}\NormalTok{, }\DecValTok{1}\NormalTok{\textgreater{}\textgreater{}\textgreater{}(v);}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}
\hypertarget{example-with-non-void-return-types-from-tasks}{%
\paragraph{Example with non-void return types from
tasks}\label{example-with-non-void-return-types-from-tasks}}
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{// put all return values from tasks into an array}
\KeywordTok{auto}\NormalTok{ join\_vec = [](}\BuiltInTok{std::}\NormalTok{vector\textless{}}\DataTypeTok{int}\NormalTok{\textgreater{}\& lhs, }\DataTypeTok{int}\NormalTok{ rhs) \{ lhs.push\_back(rhs); }\ControlFlowTok{return}\NormalTok{ lhs; \};}
\CommentTok{// sum the values returned by tasks}
\KeywordTok{auto}\NormalTok{ sum\_int = [](}\DataTypeTok{int}\NormalTok{\& lhs, }\DataTypeTok{int}\NormalTok{ rhs) \{ }\ControlFlowTok{return}\NormalTok{ lhs += rhs; \};}
\CommentTok{// task group which applies \textquotesingle{}join\_vec\textquotesingle{} to all task return values}
\NormalTok{task\_group\textless{}}\DataTypeTok{int}\NormalTok{\textgreater{} vec\_tg(join\_vec, thread\_pool);}
\CommentTok{// task group with applies \textquotesingle{}sum\_int\textquotesingle{} to all task return values}
\NormalTok{task\_group\textless{}}\DataTypeTok{int}\NormalTok{\textgreater{} sum\_tg(sum\_int, thread\_pool);}
\CommentTok{// submit work to task{-}groups}
\NormalTok{vec\_tg.exec(foo, }\DecValTok{1}\NormalTok{);}
\NormalTok{vec\_tg.exec(foo, }\DecValTok{2}\NormalTok{);}
\NormalTok{sum\_tg.exec(foo, }\DecValTok{1}\NormalTok{);}
\NormalTok{sum\_tg.exec(foo, }\DecValTok{2}\NormalTok{);}
\CommentTok{// produces std::vector\{ 1, 2 \};}
\KeywordTok{auto}\NormalTok{ vec\_result = vec\_tg.join();}
\CommentTok{// produces 1 + 2 = 3}
\KeywordTok{auto}\NormalTok{ sum\_result = sum\_tg.join();}
\end{Highlighting}
\end{Shaded}
\hypertarget{example-with-void-return-type-from-tasks}{%
\paragraph{Example with void return type from
tasks}\label{example-with-void-return-type-from-tasks}}
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{// wait for the GPU to finish}
\KeywordTok{auto}\NormalTok{ sync = []() \{ cudaDeviceSynchronize(); \};}
\CommentTok{// task group which applies \textquotesingle{}sync\textquotesingle{} after all tasks have been executed}
\NormalTok{task\_group\textless{}}\DataTypeTok{void}\NormalTok{\textgreater{} gpu\_tg(sync, thread\_pool);}
\CommentTok{// generic task group w/o a join functor}
\NormalTok{task\_group\textless{}}\DataTypeTok{void}\NormalTok{\textgreater{} general\_tg(thread\_pool);}
\CommentTok{// submit work to task{-}groups}
\NormalTok{gpu\_tg.exec(bar, }\DecValTok{1}\NormalTok{);}
\NormalTok{gpu\_tg.exec(bar, }\DecValTok{2}\NormalTok{);}
\NormalTok{general\_tg.exec(bar, }\DecValTok{1}\NormalTok{);}
\NormalTok{general\_tg.exec(bar, }\DecValTok{2}\NormalTok{);}
\CommentTok{// \textquotesingle{}sync()\textquotesingle{} will get called after all tasks in group have executed}
\CommentTok{// (i.e. return from \textquotesingle{}bar\textquotesingle{} function). \textquotesingle{}sync\textquotesingle{} will then block until}
\CommentTok{// all GPU work has been completed}
\NormalTok{gpu\_tg.join();}
\CommentTok{// will block only until all tasks in group have been executed}
\CommentTok{// (i.e. returned from \textquotesingle{}bar\textquotesingle{} function)}
\NormalTok{generic\_tg.join();}
\end{Highlighting}
\end{Shaded}