A library for writing OpenMP-style parallel code in .NET. Inspired by the fork-join paradigm of OpenMP, and attempts to replicate the OpenMP programming style as faithfully as possible, though breaking spec at times.
Given the OpenMP:
#pragma omp parallel
{
work();
}
OpenMP.NET provides:
OpenMP.Parallel.ParallelRegion(() => {
work();
});
This function supports the num_threads
optional parameter, which sets the number of threads to spawn.
The default value is the number of logical threads on the system.
Given the OpenMP:
#pragma omp for
for (int i = a, i < b; i++)
{
work(i);
}
OpenMP.NET provides:
OpenMP.Parallel.For(a, b, i => {
work(i);
});
This function supports the schedule
optional parameter, which sets the parallel scheduler to use.
Permissible values are OpenMP.Parallel.Schedule.Static
, OpenMP.Parallel.Schedule.Dynamic
, and OpenMP.Parallel.Schedule.Guided
.
The default value is OpenMP.Parallel.Schedule.Static
.
This function supports the chunk_size
optional parameter, which sets the chunk size for the scheduler to use.
The default value is dependent on the scheduler and is not documented, as it may change from version to version.
The behavior of OpenMP.Parallel.For
is undefined if not used within a ParallelRegion
.
Given the OpenMP:
#pragma omp parallel for
for (int i = a, i < b; i++)
{
work(i);
}
OpenMP.NET provides:
OpenMP.Parallel.ParallelFor(a, b, i => {
work(i);
});
This function supports all of the optional parameters of ParallelRegion
and For
, and is merely a wrapper around those two functions for conciseness.
Given the OpenMP:
type local = c;
#pragma omp for reduction(op:local)
for (int i = a; i < b; i++)
{
local `op` d;
}
OpenMP.NET provides:
type local = c;
OpenMP.Parallel.ForReduction(a, b, op, ref local, (ref type local, int i) => {
local `op` d;
});
op
is a value provided by the OpenMP.Operations
enum, which supports the values Add
, Subtract
, Multiply
, BinaryAnd
, BinaryOr
, BinaryXor
, BooleanAnd
, BooleanOr
, Min
, and Max
.
This function supports all of the optional parameters of For
.
Given the OpenMP:
type local = c;
#pragma omp parallel for reduction(op:local)
for (int i = a; i < b; i++)
{
local `op` d;
}
OpenMP.NET provides:
type local = c;
OpenMP.Parallel.ParallelForReduction(a, b, op, ref local, (ref type local, int i) => {
local `op` d;
});
This function supports all of the optional parameters of ParallelRegion
and ForReduction
, and is merely a wrapper around those two functions for conciseness.
Given the OpenMP:
#pragma omp critical
{
work();
}
OpenMP.NET provides:
OpenMP.Parallel.Critical(id, () => {
work();
});
This function requires an id
parameter, which is used as a unique identifier for a particular critical region.
If multiple critical regions are present in the code, they should each have a unique id
.
The id
should likely be a const int
or an integer literal.
Given the OpenMP:
#pragma omp barrier
OpenMP.NET provides:
OpenMP.Parallel.Barrier();
Given the OpenMP:
#pragma omp master
{
work();
}
OpenMP.NET provides:
OpenMP.Parallel.Master(() => {
work();
});
Master
's behavior is left undefined if used outside of a For
.
Given the OpenMP:
#pragma omp single
{
work();
}
OpenMP.NET provides:
OpenMP.Parallel.Single(id, () => {
work();
});
The id
parameter provided should follow the same guidelines as specified in Critical
.
The behavior of a single
region is as follows: the first thread in a team to reach a given Single
region will "own" the Single
for the duration of the ParallelRegion
.
On all subsequent encounters, only that first thread will execute the region. All other threads will ignore it.
Single
's behavior is left undefined if used outside of a For
.
Given the OpenMP:
#pragma omp ordered
{
work();
}
OpenMP.NET provides:
OpenMP.Parallel.Ordered(id, () => {
work();
});
The id
parameter provided should follow the same guidelines as specified in Critical
.
Ordered
's behavior is left undefined if used outside of a For
.
OpenMP atomics are implemented as follows:
#pragma omp atomic
a op b;
where op
is some supported operator.
OpenMP.NET supports a subset of this for the int
, uint
, long
, and ulong
types.
The only implemented atomic operations are a += b
, a &= b
, a |= b
, ++a
, and --a
.
a -= b
is implemented, but for signed types only, due to restrictions interfacting with C#'s Interlocked
class.
The following table documents the supported atomics:
Operation | OpenMP.NET function |
---|---|
a += b |
OpenMP.Atomic.Add(ref a, b) |
a -= b |
OpenMP.Atomic.Sub(ref a, b) |
a &= b |
OpenMP.Atomic.And(ref a, b) |
a |= b |
OpenMP.Atomic.Or(ref a, b) |
++a |
OpenMP.Atomic.Inc(ref a) |
--a |
OpenMP.Atomic.Dec(ref a) |
For atomic operations like compare-exchange, we recommend interfacting directly with System.Threading.Interlocked
.
For non-supported atomic operations or types, we recommend using OpenMP.Parallel.Critical
.
This is more of a limitation of the underlying hardware than anything.
OpenMP.NET supports OpenMP-style locks.
It is recommended to use C#'s native lock
keyword where possible for performance.
However, this API is provided to those who want the familiarity of OpenMP locks.
OpenMP.NET supports the OpenMP.Lock
object, which is the replacement for omp_lock_t
.
omp_init_lock
and omp_destroy_lock
are not implemented.
Instead, users should instantiate the OpenMP.Lock
object using the new
keyword.
OpenMP.NET provides the following functions:
<omp.h> function | OpenMP.NET function | Comments |
---|---|---|
omp_set_lock(lock) | OpenMP.Locking.Set(lock) | Halt the current thread until the lock is obtained |
omp_unset_lock(lock) | OpenMP.Locking.Unset(lock) | Free the current lock, making it available for other threads |
omp_test_lock(lock) | OpenMP.Locking.Test(lock) | Attempt to obtain a lock without blocking, returns true if locking is successful |
OpenMP.NET provides an analog of the following functions:
<omp.h> function | OpenMP.NET function | Comments |
---|---|---|
omp_get_num_procs() | OpenMP.Parallel.GetNumProcs() | Returns the number of logical threads on the system |
omp_get_num_threads() | OpenMP.Parallel.GetNumThreads() | Returns the number of active threads in the current region |
omp_set_num_threads(int) | OpenMP.Parallel.SetNumThreads(int) | Sets the number of threads for the next parallel region to use |
omp_get_thread_num() | OpenMP.Parallel.GetThreadNum() | Gets the ID of the current thread |
omp_get_max_threads() | OpenMP.Parallel.GetMaxThreads() | Gets the maximum number of threads the runtime may use in the next region |
omp_in_parallel() | OpenMP.Parallel.InParallel() | Returns true if called from within a parallel region |
omp_set_dynamic(int) | OpenMP.Parallel.SetDynamic() | Tells the runtime to dynamically adjust the number of threads, can disable by calling SetNumThreads |
omp_get_dynamic() | OpenMP.Parallel.GetDynamic() | Returns true if the runtime can dynamically adjust the number of threads |
omp_set_nested(int) | OpenMP.Parallel.SetNested(int) | Returns a NotImplementedException |
omp_get_nested() | OpenMP.Parallel.GetNested() | Returns false |
omp_get_wtime() | OpenMP.Parallel.GetWTime() | Returns the number of seconds since the Unix Epoch as a double |