commit
2ec1501c7a
42 changed files with 2115 additions and 192 deletions
|
|
@ -2823,7 +2823,7 @@ The following builtin procs cannot be overloaded for reasons of implementation
|
|||
simplicity (they require specialized semantic checking)::
|
||||
|
||||
defined, definedInScope, compiles, low, high, sizeOf,
|
||||
is, of, echo, shallowCopy, getAst
|
||||
is, of, echo, shallowCopy, getAst, spawn
|
||||
|
||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||
|
|
|
|||
98
doc/spawn.txt
Normal file
98
doc/spawn.txt
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
==========================================================
|
||||
Parallel & Spawn
|
||||
==========================================================
|
||||
|
||||
Nimrod has two flavors of parallelism:
|
||||
1) `Structured`:idx parallelism via the ``parallel`` statement.
|
||||
2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
|
||||
|
||||
Both need the `threadpool <threadpool.html>`_ module to work.
|
||||
|
||||
Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement
|
||||
with slightly different semantics. ``spawn`` always takes a call expression of
|
||||
the form ``f(a, ...)``. Let ``T`` be ``f``'s return type. If ``T`` is ``void``
|
||||
then ``spawn``'s return type is also ``void``. Within a ``parallel`` section
|
||||
``spawn``'s return type is ``T``, otherwise it is ``FlowVar[T]``.
|
||||
|
||||
The compiler can ensure the location in ``location = spawn f(...)`` is not
|
||||
read prematurely within a ``parallel`` section and so there is no need for
|
||||
the overhead of an indirection via ``FlowVar[T]`` to ensure correctness.
|
||||
|
||||
|
||||
Parallel statement
|
||||
==================
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Compute PI in an inefficient way
|
||||
import strutils, math, threadpool
|
||||
|
||||
proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
|
||||
|
||||
proc pi(n: int): float =
|
||||
var ch = newSeq[float](n+1)
|
||||
parallel:
|
||||
for k in 0..ch.high:
|
||||
ch[k] = spawn term(float(k))
|
||||
for k in 0..ch.high:
|
||||
result += ch[k]
|
||||
|
||||
echo formatFloat(pi(5000))
|
||||
|
||||
|
||||
The parallel statement is the preferred mechanism to introduce parallelism
|
||||
in a Nimrod program. A subset of the Nimrod language is valid within a
|
||||
``parallel`` section. This subset is checked to be free of data races at
|
||||
compile time. A sophisticated `disjoint checker`:idx: ensures that no data
|
||||
races are possible even though shared memory is extensively supported!
|
||||
|
||||
The subset is in fact the full language with the following
|
||||
restrictions / changes:
|
||||
|
||||
* ``spawn`` within a ``parallel`` section has special semantics.
|
||||
* Every location of the form ``a[i]`` and ``a[i..j]`` and ``dest`` where
|
||||
``dest`` is part of the pattern ``dest = spawn f(...)`` has to be
|
||||
provable disjoint. This is called the *disjoint check*.
|
||||
* Every other complex location ``loc`` that is used in a spawned
|
||||
proc (``spawn f(loc)``) has to be immutable for the duration of
|
||||
the ``parallel`` section. This is called the *immutability check*. Currently
|
||||
it is not specified what exactly "complex location" means. We need to make
|
||||
this an optimization!
|
||||
* Every array access has to be provable within bounds. This is called
|
||||
the *bounds check*.
|
||||
* Slices are optimized so that no copy is performed. This optimization is not
|
||||
yet performed for ordinary slices outside of a ``parallel`` section. Slices
|
||||
are also special in that they currently do not support negative indexes!
|
||||
|
||||
|
||||
|
||||
|
||||
Spawn statement
|
||||
===============
|
||||
|
||||
A standalone ``spawn`` statement is a simple construct. It executes
|
||||
the passed expression on the thread pool and returns a `data flow variable`:idx:
|
||||
``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
|
||||
**blocking**. However, one can use ``awaitAny`` to wait on multiple flow
|
||||
variables at the same time:
|
||||
|
||||
.. code-block:: nimrod
|
||||
import threadpool, ...
|
||||
|
||||
# wait until 2 out of 3 servers received the update:
|
||||
proc main =
|
||||
var responses = newSeq[RawFlowVar](3)
|
||||
for i in 0..2:
|
||||
responses[i] = spawn tellServer(Update, "key", "value")
|
||||
var index = awaitAny(responses)
|
||||
assert index >= 0
|
||||
responses.del(index)
|
||||
discard awaitAny(responses)
|
||||
|
||||
Like the ``parallel`` statement data flow variables ensure that no data races
|
||||
are possible. Due to technical limitations not every type ``T`` is possible in
|
||||
a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq``
|
||||
or of a type that doesn't contain a type that is garbage collected. This
|
||||
restriction will be removed in the future.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue