exponenta event banner

Классификация переменных в parfor- Петли

Обзор

MATLAB ® Coder™ классифицирует переменные внутри parfor-закольцовывание в одну из категорий в следующей таблице. Он не поддерживает переменные, которые он не может классифицировать. Если parfor-loop содержит переменные, которые не могут быть однозначно классифицированы, или, если переменная нарушает ограничения категории, parfor-loop генерирует ошибку.

КлассификацияОписание
ПетляСлужит индексом цикла для массивов
НарезанныйМассив, сегменты которого обрабатываются различными итерациями цикла
ПередачаПеременная, определенная перед циклом, значение которой используется внутри цикла, но не назначено внутри него
СокращениеНакапливает значение в итерациях цикла независимо от порядка итераций
ВременныйПеременная, созданная внутри цикла, но в отличие от разделенных или сокращенных переменных, недоступная вне цикла

Каждая из этих классификаций переменных появляется в этом фрагменте кода:

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 состоит из одного элемента этого массива:

parfor i = 1:length(A)
   B(i) = f(A(i));
end

Характеристики разрезаемой переменной

Переменная в parfor-луп разрезается, если он имеет следующие характеристики:

  • Тип индексирования первого уровня - первый уровень индексирования состоит из скобок, ().

  • Список фиксированных индексов - в скобках первого уровня список индексов одинаков для всех вхождений данной переменной.

  • Форма индексирования - в списке индексов переменной ровно один индекс включает переменную цикла.

  • Форма массива - при назначении разделенной переменной правая сторона назначения не [] или '' (эти операторы указывают на удаление элементов).

Тип индексирования первого уровня. Для разделенной переменной первый уровень индексирования заключен в круглые скобки, (). Например, A(...). При ссылке на переменную с помощью точечной нотации A.x, переменная не разделена.

Переменная A слева не нарезан; переменная A справа нарезается:

A.q(i,12)                        A(i,12).q

Список фиксированных индексов. В скобках первого уровня индексирования разделенной переменной список индексов одинаков для всех вхождений данной переменной.

Переменная B слева не разрезан, потому что B индексируется по i и i+1 в разных местах. Переменная B справа разрезается.

parfor i = 1:10
  B(i) = B(i+1) + 1;
end
parfor i = 1:10
  B(i+1) = B(i+1) + 1;
end

Форма индексирования. В списке индексов для разделенной переменной один индекс имеет вид i, i+k, i-k, k+i, или k-i.

  • i является переменной цикла.

  • k является константой или простой (неиндексированной) переменной.

  • Каждый другой индекс является константой, простой переменной, двоеточием или end.

При использовании других переменных вместе с переменной цикла для индексирования массива невозможно задать эти переменные внутри цикла. Эти переменные являются постоянными в течение выполнения всего parfor заявление. Невозможно объединить переменную цикла с самим собой для формирования индексного выражения.

В следующих примерах: i - переменная цикла, j и k являются неиндексированными переменными.

Переменная A не разрезаетсяПеременная A разрезается
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)

Форма массива. Разделенная переменная должна сохранять постоянную форму. В следующих примерах переменная A не разрезан:

A(i,:) = [];
A(end + 1) = i;

Переменные широковещательной передачи

Широковещательная переменная - это переменная, отличная от переменной цикла, или разделенная переменная, которая не изменяется внутри цикла.

Переменные сокращения

Понижающая переменная накапливает значение, которое зависит от всех итераций вместе, но не зависит от порядка итераций.

В этом примере показан parfor-loop, использующий скалярное понижающее назначение. Используется переменная сокращения x для накопления суммы 10 итерации цикла. Порядок выполнения итераций в потоках не имеет значения.

x = 0;
parfor i = 1:10
   x = x + i;
end
x

Где expr - выражение MATLAB, переменные редукции появляются на обеих сторонах оператора назначения.

X = X + exprX = expr + X
X = X - exprСм. разделы Назначение сокращения, ассоциативность и коммутативность функций сокращения.
X = X .* exprX = expr .* X
X = X * exprX = expr * X
X = X & exprX = expr & X
X = X | exprX = expr | X
X = min(X, expr)X = min(expr, X)
X = max(X, expr)X = max(expr, X)
X=f(X, expr)
Функция f должна быть определяемой пользователем функцией.
X = f(expr, X)
См. разделы Назначение сокращения, ассоциативность и коммутативность функций сокращения.

Каждая из разрешенных инструкций называется назначением сокращения. Переменная сокращения может отображаться только в присвоениях этого типа.

В следующем примере показано типичное использование переменной сокращения X:

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

Этот цикл эквивалентен следующему, где каждый d(i) рассчитывается другой итерацией:

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

Если цикл был обычным for-loop, переменная X в каждой итерации получит свое значение либо перед входом в цикл, либо из предыдущей итерации цикла. Однако это понятие не относится к parfor-контуры.

В parfor-loop, значение X не обновляется непосредственно внутри каждого потока. Скорее, добавления d(i) выполняются в каждом потоке, с i диапазон по подмножеству 1:n выполняется на этом потоке. Затем программное обеспечение накапливает результаты в X.

Аналогичным образом, сокращение:

r=r<op> x(i)
эквивалентно:
r=r<op>x(1)] <op>x(2)...<op>x(n)
Операция <op> сначала применяется к x(1)...x(n), то частичный результат применяется к r.

Если работа <op> принимает два ввода, он должен отвечать одному из следующих критериев:

  • Взять два аргумента typeof(x(i)) и возвращение typeof(x(i))

  • Взять один аргумент typeof(r) и один из typeof(x(i)) и возвращение typeof(r)

Правила для переменных сокращения

Используйте одну и ту же функцию сокращения или операцию во всех назначениях сокращения.  Для переменной сокращения необходимо использовать одну и ту же функцию сокращения или операцию во всех назначениях сокращения для этой переменной. В следующем примере parfor-loop слева недопустим, так как назначение сокращения использует + в одном случае, и * в другом.

Недопустимое использование переменной сокращенияДопустимое использование переменной сокращения
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

Ограничения для параметров функции сокращения и типов возврата.  Сокращение r=r<op> x(i), должны принимать аргументы typeof(x(i)) и возвращение typeof(x(i)) или взять аргументы typeof(r) и typeof(x(i)) и возвращение typeof(r).

В следующем примере в недопустимом цикле r является типом с фиксированной точкой и 2 нет. Чтобы устранить эту проблему, приведите 2 быть того же типа, что и r.

Недопустимое использование переменной сокращенияДопустимое использование переменной сокращения
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

В следующем примере функция сокращения fcn недопустим, поскольку он не обрабатывает регистр при вводе u является фиксированной точкой. ( + и * операции автоматически полиморфны.) Необходимо написать полиморфную версию fcn для обработки ожидаемых типов ввода.

Недопустимое использование переменной сокращенияДопустимое использование переменной сокращения
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

Назначения сокращения, ассоциативность и коммутативность функций сокращения

Назначения сокращения. Кодер MATLAB не позволяет считывать переменные сокращения в любом месте parfor-loop, за исключением операторов сокращения. В следующем примере вызов foo(r) после выписки о сокращении r=r+i приводит к недопустимости цикла.

function r = temp %#codegen
  r = 0;
  parfor i=1:10
    r = r + i;
    foo(r);
  end
end

Ассоциативность в назначениях сокращения. При использовании пользовательской функции f в определении переменной редукции, чтобы получить детерминированное поведение parfor-циклы, функция редукции f должен быть ассоциативным.

Примечание

Если f не является ассоциативным, кодер MATLAB не генерирует ошибку. Необходимо написать код, соответствующий этой рекомендации.

Чтобы быть ассоциативной, функция f должны удовлетворять следующим требованиям для всех a, b, и c:

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

Коммутативность в назначениях сокращения. Некоторые ассоциативные функции, в том числе +, ., min, и max, также являются коммутативными. То есть удовлетворяют следующим для всех a и b:

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

Функция f назначения сокращения должны быть коммутативными. Если f не является коммутативным, различные выполнения цикла могут привести к различным ответам.

Если f является известным некоммутативным встроенным, программное обеспечение предполагает, что он является коммутативным.

Временные переменные

Временная переменная является переменной, которая является целью прямого неиндексированного назначения, но не является переменной сокращения. В следующем parfor-луп, a и d временные переменные:

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

В отличие от поведения for- цикл, перед каждой итерацией parfor-loop, кодер MATLAB эффективно очищает временные переменные. Поскольку итерации должны быть независимыми, значения временных переменных не могут передаваться из одной итерации цикла в другую. Поэтому временные переменные должны быть установлены внутри тела parfor-контур, чтобы их значения определялись отдельно для каждой итерации.

Временная переменная в контексте parfor оператор отличается от переменной с таким же именем, которая существует вне цикла.

Неинициализированные временные элементы

Поскольку временные переменные очищаются в начале каждой итерации, кодер MATLAB может обнаружить определенные случаи, в которых итерация в цикле использует временную переменную до того, как она будет задана в этой итерации. В этом случае кодер MATLAB выдает статическую ошибку, а не ошибку времени выполнения, поскольку есть мало смысла разрешать выполнение в случае возникновения ошибки времени выполнения. Например, предположим, что вы пишете:

  b = true;
  parfor i = 1:n
     if b && some_condition(i)
        do_something(i);
        b = false;
     end
     ...
  end

Этот контур допустим как обычный for-loop, но как parfor-луп, b является временной переменной, поскольку она происходит непосредственно в качестве цели назначения внутри цикла. Поэтому он очищается в начале каждой итерации, поэтому его использование в условии if не инициализирован. (При изменении parfor кому for, значение b предполагает последовательное выполнение цикла, так что do_something(i) выполняется только для нижних значений i до b установлено false.)