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.
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
.)