parfor
-LoopsMATLAB®
Coder™ classifies variables inside a parfor
-loop into one of the
categories in the following table. It does not support variables that it cannot classify. If
a parfor
-loop contains variables that cannot be uniquely categorized or
if a variable violates its category restrictions, the parfor
-loop
generates an error.
Classification | Description |
---|---|
Loop | Serves as a loop index for arrays |
Sliced | An array whose segments are operated on by different iterations of the loop |
Broadcast | A variable defined before the loop whose value is used inside the loop, but not assigned inside the loop |
Reduction | Accumulates a value across iterations of the loop, regardless of iteration order |
Temporary | A variable created inside the loop, but unlike sliced or reduction variables, not available outside the loop |
Each of these variable classifications appears in this code fragment:
a=0; c=pi; z=0; r=rand(1,10); parfor i=1:10 a=i; % 'a' is a temporary variable z=z+i; % 'z' is a reduction variable b(i)=r(i); % 'b' is a sliced output variable; % 'r' a sliced input variable if i<=c % 'c' is a broadcast variable d=2*a; % 'd' is a temporary variable end end
A sliced variable is one whose value can be broken up into segments, or slices, which are then operated on separately by different threads. Each iteration of the loop works on a different slice of the array.
In the next example, a slice of A
consists of a single element of
that array:
parfor i = 1:length(A) B(i) = f(A(i)); end
A variable in a parfor
-loop is sliced if it has the following
characteristics:
Type of First-Level Indexing — The first level of indexing is parentheses,
()
.
Fixed Index Listing — Within the first-level parenthesis, the list of indices is the same for all occurrences of a given variable.
Form of Indexing — Within the list of indices for the variable, exactly one index involves the loop variable.
Shape of Array — In assigning to a sliced variable, the right-hand side of
the assignment is not []
or ''
(these operators
indicate deletion of elements).
Type of First-Level Indexing. For a sliced variable, the first
level of indexing is enclosed in parentheses, ()
. For example,
A(...)
. If you reference a variable using dot notation,
A.x
, the variable is not sliced.
Variable A
on the left is not sliced; variable A
on the right is sliced:
A.q(i,12) A(i,12).q
Fixed Index Listing. Within the first-level parentheses of a sliced variable's indexing, the list of indices is the same for all occurrences of a given variable.
Variable B
on the left is not sliced because B
is indexed by i
and i+1
in different places.
Variable B
on the right is sliced.
parfor i = 1:10 B(i) = B(i+1) + 1; end |
parfor i = 1:10 B(i+1) = B(i+1) + 1; end |
Form of Indexing. Within the list of indices for a sliced
variable, one index is of the form i
, i+k
,
i-k
, k+i
, or k-i
.
i
is the loop variable.
k
is a constant or a simple (nonindexed) variable.
Every other index is a constant, a simple variable, colon, or end
.
When you use other variables along with the loop variable to index an array, you
cannot set these variables inside the loop. These variables are constant over the
execution of the entire parfor
statement. You cannot combine the loop
variable with itself to form an index expression.
In the following examples, i
is the loop variable,
j
and k
are nonindexed variables.
Variable A Is Not Sliced | Variable A Is Sliced |
---|---|
A(i+f(k),j,:,3) A(i,20:30,end) A(i,:,s.field1) |
A(i+k,j,:,3) A(i,:,end) A(i,:,k) |
Shape of Array. A sliced variable must maintain a constant shape.
In the following examples, the variable A
is not sliced:
A(i,:) = []; A(end + 1) = i;
A broadcast variable is a variable other than the loop variable or a sliced variable that is not modified inside the loop.
A reduction variable accumulates a value that depends on all the iterations together, but is independent of the iteration order.
This example shows a parfor
-loop that uses a scalar reduction
assignment. It uses the reduction variable x
to accumulate a sum across
10
iterations of the loop. The execution order of the iterations on the
threads does not
matter.
x = 0; parfor i = 1:10 x = x + i; end x
Where expr
is a MATLAB expression, reduction variables appear on both sides of an assignment
statement.
X = X + expr | X = expr + X |
X = X - expr | See Reduction Assignments, Associativity, and Commutativity of Reduction Functions |
X = X .* expr | X = expr .* X |
X = X * expr | X = expr * X |
X = X & expr | X = expr & X |
X = X | expr | X = expr | X |
X = min(X, expr) | X = min(expr, X) |
X = max(X, expr) | X = max(expr, X) |
X=f(X, expr) Function f
must be a user-defined function. | X = f(expr, X) See Reduction Assignments, Associativity, and Commutativity of Reduction Functions |
Each of the allowed statements is referred to as a reduction assignment. A reduction variable can appear only in assignments of this type.
The following example shows a typical usage of a reduction variable
X
:
X = ...; % Do some initialization of X parfor i = 1:n X = X + d(i); end
This loop is equivalent to the following, where each d(i)
is
calculated by a different iteration:
X = X + d(1) + ... + d(n)
If the loop were a regular for
-loop, the variable
X
in each iteration would get its value either before entering the loop
or from the previous iteration of the loop. However, this concept does not apply to
parfor
-loops.
In a parfor
-loop, the value of X
is not updated
directly inside each thread. Rather, additions of d(i)
are done in each
thread, with i
ranging over the subset of 1:n
being
performed on that thread. The software then accumulates the results into
X
.
Similarly, the reduction:
r=r<op> x(i)
r=r<op>x(1)] <op>x(2)...<op>x(n)
<op>
is first applied to x(1)...x(n)
,
then the partial result is applied to r
.If operation <op>
takes two inputs, it should meet one of the
following criteria:
Take two arguments of typeof(x(i))
and return
typeof(x(i))
Take one argument of typeof(r)
and one of
typeof(x(i))
and return typeof(r)
Use the same reduction function or operation in
all reduction assignments. For a reduction variable, you must use the same reduction function or operation in
all reduction assignments for that variable.
In the following example, the parfor
-loop on the left is not valid
because the reduction assignment uses +
in one instance, and
*
in another.
Invalid Use of Reduction Variable | Valid Use of Reduction Variable |
---|---|
parfor i = 1:n if A > 5*k A = A + 1; else A = A * 2; end |
parfor i = 1:n if A > 5*k A = A * 3; else A = A * 2; end |
Restrictions on reduction function parameter and return types. A reduction r=r<op> x(i)
, should take arguments of
typeof(x(i))
and return typeof(x(i))
or take
arguments of typeof(r)
and typeof(x(i))
and return
typeof(r)
.
In the following example, in the invalid loop, r
is a fixed-point
type and 2
is not. To fix this issue, cast 2
to be
the same type as r
.
Invalid Use of Reduction Variable | Valid Use of Reduction Variable |
---|---|
function r = fiops(in) r=fi(in,'WordLength',20,... 'FractionLength',14,... 'SumMode','SpecifyPrecision',... 'SumWordLength',20,... 'SumFractionLength',14,... 'ProductMode', 'SpecifyPrecision',... 'ProductWordLength',20,... 'ProductFractionLength',14); parfor i = 1:10 r = r*2; end |
r=fi(in,'WordLength',20,... 'FractionLength',14,... 'SumMode','SpecifyPrecision',... 'SumWordLength',20,... 'SumFractionLength',14,... 'ProductMode','SpecifyPrecision',... 'ProductWordLength',20,... 'ProductFractionLength',14); T = r.numerictype; F = r.fimath; parfor i = 1:10 r = r*fi(2,T,F); end |
In the following example, the reduction function fcn
is invalid
because it does not handle the case when input u
is fixed point. (The
+
and *
operations are automatically
polymorphic.) You must write a polymorphic version of fcn
to handle
the expected input types.
Invalid Use of Reduction Variable | Valid Use of Reduction Variable |
---|---|
function [y0, y1, y2] = pfuserfcn(u) y0 = 0; y1 = 1; [F, N] = fiprops(); y2 = fi(1,N,F); parfor (i=1:numel(u),12) y0 = y0 + u(i); y1 = y1 * u(i); y2 = fcn(y2, u(i)); end end function y = fcn(u, v) y = u * v; end |
function [y0, y1, y2] = pfuserfcn(u) y0 = 0; y1 = 1; [F, N] = fiprops(); y2 = fi(1,N,F); parfor (i=1:numel(u),12) y0 = y0 + u(i); y1 = y1 * u(i); y2 = fcn(y2, u(i)); end end % fcn handles inputs of type double % and fi function y = fcn(u, v) if isa(u,'double') y = u * v; else [F, N] = fiprops(); y = u * fi(v,N,F); end end function [F, N] = fiprops() N = numerictype(1,96,30); F = fimath('ProductMode',... 'SpecifyPrecision',... 'ProductWordLength',96); end |
Reduction Assignments. MATLAB
Coder does not allow reduction variables to be read anywhere in the
parfor
-loop except in reduction statements. In the following example,
the call foo(r)
after the reduction statement r=r+i
causes the loop to be
invalid.
function r = temp %#codegen r = 0; parfor i=1:10 r = r + i; foo(r); end end
Associativity in Reduction Assignments. If you use a user-defined function f
in the definition of
a reduction variable, to get deterministic behavior of
parfor
-loops, the reduction function f
must be
associative.
Note
If f
is not associative, MATLAB
Coder does not generate an error. You must write code that meets this
recommendation.
To be associative, the function f
must satisfy the following for all
a
, b
, and c
:
f(a,f(b,c)) = f(f(a,b),c)
Commutativity in Reduction Assignments. Some associative functions, including +
,
.
, min
, and max
, are also commutative. That is, they satisfy the following for all
a
and b
:
f(a,b) = f(b,a)
The function f
of a reduction assignment must be commutative. If
f
is not commutative, different executions of the loop might result
in different answers.
Unless f
is a known noncommutative built-in, the software assumes
that it is commutative.
A temporary variable is a variable that is the target of a direct,
nonindexed assignment, but is not a reduction variable. In the following
parfor
-loop, a
and d
are
temporary variables:
a = 0; z = 0; r = rand(1,10); parfor i = 1:10 a = i; % Variable a is temporary z = z + i; if i <= 5 d = 2*a; % Variable d is temporary end end
In contrast to the behavior of a for
-loop, before each iteration of a parfor
-loop,
MATLAB
Coder effectively clears temporary variables. Because the iterations must be
independent, the values of temporary variables cannot be passed from one iteration of the
loop to another. Therefore, temporary variables must be set inside the body of a
parfor
-loop, so that their values are defined separately for each
iteration.
A temporary variable in the context of the parfor
statement is
different from a variable with the same name that exists outside the loop.
Because temporary variables are cleared at the beginning of every iteration, MATLAB Coder can detect certain cases in which an iteration through the loop uses the temporary variable before it is set in that iteration. In this case, MATLAB Coder issues a static error rather than a run-time error, because there is little point in allowing execution to proceed if a run-time error will occur. For example, suppose you write:
b = true; parfor i = 1:n if b && some_condition(i) do_something(i); b = false; end ... end
This loop is acceptable as an ordinary for
-loop, but as a
parfor
-loop, b
is a temporary variable because it
occurs directly as the target of an assignment inside the loop. Therefore, it is cleared
at the start of each iteration, so its use in the condition of the if
is uninitialized. (If you change parfor
to for
, the
value of b
assumes sequential execution of the loop, so that
do_something(i)
is executed for only the lower values of
i
until b
is set false
.)