Support of Multithreading in User Application
For using the kernel interfaces in several threads, the multithreading mode mtm_SafeItems or higher should be defined for the kernel. By default the kernel works in the maximal multithreading mode (mtm_Max).
If the user application uses the kernel interfaces in parallel computation, it must notify the kernel about entering and exiting a parallel region. For that, the ParallelRegionGuard class (a scoped guard of a parallel region) or the pair of functions EnterParallelRegion and ExitParallelRegion can be used.
Protection of Parallel Code in User Application
The ParallelRegionGuard class notifies the kernel that code in the scope runs in parallel. It should be used to protect a parallel region.
Instead of the ParallelRegionGuard class, the following pair of functions also can be used to notify the kernel about parallel region boundaries:
The EnterParallelRegion function notifies the kernel about entering a parallel region.
The ExitParallelRegion function notifies the kernel about exiting a parallel region.
Interfaces ParallelRegionGuard, EnterParallelRegion and ExitParallelRegion must be used in cases when the kernel is used in several threads.
Note that before starting to use parallel computing it is necessary to ensure that the multithreading mode mtm_SafeItems or higher is set in the kernel (by default the maximal multithreading mode mtm_Max is set).
The Examples of Notifying the Kernel about its Use in Parallel Computing
ParallelRegionGuard should be called one time before starting work in several threads. For example:
// ... Sequential code ...
if( Math::CheckMultithreadedMode( mtm_Items ) ) {
ParallelRegionGuard guard; // works in the scope
std::thread t1( func1 );
std::thread t2( func2 );
t1.join();
t2.join();
}
// ... Sequential code ...
The same can be done using a pair of functions EnterParallelRegion and ExitParallelRegion instead of ParallelRegionGuard:
// ... Sequential code ...
if( Math::CheckMultithreadedMode( mtm_Items ) ) {
EnterParallelRegion();
std::thread t1( func1 );
std::thread t2( func2 );
t1.join();
t2.join();
ExitParallelRegion();
}
// ... Sequential code ...
The purpose of the ParallelRegionGuard, EnterParallelRegion and ExitParallelRegion interfaces is notifying the kernel about parallel calculations in a user application. These notifications are necessary to guarantee correct work of thread-safety mechanisms in the kernel.
Quick Reference on Organizing Parallel cComputing Using C3D Interfaces
Before running parallel calculations the application must check that the multithreading mode of the kernel allows parallel computing. To use kernel interfaces in several threads, the multithreading mode of the kernel must be set to at least mtm_SafeItems.
Example of checking the kernel multithreading mode:
bool useParallel = Math::CheckMultithreadedMode( mtm_SafeItems );
When using parallel calculations the user application must notify the kernel about entering and exiting a parallel region. If a parallel cycle is used, the application must call EnterParallelRegion() before the cycle start and ExitParallelRegion() after the cycle finish.
Example:
if( Math::CheckMultithreadedMode( mtm_Items ) ) { EnterParallelRegion(); #pragma omp parallel for for( ptrdiff_t i = 0; i < count; ++i ) { /* Cycle body */ } ExitParallelRegion(); }
If the kernel interfaces are simply called from several threads, then in each thread EnterParallelRegion must be placed before the kernel calls and ExitParallelRegion must be placed after the kernel calls.
Example:
// Thread 1: EnterParallelRegion(); // The kernel calls ExitParallelRegion(); // Thread 2: EnterParallelRegion(); // The kernel calls ExitParallelRegion();
After the first call of EnterParallelRegion the kernel starts using multithreaded caches, and after the last call of ExitParallelRegion it returns to the base mode of sequential execution.
It is not recommended to call ParallelRegionGuard(), EnterParallelRegion() and ExitParallelRegion() without need (in the absence of multithreading), because the use of multithreaded caches in sequential execution entails some time overhead of obtaining cached data.