cdvmLDr (1158335), страница 7
Текст из файла (страница 7)
/* arrays В1,В2,В3 - the values on the current iteration */
DVM(DISTRIBUTE) float (*A1)[N1+1],(*A2)[N2+1],(*A3)N2+1];
DVM(ALIGN) float (*B1)[N1+1], (*B2)[N2+1], (*B3)[N2+1];
/* description of task array */
DVM(TASK) void *MB[3];
DVM (REMOTE_GROUP) void *RS;
. . .
/* distribution of tasks on processor arrangement */
/* sections and distribution of arrays on tasks */
/* (each section contain third of all the processors) */
NP = NUMBER_OF_PROCESSORS()/3;
DVM(MAP MB[1] ONTO P(0: NP-1));
A1=malloc(M*(N1+1)*sizeof(float));
DVM(REDISTRIBUTE A1[][BLOCK] ONTO MB[1]);
B1=malloc(M*(N1+1)*sizeof(float));
DVM(REALIGN B1[i][j] WITH A1[i][j]);
DVM(MAP MB[2] ONTO P( NP : 2*NP-1));
A2=malloc((M1+1)*(N2+1)*sizeof(float));
DVM(REDISTRIBUTE A2[][BLOCK] ONTO MB[2]);
B2=malloc((M1+1)*(N2+1)*sizeof(float));
DVM(REALIGN B2[i][j] WITH A2[i][j]);
DVM(MAP MB[3] ONTO P(2*NP : 3*NP-1));
A3=malloc((M1+1)*(N2+1)*sizeof(float));
DVM(REDISTRIBUTE A3[][BLOCK] ONTO MB[3]);
B3=malloc((M1+1)*(N2+1)*sizeof(float));
DVM(REALIGN B3[i][j] WITH A3[i][j]);
. . .
FOR(IT,MAXIT)
{. . .
DVM(PREFETCH RS);
/* exchanging edges of adjacent blocks */
. . .
/* distribution of statement blocks on tasks */
DVM(TASK_REGION MB)
{
DVM(ON MB[1]) JACOBY(A1, B1, M, N1+1);
DVM(ON MB[2]) JACOBY(A2, B2, M1+1, N2+1);
DVM(ON MB[3]) JACOBY(A3, B3, M2+1, N2+1);
} /* TASK_REGION */
} /* FOR */
7.7Фрагмент динамической многообластной задачи
Рассмотрим фрагмент программы, которая динамически настраивается на количество областей и размеры каждой области.
#define NA 20 /* NA - maximal number of blocks */
DVM(PROCESSORS) void *R[NUMBER_OF_PROCESSORS()];
int SIZE[2][NA]; /* sizes of dynamic arrays */
/* arrays of pointers for А и В */
DVM(* DISTRIBUTE) float *PA[NA];
DVM(* ALIGN) float *PB[NA];
DVM(TASK) void *PT[NA];
. . .
NP = NUMBER_OF_PROCESSORS( );
/* distribution of arrays on tasks */
/* dynamic allocation of the arrays */
/* and execution of postponed directives */
/* DISTRIBUTE and ALIGN */
FOR(i,NA)
{
DVM(MAP PT[I] ONTO R[ i%(NP/2) : i%(NP/2)+1] );
PA[i] = malloc(SIZE[0][i]*SIZE[1][i]*sizeof(float));
DVM(REDISTRIBUTE (PA[i])[][BLOCK] ONTO PT[I]);
PB[i] = malloc(SIZE[0][i]*SIZE[1][i]*sizeof(float));
DVM(REALIGN (PB[i])[I][J] WITH (PA[i])[I][J])
} /*FOR i */
. . .
/* distribution of computations on tasks */
DVM(TASK_REGION PT) {
DVM(PARALLEL[i] ON PT[i])
FOR(i,NA)
JACOBY(PA[i], PB[i], SIZE[0][i], SIZE[1][i]);
}
} /* TASK_REGION */
Массивы (области) циклически распределяются на секции из 2-х процессоров. Если NA > NP/2, то на некоторые секции будет распределено несколько подзадач. Витки циклов, распределенные на одну секцию, будут выполняться последовательно в модели параллелизма по данным.
8Процедуры
Вызов процедуры из параллельного цикла.
Процедура, вызываемая из параллельного цикла, не должна иметь побочных эффектов и содержать обменов между процессорами (прозрачная процедура). Как следствие этого, прозрачная процедура не содержит операторов ввода-вывода и DVM-директив;
Вызов процедуры вне параллельного цикла.
Если фактическим аргументом является явно распределенный массив (DISTRIBUTE или ALIGN), то он должен передаваться без изменения формы. Это означает, что фактический аргумент является ссылкой на начало массива, а соответствующий формальный аргумент имеет конфигурацию, полностью совпадающую с конфигурацией фактического аргумента.
Формальные параметры.
Если фактическим параметром функции может быть распределенный массив, то формальный параметр должен быть описан следующим образом:
-
Если распределение фактического параметра известно, то директивой
DVM(*DISTRIBUTE rd1...rdN); -
Если известна только размерность, но не конкретное распределение, то директивой DVM(*DISTRIBUTE[*]...[*]);
-
Если фактический параметр может быть произвольным (распределенным или нераспределенным) массивом, то директивой DVM(*). Такой параметр может использоваться только в функциях fread/fwrite.
Локальные массивы.
Локальные массивы могут распределяться в процедуре директивами DISTRIBUTE и ALIGN. Локальный массив может быть выровнен на формальный аргумент. Директива DISTRIBUTE распределяет локальный массив на ту подсистему процессоров, на которой была вызвана процедура (текущая подсистема). Если в директиве DISTRIBUTE указана секция массива процессоров, то количество этих процессоров должно быть равно количеству процессоров текущей подсистемы.
Пример 9.1. Распределение локальных массивов и формальных аргументов.
void dist(
/* explicit distribution of formal argument */
DVM(*DISTRIBUTE[][BLOCK]) float *A /* N*N */,
/* aligned formal argument */
DVM(*ALIGN [i][j] WITH A[i][j]) float *B /* N*N */,
/* inherited distribution of the formal argument */
DVM(*) float *C /* N*N */,
int N)
{
DVM(PROCESSORS) void *PA[NUMBER_OF_PROCECCORS()];
/* local array aligned with formal argument */
DVM(ALIGN[I][J] WITH C[I][J]) float *X;
/* distributed local array */
DVM(DISTRIBUTE [][BLOCK] ONTO PA) float *Y;
}
Замечание. Размеры массивов фактических параметров неизвестны, поэтому здесь должна использоваться техника работы с динамическими многомерными массивами, описанная в разделе 4.2.3.
9Ввод/вывод
В C-DVM, разрешены следующие операции ввода/вывода для размноженных данных:
fopen, fclose, feof, fprintf, printf, fscanf, fputc, putc, fputs, puts, fgetc, getc, fgets, gets, fflush, fseek, ftell, ferror, clearerr, perror, as well as fread, fwrite.
Эти операторы выполняются на выделенном процессоре (процессоре ввода/вывода). Выводимые значения берутся на этом процессоре, а вводимые – вводятся на нем и рассылаются на остальные процессоры.
Для распределенных данных обеспечивается только бесформатный ввод/вывод массива целиком функциями fread/fwrite.
Функции ввода/вывода не следует использовать в параллельных циклах и в блоках TASK_REGION, т.к. выводимые данные окажутся в файле (на печати) в непредсказуемом порядке.
10Ограничения на использование языка СИ
C-DVM предназначен для распараллеливания вычислительных программ, написанных в "фортрано-подобном" стиле. При этом на использование средств языка СИ накладываются некоторые ограничения. В первую очередь эти органичения касаются использования указателей на распределенные массивы и их элементы. C-DVM не запрещает использовать указатели, но некоторые операции с ними могут стать некорректными после конвертирования программы. Например:
-
идентификатор массива (или указателя на массив) становится идентификатором массива целых чисел (так называемого хендлера массива);
-
процессор содержит только локальную часть массива, поэтому относительный доступ к его элементам с помощью адресной арифметики невозможен;
-
при перераспределении массива меняется состав и расположение локальной части массива, поэтому сохраненные указатели на элементы становятся недействительными.
Второе ограничение касается типов данных: элементы распределенных массивов могут быть только скалярных типов int, long, float и double.
Далее, некоторые описания для распараллеливания требуют выполнения определенных действий. Это например:
-
описания процессорных секций и массивов задач (но не отображение задач);
-
описание распределенных массивов или темплейтов с константными размерностями ("статических");
-
инициализация переменных (требуется их регистрация для отладчика).
Такого рода неявные действия для глобальных объектов выполняются в начале функции main сразу после инициализации системы поддержки. Поэтому в момент генерации main все такие объекты должны быть известны, т.е. не могут находиться в отдельно транслируемом файле или следовать за описанием функции main. Для объектов, локальных в функции или блоке, эти действия выполняются перед первым оператором блока. Кроме того заметим, что неявные действия выполняются в порядке описаний, и это накладывает определенные семантические ограничения на порядок описаний: база ALIGN должна быть описана до выравниваемого массива и т.п.
И наконец, использование препроцессора, выходящее за пределы файлов заголовков, требует осторожности по нескольким причинам. Во-первых, C-DVM компилятор работает до препроцессора. Поэтому, например, пара определений
#define begin {
#define end }
может сделать программу совершенно непонятной для компилятора. По этой же причине DVM-директивы в файле заголовков остаются неизвестными компилятору. Следовательно, распределенные данные должны быть описаны явно в каждом файле. Во-вторых, для работы с многомерными массивами (см. 4.2) предлагается использовать препроцессор для временного переопределения размеров массивов как констант или для определения макрокоманд, моделирующих многомерный массив на одномерном. В конвертированной программе соответствующие препроцессорные директивы должны быть удалены. Это реализовано тем, что все директивы препроцессора в исходном порядке помещаются в начало выходного файла. (Заметим, что для каждой вспомогательной директивы #define требуется директива #undef, отменяющая ее.)
11Отличия версии 2.0 от версии 1.0
C-DVM 1.0 является подмножеством C-DVM 2.0. Добавлены следующие новые возможности:
-
Явное определение множеств процессоров (раздел 3).
-
Параллелизм задач (раздел 7).
-
Форматы GEN_BLOCK и WGT_BLOCK для распределения массивов (разделы 4.1.2, 4.1.3).
-
Распараллеливание циклов с регулярными зависимостями по данным (раздел 6.2.3).
-
Более точная спецификация удаленных данных, обеспечивающая эффективный доступ к ним (раздел 6.3).
11.1Копирование секций массивов
Язык C-DVM предоставляет средства для копирования распределенных массивов (секций распределенных массивов), которые позволяют обеспечить совмещение обмена данных с вычислениями.
Прямоугольная секция массива задается триплетами (<начало>:<конец>:<шаг>) по каждой размерности массива. Для присваивания секции должны иметь одинаковые ранги, т.е. одинаковое число невырожденных размерностей (массивы при этом могут иметь разные ранги), одинаковое число элементов в соответствующих невырожденных размерностях секции источника и получателя.
Для того, чтобы C-DVM программа могла компилироваться и выполняться как обычная последовательная программа, копирование массивов (секций) записывается как обычный многомерный цикл. Тело цикла должно состоять из единственного оператора присваивания. Заголовки цикла должны быть записаны макрокомандами DO(v,first,last,step) или FOR(v,times). Все переменные цикла должны использоваться в индексных выражениях правой и левой части оператора присваивания, причем в одном и том же порядке. Каждое индексное выражение может содержать только одну переменную цикла и должно зависеть от нее линейно.
Для асинхронного копирования нужно:
-
объявить директивой COPY_FLAG переменную -- флаг завершения операции копирования;
-
перед циклом указать директиву COPY_START с адресом переменной-флага;
-
перед использованием присвоенных значений убедиться в завершении (дождаться завершения) операции копирования директивой COPY_WAIT с адресом той же переменной.
Пример 11.1. Асинхронное копирование.
DVM(DISTRIBUTE [BLOCK][]) float A[N][N];
DVM(ALIGN [i][j] WITH [j][i]) float B[N][N];
. . .
DVM(COPY_FLAG) void * flag;
. . .
DVM(COPY_START &flag)