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

Обзор

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

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

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

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 - цикл, который использует скалярное присвоение сокращения. Это использует переменную 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 - цикл, то переменная X в каждой итерации получила бы свое значение или прежде, чем ввести цикл или от предыдущей итерации цикла. Однако эта концепция не применяется к parfor - циклы.

В parfor - цикл, значение 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 - цикл слева не допустим, потому что присвоение сокращения использует + в одном экземпляре и * в другом.

Недопустимое использование переменной сокращенияДопустимое использование переменной сокращения
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 Coder не позволяет переменным сокращения быть считанными где угодно в parfor - цикл кроме операторов сокращения. В следующем примере вызов 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 Coder не генерирует ошибку. Необходимо записать код, который выполняет этой рекомендации.

Чтобы быть ассоциативным, функциональный 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 - цикл, MATLAB Coder эффективно очищает временные переменные. Поскольку итерации должны быть независимыми, значения временных переменных не могут быть переданы от одной итерации цикла другому. Поэтому временные переменные должны быть установлены в теле parfor - цикл, так, чтобы их значения были заданы отдельно для каждой итерации.

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

Неинициализированные временные файлы

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

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

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