## 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

MATLAB supports an important exception, called reduction, to the rule that loop iterations must be independent. A *reduction variable* accumulates a value that depends on all the iterations together, but is independent of the iteration order. MATLAB allows reduction variables in `parfor`

-loops.

Reduction variables appear on both sides of an assignment statement, such as any of the following, where `expr`

is a MATLAB expression.

`X = X + expr` | `X = expr + X` |

`X = X - expr` | See Associativity in Reduction Assignments in Requirements for Reduction Assignments |

`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 = [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 = union(X, expr)` | `X = union(expr, X)` |

`X = intersect(X, expr)` | `X = intersect(expr, X)` |

Each of the allowed statements listed in this table is referred to as a *reduction assignment*. By definition, a reduction variable can appear only in assignments of this type.

The general form of a reduction assignment is

`X = f(X, expr)` | `X = f(expr, X)` |

The following example shows a typical usage of a reduction variable `X`

.

X = 0; % Do some initialization of X parfor i = 1:n X = X + d(i); end

This loop is equivalent to the following, where you calculate each `d(i)`

by a different iteration.

X = X + d(1) + ... + d(n)

In a regular `for`

-loop, the variable `X`

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 never transmitted from client to workers or from worker to worker. Rather, additions of `d(i)`

are done in each worker, with `i`

ranging over the subset of `1:n`

being performed on that worker. The results are then transmitted back to the client, which adds the partial sums of the workers into `X`

. Thus, workers do some of the additions, and the client does the rest.

#### Notes About Required and Recommended Guidelines

If your `parfor`

code does not adhere to the guidelines and restrictions labeled as **Required**, you get an error. MATLAB catches some of these errors at the time it reads the code, and others when it executes the code. These errors are labeled as **Required (static)** or **Required (dynamic)** respectively. Guidelines that do not cause errors are labeled as **Recommended**. You can use MATLAB Code Analyzer to help `parfor`

-loops comply with the guidelines.

#### Basic Rules for Reduction Variables

The following requirements further define the reduction assignments associated with a given variable.

Required (static): For any reduction variable, the same reduction function or operation must be used in all reduction assignments for that variable. |

The `parfor`

-loop on the left is not valid because the reduction assignment uses `+`

in one instance, and `[,]`

in another. The `parfor`

-loop on the right is valid.

Invalid | Valid |
---|---|

parfor i = 1:n if testLevel(k) A = A + i; else A = [A, 4+i]; end % loop body continued end | parfor i = 1:n if testLevel(k) A = A + i; else A = A + i + 5*k; end % loop body continued end |

Required (static): If the reduction assignment uses `*` , `[,]` , or `[;]` , then `X` must be consistently specified as the first or second argument in every reduction assignment. |

The `parfor`

-loop on the left is not valid because the order of items in the concatenation is not consistent throughout the loop. The `parfor`

-loop on the right is valid.

Invalid | Valid |
---|---|

parfor i = 1:n if testLevel(k) A = [A, 4+i]; else A = [r(i), A]; end % loop body continued end | parfor i = 1:n if testLevel(k) A = [A, 4+i]; else A = [A, r(i)]; end % loop body continued end |

Required (static): You cannot index or subscript a reduction variable. |

The code on the left is not valid because it tries to index `a`

, and so MATLAB cannot classify it as a reduction variable. To fix it, the code on the right uses a non-indexed variable.

Invalid | Valid |
---|---|

a.x = 0 parfor i = 1:10 a.x = a.x + 1; end | tmpx = 0 parfor i = 1:10 tmpx = tmpx + 1; end a.x = tmpx; |

#### Requirements for Reduction Assignments

*Reduction Assignments.* In addition to the specific forms of reduction assignment listed in the table in Reduction Variables, the only other (and more general) form of a reduction assignment is

`X = f(X, expr)` | `X = f(expr, X)` |

Required (static): `f` can be a function or a variable. If `f` is a variable, then you cannot change `f` in the `parfor` body (in other words, it is a broadcast variable). |

If `f`

is a variable, then for all practical purposes its value at run time is a function handle. However, as long as the right side can be evaluated, the resulting value is stored in `X`

.

The `parfor`

-loop on the left does not execute correctly because the statement `f = @times`

causes `f`

to be classified as a temporary variable. Therefore `f`

is cleared at the beginning of each iteration. The `parfor`

-loop on the right is correct, because it does not assign `f`

inside the loop.

Invalid | Valid |
---|---|

f = @(x,k)x * k; parfor i = 1:n a = f(a,i); % loop body continued f = @times; % Affects f end | f = @(x,k)x * k; parfor i = 1:n a = f(a,i); % loop body continued end |

The operators `&&`

and `||`

are not listed in the table in Reduction Variables. Except for `&&`

and `||`

, all the matrix operations of MATLAB have a corresponding function `f`

, such that `u op v`

is equivalent to `f(u,v)`

. For `&&`

and `||`

, such a function cannot be written because `u&&v`

and `u||v`

might or might not evaluate `v`

. However, `f(u,v)`

*always* evaluates `v`

before calling `f`

. Therefore `&&`

and `||`

are excluded from the table of allowed reduction assignments for a `parfor`

-loop.

Every reduction assignment has an associated function `f`

. The properties of `f`

that ensure deterministic behavior of a parfor statement are discussed in the following sections.

*Associativity in Reduction Assignments.* The following practice is recommended for the function `f`

, as used in the definition of a reduction variable. However, this rule does not generate an error if not adhered to. Therefore, it is up to you to ensure that your code meets this recommendation.

Recommended: To get deterministic behavior of `parfor` -loops, the reduction function `f` must be associative. |

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)

The classification rules for variables, including reduction variables, are purely syntactic. They cannot determine whether the `f`

you have supplied is truly associative or not. Associativity is assumed, but if you violate this rule, each execution of the loop might result in different answers.

**Note**

The addition of mathematical real numbers is associative. However, the addition of floating-point numbers is only approximately associative. Different executions of this `parfor`

statement might produce values of `X`

with different round-off errors. You cannot avoid this cost of parallelism.

For example, the statement on the left yields 1, while the statement on the right returns 1 + `eps`

:

(1 + eps/2) + eps/2 1 + (eps/2 + eps/2)

Except for the minus operator (`-`

), all special cases listed in the table in Reduction Variables have a corresponding (approximately) associative function. MATLAB calculates the assignment `X = X - expr`

by using `X = X + (-expr)`

. (So, technically, the function for calculating this reduction assignment is `plus`

, not `minus`

.) However, the assignment `X = expr - X`

cannot be written using an associative function, which explains its exclusion from the table.

*Commutativity in Reduction Assignments.* Some associative functions, including `+`

, `.*`

, `min`

, and `max`

, `intersect`

, and `union`

, are also commutative. That is, they satisfy the following for all `a`

and `b`

.

f(a,b) = f(b,a)

Noncommutative functions include `*`

(because matrix multiplication is not commutative for matrices in which both dimensions have size greater than one), `[,]`

, and `[;]`

. Noncommutativity is the reason that consistency in the order of arguments to these functions is required. As a practical matter, a more efficient algorithm is possible when a function is commutative as well as associative, and `parfor`

is optimized to exploit commutativity.

Recommended: Except in the cases of `*` , `[,]` , and `[;]` , 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. |

Violating the restriction on commutativity in a function used for reduction could result in unexpected behavior, even if it does not generate an error.

Unless `f`

is a known noncommutative built-in function, it is assumed to be commutative. There is currently no way to specify a user-defined, noncommutative function in `parfor`

.

Recommended: An overload of `+` , `*` , `.*` , `[,]` , or `[;]` must be associative if it is used in a reduction assignment in a `parfor` -loop. |

Recommended: An overload of `+` , `.*` , `union` , or `intersect` must be commutative. |

Similarly, because of the special treatment of `X = X - expr`

, the following is recommended.

Recommended: An overload of the minus operator (`-` ) must obey the mathematical law that `X - (` is equivalent to `(X - ` . |

#### Using a Custom Reduction Function

In this example, you run computations in a loop and store the maximum value and corresponding loop index. You can use your own reduction function and a `parfor`

-loop to speed up your code. In each iteration, store the value of the computation and the loop index in a 2-element row vector. Use a custom reduction function to compare this vector to a stored vector. If the value from the computation is greater than the stored value, replace the old vector with the new vector.

Create a reduction function `compareValue`

. The function takes two vectors as
inputs: `valueAndIndexA`

and `valueAndIndexB`

. Each
vector contains a value and an index. The reduction function
`compareValue`

returns the vector with the greatest value (first
element).

function v = compareValue(valueAndIndexA, valueAndIndexB) valueA = valueAndIndexA(1); valueB = valueAndIndexB(1); if valueA > valueB v = valueAndIndexA; else v = valueAndIndexB; end end

Create a 1-by-2 vector of all zeros,
`maxValueAndIndex`

.

maxValueAndIndex = [0 0];

`parfor`

-loop. In each iteration, use `rand`

to
create a random value. Then, use the reduction function `compareValue`

to compare `maxValueAndIndex`

to the random value and loop index. When
you store the result as `maxValueAndIndex`

, you use
`maxValueAndIndex`

as a reduction variable.parfor ii = 1:100 % Simulate some actual computation thisValueAndIndex = [rand() ii]; % Compare value maxValueAndIndex = compareValue(maxValueAndIndex, thisValueAndIndex); end

After the `parfor`

-loop finishes running, the reduction variable `maxValueAndIndex`

is available on the client. The first element is the largest random value computed in the `parfor`

-loop, and the second element is the corresponding loop index.

maxValueAndIndex

maxValueAndIndex = 0.9706 89.0000

#### Chaining Reduction Operators

MATLAB classifies assignments of the form `X = expr op X`

or `X = X op expr`

as reduction statements when they are equivalent to the parenthesized assignments `X = (expr) op X`

or `X = X op (expr)`

respectively. `X`

is a variable, `op`

is a reduction operator, and `expr`

is an expression with one or more binary reduction operators. Consequently, due to the MATLAB operator precedence rules, MATLAB might not classify some assignments of the form `X = expr op1 X op2 expr2 ...`

, that chain operators, as reduction statements in `parfor`

-loops.

In this example, MATLAB classifies `X`

as a reduction variable because the assignment is equivalent to `X = X + (1 * 2)`

.

X = 0; parfor i=1:10 X = X + 1 * 2; end

In this example, MATLAB classifies `X`

as a temporary variable because the assignment, equivalent to `X = (X * 1) + 2`

, is not of the form `X = (expr) op X`

or `X = X op (expr)`

.

X = 0; parfor i=1:10 X = X * 1 + 2; end

As a best practice, use parentheses to explicitly specify operator precedence for chained reduction assignments.

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

.)