Работаем на параллельном компьютере ParsytecCC/16

М. П. Крутиков


Введение

Фирма Parsytec (Германия) производит многопроцессорные вычислительные системы архитектуры MIMD. Машина Parsytec CC/16 представляет собой 16-процессорную систему, в которой имеется 16 узлов (по одному процессору на узел).

При ближайшем рассмотрении, выясняется, что каждый узел представляет собой ``нормальную'' машину, со своим жестким диском, своей оперативной памятью. Более того, на каждом узле запущена своя копия операционной системы. Что же позволяет считать эту машину параллельным суперкомпьютером? А вот что: все узлы оборудованы специальным коммуникационным устройством, которое позволяет осуществлять быстрый обмен информацией между узлами. Таким образом, появляется возможность синхронизации процессов, запущенных на разных узлах, и информационного обмена.

Итак, Parsytec CC - это 16 компьютеров, связанных быстрой сетью. Возможности параллельного исполнения задания опираются на работу с ``быстрыми связями''. Интерфейсом к параллельной системе является операционная система PARIX (точнее Embedded PARIX), позволяющая скрыть от пользователя детали синхронного запуска задач.

1 Кирпичик

Что представляет собой узел? Каждый узел системы Parsytec CC/16 есть компьютер, построенный на процессоре PowerPC 604 фирмы Motorola, который работает на тактовой частоте 133Мгц. Стандартная материнская плата имеет PCI-слоты, в один из которых вставлена плата ``скоростной связи'' (High Speed Link). Кроме HSL, каждый узел имеет стандартный сетевой интерфейс Ethernet-TP. Кроме того, некоторые узлы оборудованы дополнительной периферией (см. ниже).

Каждый узел имеет свой SCSI жесткий диск, с которого он загружается. Размер оперативной памяти в нашей конфигурации - 32Мбайт на узел.

2 Архитектура и физическая конфигурация

Два типа элементов составляют систему: узлы и роутеры. Выше мы познакомились с отдельным узлом. Узел - это машина, из которой ``торчит'' HSL. Этот HSL входит в один из портов роутера. Функцией роутера является коммутация пакетов информации между узлами. В роутере восемь портов, каждый из которых может замыкаться либо на узел, либо на другой роутер. Таким образом строится физическая конфигурация машины, которая задается количеством роутеров, количеством узлов, и топологией связей между узлами и роутерами. В системе Parsytec CC/16 имеется 16 узлов и 4 роутера.

3 Типы узлов

Не все узлы равны между собой.

4 PARIX и AIX

На каждом узле функционирует операционная система AIX (эта UNIX-подобная операционная система разработана фирмой IBM для персональных компьютеров и рабочих станций на основе процессоров PowerPC). Сама по себе ОС AIX функционирует на отдельно стоящем узле и не может реализовать параллельную обработку задачи.

Для решения параллельных задач используется PARIX (Parallel extensions to UNIX), которая реализована в виде надстройки над ОС AIX. При обращении к ОС PARIX, последняя захватывает ресурсы всех процессоров в указанном разделе и запускает на них вашу прикладную задачу (о разделах смотри ниже).

Важно, что PARIX - это однозадачная система: если один пользователь запустил PARIX-приложение на разделе, то никакой другой пользователь уже не сможет запустить своего PARIX-приложения на данном разделе, покуда первое приложение не закончит свою работу и не освободит раздел.

С одной стороны, такой подход несомненно ограничивает пользователей, заставляя их строго координировать свои расчетные задачи. С другой же стороны, такая схема обеспечивает наиболее благоприятный режим для запущенного приложения.

5 Логическая конфигурация и разделы

Принята следующая модель представления многопроцессорной системы: все процессоры ``заполняют'' ячейки в некоем трехмерном ``ящике''. Именно: каждому процессору приписаны уникальные координаты (тройка целых положительных чисел). Так что с точки зрения пользователя процессор кроме своего порядкового номера имеет ``координаты''.

В трехмерном ящике с узлами не может быть дыр: если существует процессор с координатами (0,0,0) и существует (0,0,2), значит обязан существовать и (0,0,1). Кстати, узел (0,0,0) существует всегда (если в системе ненулевое количество процессоров).

Сие означает, что логическое пространство, занимаемое процессорами можно характеризовать положением угловых узлов. В нашей системе логическая конфигурация линейна: (0,0,0)-(15,0,0). Логическая конфигурация может быть (относительно легко) изменена. В принципе в системе с 16 процессорами возможны также конфигурации вида квадрата (0,0,0)-(4,4,0), (0,0,0)-(4,2,2) и др. Удобство той или иной конфигурации определяется в конечном счете характером решаемых задач (так, для расчетов матриц, вероятно, наиболее удобна конфигурация (0,0,0)-(4,4,0)).

Конечная трехмерная решетка, включающая в себя все процессоры данной машины называется основным разделом. Обычно основной раздел делится на несколько более мелких (и, возможно, перекрывающихся) разделов. Самым мелким, понятно, является раздел, состоящий из одного узла. Например, (0,0,0)-(15,0,0) у нас поделен на четыре четырехпроцессорных раздела (0,0,0)-(3,0,0), (4,0,0)-(7,0,0), (8,0,0)-(11,0,0) и (12,0,0)-(13,0,0) и шестнадцать однопроцессорных раздела. В общем случае, разделы могут перекрываться. Важнейшей характеристикой раздела является его размер, т.е. размер трехмерного ящика с узлами, входящими в данный раздел. Размер раздела (0,0,0)-(15,0,0) есть (4,1,1).

ОС PARIX может запускать приложения не только на основном разделе, но и на любом из логических разделов. Так, можно запустить задачу на четырехпроцессорном разделе. При этом останутся свободными три четырехпроцессорных раздела. На них также можно что-нибудь запустить. В вырожденном случае можно запустить одновременно 16 PARIX-приложений, но каждое, увы, будет иметь лишь один процессор. Как ведет себя PARIX, если разделы перекрываются? Так: если из двух перекрывающихся разделов один занят, то на другом не может быть запущено PARIX-приложение (блокируется).

И последнее (перед последующим). Ваша прикладная задача может узнать о параметрах раздела, на котором она запущена. Однако, точка отсчета раздела всегда транслируется в ноль, так что, приложение не может отличить, скажем, запущено оно на (0,0,0)-(3,0,0) или на (8,0,0)-(11,0,0). Оба случая для прикладной задачи будут выглядеть как раздел (0,0,0)-(3,0,0) с размером (4,1,1).

6 Логические связи

Прикладная задача, прежде чем приступить к выполнению расчета, должна позаботиться об организации межпроцессорных связей для обмена данными. Эти связи выглядят как коммуникационный канал (канал обмена данными) типа ``точка-точка'', то есть идет системный вызов ``создать соединение между таким-то узлом и таким-то узлом''. Топология создаваемой системы определяется исключительно характером конкретной задачи. Так, в одном случае полезно организовать ``звезду'', а в другом - ``кольцо''.

Еще раз подчеркнем отличие логических связей от физических:

7 Подумаем

Немного выводов из представленного выше описания архитектуры системы. Итак, система представляет собой многопроцессорную машину с раздельной (не разделяемой!) оперативной памятью. Синхронизация и обмен данными между отдельными процессорами происходит по специальным быстрым коммуникационным каналам. Поэтому, необходимо в расчетном алгоритме находить разумный компромисс между объемом вычислительной работы и объемом передаваемых данных. В случае неразумно большого потока обмена возможно понижение производительности, обусловленное конечной (хоть и достаточно большой) пропускной способностью канала.

Задача должна строиться так, чтобы объем требуемой оперативной памяти не превышал 32Mb (имеется в виду та часть задачи, которая будет выполняться на отдельном узле). То, что в целом, в системе 32x16=512Мb оперативной памяти вовсе не означает, что вы можете работать с массивом размером 512Mb!

Следует ответственно подходить к разработке схемы коммуникации между узлами, поскольку от этого может сильно зависеть производительность.

Очень важно понимать конфигурацию системы. Так, если в задаче очень много дисковых операций, следует запускать ее на том разделе, где есть IO-node с теми дисками, с которыми система работает, иначе данные будут передаваться через внутреннюю сеть машины (не через HSL), что довольно медленно. Наоборот, если задача чисто расчетная следует избегать использования разделов с IO-узлами, поскольку эти узлы могут быть загружены обменом данными. Особенно неприятен в этом случае Entry-node, на котором запущенное PARIX-приложение будет конкурировать за процессорное время с задачами других пользователей: компиляция и отладка программ.

Прикладная программа должна быть написана так, чтобы ее можно было запустить на произвольном количестве процессоров (масштабируема), поскольку на каком именно разделе она будет выполняться определяется пользователем при ее запуске.

Наконец, должно быть ясно, что адаптация вашей программы под PARIX есть не очень простая задача, и здесь нельзя отделаться опцией компилятору (как в ConvexOS). Вопрос: а что будет, если кто-нибудь просто откомпилирует свою задачу, не внося в нее изменений? Ответ: она будет успешно откомпилирована, однако если ее запустить, то задача будет считаться параллельно (и независимо) от начала до конца на каждом из процессоров в разделе. Так что для 16-процессорного раздела вы получите 16 идентичных результатов. Скорость счета будет равна скорости счета на одном процессоре. Из этого должен последовать неизбежный вывод о том, что, увы, придется трудиться, если есть желание использовать все мощности системы Parsytec CC/16.

8 Пользовательский интерфейс

Зарегистрировавшись на машине, вы попадаете в знакомую среду командного интерпретатора csh ОС UNIX (точнее - AIX). На машине есть все обычные для UNIX средства работы: редакторы vi и emacs, отладчики, компиляторы. Это и не удивительно - ведь вы попали на однопроцессорную UNIX-станцию, являющуюся Entry-узлом многопроцессорной машины.

Следующие, характерные для многопроцессорной системы, задачи вам предстоит решать:

С точки зрения пользователя, обращение к возможностям ОС PARIX происходит с помощью команды px

px command

Собственно команда px модифицирует пользовательское окружение, так что command понимается уже как команда ОС PARIX. Например,

px man run

выдаст manual pages из ОС PARIX, в то время как просто команда man ... выдает справочную информацию по AIX.

9 Компиляторы

Чтобы ваша прикладная программа могла быть запущена под управлением ОС PARIX необходимо, чтобы она была скомпилирована специальным образом. Чем же отличаются программы PARIX от программ AIX? Приложения PARIX используют другие библиотечные функции для операций ввода/вывода, а также специальные библиотечные функции (из библиотек PARIX).

Приложения AIX не будут работать, если их запустить на разделе PARIX, и наоборот, AIX не ``поймет'' запросов запущенного приложения PARIX. Так что PARIXу - PARIXово.

Parsytec CC/16 оснащен стандартными для AIX компиляторами языков C и FORTRAN. С помощью этих компиляторов вы можете создать как приложение для AIX, так и приложение для PARIX. Для приложений AIX вы используете команду

ancc -o test test.c

Тогда как для того, чтобы сделать исполняемый файл для PARIX, надо дать команду

px ancc -o test.px test.c

Как обычно, команда-префикс px модифицирует следующую за ней команду так, что она превращается в команду ОС PARIX.

Отметим, что по соглашению исполняемые файлы PARIX имеют суффикс .px. Это позволяет отличать их от приложений, скомпилированных для AIX.

Компилятор языка FORTRAN вызывается командой f77. Если вы компилируете PARIX приложение, следует использовать команду px f77, например

px f77 -o test.px test.f

10 Выбор раздела

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

Разделы имеют атрибуты. Основным атрибутом является количество процессоров.

Каждый раздел может использовать Entry-узел и IO-узлы машины для операций ввода-вывода.

Управление разделами осуществляется командой OC PARIX nrm (Network Resource Manager). Для просмотра конфигурации разделов, используется команда

px nrm -pc

которая выводит список определенных в системе разделов и их атрибуты:


ultra10% px nrm -pc



Partition | Attributes | Servers

----------+------------+--------

p1        |            |

earth     | p16        |

east      | p4         |

north     | p4         |

south     | p4         |

west      | p4         |

Более подробную информацию даст команда

px nrm -pp

где будет видна привязка разделов к физической конфигурации системы:


ultra10% px nrm -pp



CC Configuration: Wed Jul 24 13:12:23 1996 edited by root@ultra10



CC ParsytecCC: - no init info -



    Topologie = using file /epx/epx/nrm/system.rt, Dimensions (16,1,1),

Command Port 12544



    Node ultra10 at position (0,0,0) with attributes ( master-epxd

IO-node )

    Node cc001736 at position (1,0,0) with attributes ( P-node )

    Node cc001737 at position (2,0,0) with attributes ( P-node )

    Node cc002245 at position (3,0,0) with attributes ( IO-node )

    Node cc002243 at position (4,0,0) with attributes ( IO-node )

    Node cc001738 at position (5,0,0) with attributes ( P-node )

    Node cc001739 at position (6,0,0) with attributes ( P-node )

    Node cc002246 at position (7,0,0) with attributes ( P-node )

    Node cc001732 at position (8,0,0) with attributes ( P-node )

    Node cc001733 at position (9,0,0) with attributes ( P-node )

    Node cc001734 at position (10,0,0) with attributes ( P-node )

    Node cc001735 at position (11,0,0) with attributes ( P-node )

    Node cc001740 at position (12,0,0) with attributes ( P-node )

    Node cc001744 at position (13,0,0) with attributes ( P-node )

    Node cc001743 at position (14,0,0) with attributes ( P-node )

    Node cc001742 at position (15,0,0) with attributes ( P-node )



    Server filesrv with command line (/epx/bin/dserver -h)



    Partition p1: corner atoms = (0,0,0) (0,0,0)

                  servers = ( )

    Partition earth: attributes = ( p16 ), corner atoms = (0,0,0)

    (15,0,0)

                     servers = ( )

    Partition east: attributes = ( p4 ), corner atoms = (0,0,0)

    (3,0,0)

                    servers = ( )

    Partition north: attributes = ( p4 ), corner atoms = (4,0,0)

    (7,0,0)

                     servers = ( )

    Partition south: attributes = ( p4 ), corner atoms = (8,0,0)

    (11,0,0)

                     servers = ( )

    Partition west: attributes = ( p4 ), corner atoms = (12,0,0)

    (15,0,0)

                    servers = ( )

Следующая команда

px nrm -pa

показывает статус разделов (занят/свободен) и имя пользователя, занявшего раздел. Разделы, помеченные звездочкой являются свободными:


ultra10% px nrm -pa



CC ParsytecCC:

Partition | Used by      | Since

----------+--------------+-------------------------

p1        | east         |

earth     | east         |

east      | mike@ultra10 | Sat Nov 16 22:52:39 1996

north     | *            | Mon Nov 11 14:31:58 1996

south     | *            | Mon Nov 11 14:31:58 1996

west      | *            | Mon Nov 11 14:31:58 1996

Из данной таблицы видно, что раздел east занят пользователем mike. При этом, блокируется раздел earth, т.к. он перекрывается с east (раздел earth является основным разделом и, естественно, он блокируется при запуске приложения на любом из подразделов), а также блокирован раздел p1, т.к. этот однопроцессорный раздел содержит узел из раздела east. Свободными остались три четырехпроцессорных раздела north, south и west.

Другой способ представления той же информации дает команда

px nrm -pm

которая представляет систему наглядно в виде матрицы, где элементу соответствует один процессор:


ultra10% px nrm -pm



CC ParsytecCC:

Actual users of CC ParsytecCC:



  A         :  mike@ultra10



Allocation of CC ParsytecCC:



  AAAA************

11 Запуск PARIX приложения

Для запуска PARIX-приложения используется команда run. При этом вы обязательно должны указать раздел, на котором должно быть запущено приложение.

Раздел можно именовать либо явно (тем именем, которое присвоено разделу администратором - см. px nrm -pc), либо неявно - указанием атрибутов раздела. Последнее более удобно, так как вы можете просто сказать, что ``требуется раздел с 4 процессорами'', и система выберет вам первый свободный 4-процессорный раздел.

Например

px run -a earth hello.px

Запускает PARIX-приложение hello.px на разделе с именем earth,

px run -a p4 hello.px

Запускает ту же программу на 4-х процессорном разделе.

Отметим, что команда run занимает указанный раздел, так что он (вместе с перекрывающимися разделами) становится недоступен для запуска других приложений PARIX.

12 Освобождение раздела

По окончании PARIX-приложения, run автоматически освобождает занятый раздел, так что он становится доступен для других PARIX-приложений.

Однако в ряде случаев (при аварийном завершении) раздел может остаться занятым. То есть, несмотря на то, что никакое приложение не исполняется на разделе, он остается недоступен для других пользователей. Поэтому будьте очень внимательны, и убедитесь, что уходя из системы, вы не оставили блокированных за вами разделов. Используйте для этого команду px nrm -pa. Разблокировать раздел можете либо вы сами, либо системный администратор.

Для явного освобождения раздела дайте команду PARIX nrm -f, например

px nrm -f earth

Отметим, что при освобождении раздела требуется указать имя раздела (а не его атрибуты).

13 Виртуальные процессоры

Напомним, что приложение может узнать о конфигурации раздела, на котором оно запущено. Более того, нормальное PARIX-приложение обязательно использует эту информацию для организации взаимодействия между процессорами. Некоторые приложения требуют жестких ограничений на конфигурацию своего раздела. Например, приложение может проверять количество процессоров в разделе и завершать свою работу в случае, если количество процессоров меньше 4. Можно ли запустить такое приложение на 2-процессорном разделе? Да, можно.

PARIX позволяет организовывать виртуальную конфигурацию раздела. Так, можно потребовать, чтобы раздел, который имеет размеры (2,1,1) выглядел для приложения как раздел (2,2,1). Такая процедура называется организацией виртуального раздела.

Существует ограничение: каждый физический процессор может эмулировать не более 16 виртуальных. Так что на Parsytec CC/16 нельзя организовать виртуальный раздел с числом процессоров более 256.

Когда полезны виртуальные разделы?

Для создания виртуального раздела используются аргументы к команде run:

px run -a earth 4 2 2 test.px

запускает приложение на физическом разделе earth, но приложение ``видит'' виртуальный раздел с конфигурацией (4,2,2).

14 Программная модель

Принята следующая модель работы PARIX-приложения.

На каждом узле имеется набор глобальных данных (доступных программе), которые определяют положение узла в разделе. Именно

Обмен данных основан на концепции виртуальных связей между процессорами, которые образуют каналы ``точка-точка'' между любыми процессорами. Набор созданных виртуальных связей задает виртуальную топологию (решетка, конвейер, дерево, гиперкуб).

В PARIX связи могут быть одного из следующих трех видов:

15 Пример программы на языке C

Начнем с классического примера: программы ``hello, world''.

#include < stdio.h> 



int main() {

  printf("Hello, world\n");

  return 0;

  }

Теперь надо построить PARIX-приложение. Для этого даем команду

px ancc -o hello.px hello.c

Теперь мы получили исполняемый файл hello.px, который можно запустить. Действительно, команда

px run -a p4 hello.px

запустит PARIX-приложение на разделе из 4-х процессоров и вы получите четыре строчки (ответит каждый процессор) Hello, world

Теперь немного модифицируем программу.


#include < stdio.h> 

#include < epx/root.h> 



int main() {

  int ID;



  ID=GET_ROOT()-> ProcRoot-> MyProcID;



  printf("Hello, world! This is processor %d.\n",ID);

  return 0;

  }

Скомпилируем программу и запустим. Каждый процессор сообщит свой номер. На этом простом примере демонстрируется важное свойство PARIX: на каждом узле изначально запущены идентичные приложения. Но поведение процесса изменяется в соответствии с позицией процессора в решетке раздела. Функция GET_ROOT осуществляет доступ к (глобальным) данным о конфигурации раздела и положении текущего узла в решетке. Эта функция возвращает указатель на данные следующей структуры:


typedef struct {

  int MyProcID;    /* my processor number */

  int MyX;         /* my X coordinate */

  int MyY;         /* my Y coordinate */

  int MyZ;         /* my Z coordinate */

  int nProcs;      /* Number of processors */

  int DimX;        /* array dimension in X direction */

  int DimY;        /* array dimension in Y direction */

  int DimZ;        /* array dimension in Z direction */

  } RootProc_t;

Эти данные не являются независимыми, т.к. справедливо следующее


MyProcID = (MyZ * DimX * DimY) + (MyY * DimX) + MyX

nProcs   = Dimx * DimY * DimZ

Эта информация позволяет также вычислить идентификаторы соседних процессоров.

Более содержательные примеры см. в каталоге /epx/epx/examples.

16 Пример программы на языке FORTRAN

Приведем простые программы на языке FORTRAN

C

C   Hello, world!

C

      PROGRAM HELLO

      WRITE (*,*) 'Hello, world!'

      STOP

      END

Теперь надо построить PARIX-приложение. Для этого даем команду

px f77 -o hello.px hello.f

Теперь мы получили исполняемый файл hello.px, который можно запустить. Действительно, команда

px run -a p4 hello.px

запустит PARIX-приложение на разделе из 4-х процессоров и вы получите четыре строчки (ответит каждый процессор) Hello, world!

Теперь немного модифицируем программу.


C

C   Hello, world! (Variant 2)

C

      PROGRAM HELLO

      INTEGER MYPROCID

      ID=MYPROCID()

      WRITE (*,*) 'Hello, world! This is processor ',ID,'.'

      STOP

      END

Скомпилируем программу и запустим. Каждый процессор сообщит свой номер.

Более содержательные примеры ищите в каталоге /epx/epx/examples/f77.

17 Элементы параллельного программирования

PARIX предоставляет специальные библиотечные функции для упрощения работы с процессами.

В рамках одного узла можно организовать несколько ветвей процесса (threads). Каждая ветвь выполняется параллельно. Все созданные ветви одного процесса имеют доступ к глобальным данным процесса. Говорят, что все ветви исполняются в рамках одного контекста. Каждая ветвь имеет свой собственный стек.

Понятие ветви является основным для модели программирования в симметричных мультипроцессорных системах (например Convex) и должно быть уже известно тем пользователям, кто создавал параллельные приложения для Convex.

Функция CreateThread создает новую ветвь процесса. Как обычно, стандартным методом синхронизации ветвей одного процесса является механизм семафоров.

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

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

Источники дополнительной информации

Приведем список функций:


AExit

AINIT

AInfo

AInit

ARecv

ASend

ASync

AbortServer

AddHead

AddTail

AddTop

AddTop_Data

BreakLink

ChangePriority

CondSelect

CondSelectList

ConnectLink

CreateSem

CreateThread

DestroySem

Execute

Free2DGrid

Free2DTorus

Free3DGrid

Free3DTorus

FreeClique

FreeDeb

FreeHCube

FreePipe

FreeRing

FreeStar

FreeTop

FreeTree

GETGLOBAL

GET_ROOT

Get2DGrid_Data

Get2DTorus_Data

Get3DGrid_Data

Get3DTorus_Data

GetClique_Data

GetDeb_Data

GetGlobId

GetHCube_Data

GetLink

GetLinkCB

GetLocal

GetPipe_Data

GetPriority

GetRing_Data

GetStar_Data

GetTop_Data

GetTree_Data

InitList

InitSem

LIST

LogError

Make2DGrid

Make2DTorus

Make3DGrid

Make3DTorus

MakeClique

MakeDeb

MakeHCube

MakeLink

MakePipe

MakeRTree

MakeRing

MakeStar

MakeTree

NewTop

PostInsert

PreInsert

ReceiveOption

ReceiveOption_B

Recv

RecvLink

RecvNode

RemHead

RemTail

Remove

SELECT

SEMAPHORES

SEND

SENDNODE

SearchList

Select

SelectList

Send

SendLink

SendNode

SetLocal

Signal

StartThread

TIME

TIME

TOPOLOGY

TOPOLOGY

Terminate

TestWait

TimeAfter

TimeAfterOption

TimeAfterOption_B

TimeNow

TimeWait

VIRT_TOP

Virt_Top

Wait

WaitThread

WalkList