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

Обзор

MATLAB® Coder™ классифицирует переменные внутри parfor-включиться в одну из категорий в следующей таблице. Он не поддерживает переменные, которые не может классифицировать. Если a 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 является фиксированной точкой. (The + и * операции являются автоматически полиморфными.) Необходимо написать полиморфную версию 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

В отличие от поведения a 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.)