\subsection{Алгоритмы распараллеливания с распределением данных}

Рассмотрим подробнее устройство проходов, отвечающих за построение схемы распределения данных и распараллеливании (см. Рис. \ref{fig:distr-alg}).

Начнём с прохода LOOP\_ANALYZER\_DATA\_DIST\_S0. На вход он получает АСТ, заполненные структуры \texttt{FuncInfo} и \texttt{LoopGraph} для всей программы. Результатом данного прохода является заполненный граф измерений массивов. 

В проходе происходит итерация по всем структурам \texttt{FuncInfo}, то есть по каждой функции. Для каждой функции выполняется следующее:

\begin{itemize}
    \item выполняется проход по всем операторам функции;

    \item если этот оператор содержит запись в элемент массива или чтение из массива и этот массив распределяемый, то есть он не является нигде приватным или редукционным, происходит отображение этой операции на содержащие её циклы;

    \item отображение заключается в сохранении записей вида \textit{<цикл, массив, тип операции, номер измерения, A, B>} во временную структуру. Такая запись создаётся для каждого измерения, обращение по которому в текущей операции имеет вид \textit{A*i+B}, где \textit{A} и \textit{B} - целые числа, \textit{i} - счётчик цикла.

    \item после похода по оператором, для каждого цикла проводится анализ зависимостей по данным. То есть проверка на то, что никакой виток цикла не использует данные, записываемые другими витками. Исключения составляют зависимости с фиксированным расстоянием между записывающим и читающим витками циклов. При наличии зависимостей в цикле, в структуру \texttt{LoopGraph} заносится соответствующая пометка.

    \item далее происходит добавление полученных записей об обращениях к массив в специальную структура данных -- граф измерений массивов. Эта структура используется для построения схемы распределения данных и представляет собой граф, вершинами которого являются измерения массивов, в дугами -- связи между измерениями массивов согласно их использованию в циклах программы (см. пример на Рис. \ref{fig:array_graph}). Подробно построение графа массивов описано в \cite{disser}; 

    \item после обработки всех функций проход завершает работу;
\end{itemize}

\begin{figure}[h]
\centering
\includegraphics[scale=0.5]{src/assets/array_graph.png}
\caption{пример графа измерений массивов.}
\label{fig:array_graph}
\end{figure}

Далее запускается проход LOOP\_ANALYZER\_DATA\_DIST\_S1. Входные данные у него такие же, как и у LOOP\_ANALYZER\_DATA\_DIST\_S0. Его цель -- собрать информацию о зависимостях по данным в циклах и обнавить соответствующие флаги в структурах \texttt{LoopGraph}.

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

\begin{itemize}
    \item опять выполняется проход по всем операторам функции;

    \item точно так же обрабатываются обращения к массивам с сохранением записей вида \textit{<цикл, массив, тип операции, номер измерения, A, B>};

    \item вместо добавления информации в граф измерений массивов, выявляются зависимости по данным внутри циклов путём построения специального графа зависимостей;

    \item для каждого цикла, который имеет зависимости по данным, заносится пометка в структуру \texttt{LoopGraph} о том, что цикл имеет зависимость по данным и не может быть распараллелен;

    \item исключения составляют зависимости фиксированной длинны, то есть такие, что номера записывающих и читающих витков отличаются на фиксированную величину. Такие циклы могут быть распараллелены с помощью директивы ACROSS (см. пример на Рис. \ref{fig:hp} в главе \ref{sec:benchmark});

    \item после обработки всех функций проход завершает работу;
\end{itemize}

Проход LOOP\_ANALYZER\_DATA\_DIST\_S2 получает на вход граф измерений массивов. Результатом прохода является усечённый граф измерений массивов (такого графа измерений массивов, который не содержит циклов, порождающие конфликтные ситуации) для последующего создания распределения данных.

Он имеет следующий алгоритм:

\begin{itemize}
    \item рассматривается граф измерений массивов;

    \item происходит поиск циклов в графе;

    \item из графа удаляются дуги так, чтобы разорвать циклы, приводящие к конфликтам распределения данных. В последствии удаление дуг приведёт к появлению директив удалённого доступа к данным;
\end{itemize}

Проход CRATE\_TEMPLATE\_LINKS получает на вход оптимизированный граф измерений массивов, в котором отсутствуют циклы. Поскольку в этом графе нет циклов, все его компоненты связности представляют собой деревья. В каждом таком дереве находится массив с наибольшей размерностью и по нему создаётся служебный массив-шаблон, на который выравниваются все массивы рассматриваемого дерева. Таким образом, на вход этот проход принимает граф измерений массивов, а на выходе получаются директивы распределения и выравнивания данных, представленные структурой \texttt{DataDirective}, хранящей всю необходимую информацию.

После этого запускается проход LOOP\_ANALYZER\_COMP\_DIST распределения вычислений согласно построенному распределению данных. На вход он получает АСТ программы, получает АСТ, структуры \texttt{FuncInfo}, \texttt{LoopGraph}, граф измерений массивов и построенное распределение данных. Общая схема работы совпадает с LOOP\_ANALYZER\_DATA\_DIST\_S0 и LOOP\_ANALYZER\_DATA\_DIST\_S1: происходит итерация по всем функциям и заполнение кортежей \textit{<цикл, массив, тип операции, номер измерения, A, B>}. После заполнения этой информации, происходят следующие действия:

\begin{itemize}
    \item по отдельности рассматривается каждый цикл;

    \item по группе флагов структуры \texttt{LoopNode} определяется, есть ли факторы, препятствующие распараллеливанию цикла.

    \item среди всех массивов, используемых в цикле, выбирается лучший для того, чтобы относительно него распределить витки циклы. Предпочтение отдаётся массивам, в которые происходит запись и имеют наибольшее количество измерений. Если таких нет, выбираются массивы, из которых происходит чтение.

    \item для каждого цикла без ограничений на распараллеливание создаётся структура типа \texttt{ParallelDirective}, в которую записывается выбранный массив и другая информация, такая как ACROSS-зависимости и др., извлекаемая в том числе из графа измерений массивов;

    \item полученные структуры \texttt{ParallelDirective} сохраняются в соответствующих структурах \texttt{LoopNode}.

    \item после создание всех директив, для каждого тесновложенного гнезда параллельных циклов происходит объединение \texttt{ParallelDirective} для того, чтобы распараллеливался не только самый верхний цикл, а всё гнездо.
\end{itemize}

Следом вызывается проход CREATE\_PARALLEL\_DIRS. На вход он получает построенные директивы распределения данных и директивы распараллеливания циклов в виде внутренних структур системы SAPFOR \texttt{DataDirective} и \texttt{ParallelDirective}.

Сначала рассматриваются директивы распределения данных. Для каждой директивы, по информации из структуры, строится её текстовое представление, в виде строк конструируются такие DVM-директивы как ALIGN и DISTRIBUTE.

Далее аналогичный процесс происходит с директивами распараллеливания: формируются директивы PARALLEL ON и все нужные клаузы, такие как PRIVATE, REDUCTION, ACROSS, SHADOW\_RENEW, REMOTE\_ACCESS.

Текстовые директивы DVM-системы сохраняются в виде множества структур \texttt{CreatedDirective}, которые хранят текст директивы и строку, перед которой её необходимо будет вставить.

Наконец, проход INSERT\_PARALLEL\_DIRS, получающий множество созданных директив, производит их вставку в виде комментариев в АСТ обрабатываемой программы перед соответствующими операторами.

