## Classification of Variables in `parfor`

-Loops

### Overview

MATLAB^{®}
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

### Sliced Variables

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

#### Characteristics of a Sliced Variable

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;

### Broadcast Variables

A *broadcast variable* is a variable other than the loop variable or
a sliced variable that is not modified inside the loop.

### Reduction Variables

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

#### Rules for Reduction Variables

**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, Associativity, and Commutativity of Reduction Functions

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

### Temporary Variables

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.

#### Uninitialized Temporaries

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`

.)