
- •Оглавление
- •Цикл жизни программного обеспечения
- •Начальные этапы разработки по
- •Общие требования к методологии и проектированию по
- •Качество и надежность программного обеспечения
- •Показатели качества
- •Сложность комплексов программ
- •Надежность комплексов программ
- •Критерии надежности
- •Сбой, отказ, восстановление
- •Алгоритмы сортировки
- •Введение
- •Внутренняя сортировка
- •Сравнение эффективности алгоритмов сортировки
- •Простая сортировка вставками
- •Быстрая сортировка Хоара. Сортировка методом пузырька
- •Сортировка методом «турнира с выбыванием»
- •Реализация сортировки вставками. Алгоритм Шелла.
- •Сортировка слиянием. Поразрядная сортировка
- •Поиск данных
- •Введение в поиск данных
- •Последовательный поиск
- •Поиск в упорядоченной таблице
- •Бинарный поиск
- •Поиск по дереву
- •Вставка в дерево бинарного поиска
- •Удаление из дерева бинарного поиска
- •Хеширование
- •Разрешение коллизий при хешировании методом открытой адресации
- •Выбор хеш-функции
- •Объектно-ориентированное программирование
- •Введение в объектно-ориентированное программирование
- •Инкапсуляция
- •Полиморфизм
- •Конструкторы и деструкторы
- •Наследование
- •Объединения, встраиваемые функции
- •Указатели и адреса
- •Программирование параллельных вычислений
- •Введение
- •Сети. Родитель сети
- •Синхронизация процессов
- •Литература
Сети. Родитель сети
Программы, которые мы до сих пор рассматривали, описывали вычисления, в которых участвовали либо все процессы параллельной программы, либо только хост - процесс. Очень часто число процессов, вовлечённых в параллельное решение задачи, зависит от самой задачи и/или параллельного алгоритма её решения и определяется входными данными. Например, если для параллельного моделирования движения N групп тел под влиянием взаимного притяжения используется отдельный процесс для каждой группы тел, то в соответствующие параллельные вычисления должны быть вовлечены в точности N процессов. Запуск параллельной программы осуществляется средствами внешними по отношению к языку mpC. Общее число составляющих её процессов не определяется разработчиком программы на mpC. У разработчика есть лишь языковые средства для определения этого числа.
Рассмотрим программу, которая даёт первое знакомство с языковыми средствами, позволяющими программисту описывать параллельные вычисления на нужном числе процессов. Сами вычисления, по-прежнему, предельно просты - каждый из участвующих в них процессов просто выводит на терминал пользователя приветствие "Hello, world!". Число же участвующих процессов (N =3 ) будет задаваться программистом.
Группе процессов, совместно выполняющих некоторые параллельные вычисления, в языке mpC соответствует понятие сети. Сеть в mpC - это абстрактный механизм, облегчающий программисту работу с реальными физическими процессами параллельной программы.
В простейшем случае, сеть - это просто множество виртуальных процессоров. Для программирования вычислений на нужном числе параллельных процессов, прежде всего, нужно определить сеть, состоящую из соответствующего числа виртуальных процессоров. И лишь после того, как сеть определена, можно приступать к описанию параллельных вычислений на этой сети.
Определение сети вызывает создание группы процессов, представляющей эту сеть, так что каждый виртуальный процессор представляется отдельным процессом параллельной программы. Описание вычислений на сети вызывает выполнение соответствующих вычислений процессами параллельной программы, представляющими виртуальные процессоры сети. Важное отличие реальных процессов от виртуальных заключается в том, что в разные моменты выполнения параллельной программы один и тот же процесс может представлять различные виртуальные процессоры различных сетей. Другими словами, определение сети вызывает отображение её виртуальных процессоров на реальные процессы параллельной программы, и это отображение сохраняется всё время жизни сети.
#include <mpc.h>
#define N 3
int [*]main()
{
net SimpleNet(N) mynet;
[mynet]MPC_Printf("Hello, world!\n"); }
Так, в рассматриваемой программе сначала определяется сеть mynet, состоящая из N виртуальных процессоров, а затем на этой сети вызывается узловая библиотечная функция MPC_Printf. Выполнение этой программы заключается в параллельном вызове функции MPC_Printf теми N процессами программы, на которые отображены виртуальные процессоры сети mynet. Это отображение осуществляется системой программирования языка mpC во время выполнения программы. Если система программирования не может выполнить отображение (например, если значение N превышает общее число процессов программы), то программа завершается аварийно с соответствующей диагностикой.
Заметим схожесть конструкций [mynet] и [host]. Ключевое слово host можно рассматривать как имя сети из одного виртуального процессора, который всегда отображается на связанный с терминалом пользователя хост - процесс.
Следующая программа выводит на терминал пользователя более содержательные послания от тех процессов параллельной программы, на которые отображаются виртуальные процессоры сети mynet.
#include <mpc.h>
#include <sys/utsname.h>
#define N 3
int [*]main()
{
net SimpleNet(N) mynet;
struct utsname [mynet]un;
[mynet]uname(&un);
[mynet]MPC_Printf(" Hello world! I'm on \"%s\".\n", un.nodename);
}
Следующая программа семантически полностью эквивалентна предыдущей программе, однако за счет использования специальной распределенной метки [mynet]: имеет более простой синтаксис. Оператор, помеченный такой меткой, полностью выполняется виртуальными процессорами соответствующей сети.
#include <mpc.h>
#include <sys/utsname.h>
#define N 3
int [*]main()
{
net SimpleNet(N) mynet;
[mynet]:
{
struct utsname un;
uname(&un);
MPC_Printf("Hello world! I'm on \"%s\".\n", un.nodename);
}
}
Время жизни сети mynet, как и время жизни переменной un, ограничено блоком, в котором эта сеть определяется. При выходе из этого блока, все процессы программы, захваченные под виртуальные процессоры сети mynet, освобождаются и могут быть использованы при создании других сетей. Такие сети называются в mpC автоматическими.
В отличие от автоматических, время жизни статических сетей ограничено лишь временем выполнения программы. Следующие две программы демонстрируют различие статических и автоматических сетей. Программы выглядят практически идентичными. Обе они заключаются в циклическом выполнении блока, включающего определение сети и выполнении уже знакомых вычислений на этой сети. Единственным, но существенным, отличием является то, что в первой программе определяется автоматическая сеть, а во второй - статическая.
#include <mpc.h>
#include <sys/utsname.h>
#define Nmin 3
#define Nmax 5
int [*]main()
{
repl n;
for(n=Nmin; n<=Nmax; n++)
{
auto net SimpleNet(n) anet;
[anet]:
{
struct utsname a;
uname(&a);
MPC_Printf("I'm from an automatic network on \"%s\" (n=%d).\n",
a.nodename, n);
}
}
}
#include <mpc.h>
#include <sys/utsname.h>
#define Nmin 3
#define Nmax 5
int [*]main() {
repl n;
for(n=Nmin; n<=Nmax; n++) {
static net SimpleNet(n) snet;
[snet]: {
struct utsname s;
uname(&s);
MPC_Printf("I'm from the static network on \"%s\" (n=%d).\n",
s.nodename, n);
}
}
}
В процессе выполнении первой программы при входе в блок на первом витке цикла создается автоматическая сеть из трёх виртуальных процессоров (n = Nmin = 3), которая при выходе из цикла уничтожается. При входе в блок на втором витке цикла создается новая автоматическая сеть уже из четырёх виртуальных процессоров, которая также прекращает своё существование при выходе из блока, так что к моменту выполнения повторной инициализации цикла (n ++) эта 4-процессорная сеть уже не существует. Наконец, на последнем витке при входе в блок создаётся автоматическая сеть из пяти виртуальных процессоров (n = Nmax = 5).
В процессе выполнении второй программы, при входе в блок на первом витке цикла также создается сеть из трёх виртуальных процессоров, однако при выходе из блока она не уничтожается, а просто перестаёт быть видна. Таким образом, в этом случае блок является не областью существования сети, а областью её видимости. Поэтому во время выполнения повторной инициализации цикла и проверки условия цикла эта статическая трех процессорная сеть существует, но недоступна (эти точки программы находятся вне области видимости имени сети snet). При повторных входах в блок на последующих витках цикла никакие новые сети не создаются, а просто становится видна та статическая сеть, которая была создана при первом входе в блок.
Таким образом, в отличие от первой программы, в которой одно и то же имя mynet на разных витках цикла обозначает совершенно разные сети, во второй программе имя snet обозначает уникальную сеть, существующую с момента первого входа в блок, в котором она определяется, и до конца выполнения программы. Если класс сети не специфицирован явно путём использования ключевого слова auto или static в определении сети, то, по умолчанию, она считается автоматической, если сеть определена внутри функции, и статической, если определена вне функции.
В разобранных до настоящего момента программах все виртуальные процессоры сетей параллельно выполняли абсолютно одинаковые вычисления. Поэтому, у нас не было необходимости различать их внутри сети. Если же параллельный алгоритм, который нужно запрограммировать, предполагает, что различные параллельные процессы должны выполнять разные вычисления, то необходимы средства для выделения отдельных виртуальных процессоров внутри сети. Такие средства предусмотрены в языке mpC. Они позволяют привязывать виртуальные процессоры любой сети к некоторой системе координат и выделять отдельный виртуальный процессор путем задания его координат.
Вообще говоря, в программе на mpC нельзя определить просто сеть, а можно определить лишь сеть того или иного типа. Тип является важнейшим атрибутом сети, в частности, конкретизируя операции доступа к её виртуальным процессорам. Указание типа является обязательной частью определения любой сети. Поэтому, всякому определению сети должно предшествовать определение соответствующего сетевого типа. В рассмотренных нами примерах определение используемого сетевого типа SimpleNet находится среди прочих стандартных определений языка mpC в заголовке mpc.h и включается в программу с помощью директивы #include препроцессора. Выглядит это определение следующим образом:
nettype SimpleNet(int n)
{
сoord I=n;
};
Оно вводит имя SimpleNet сетевого типа, параметризованного целым параметром n. В теле определения объявляется координатная переменная I, изменяющаяся в пределах от 0 до n-1. Тип SimpleNet является простейшим параметризованным сетевым типом и соответствует сетям, состоящим из n виртуальных процессоров, линейно-упорядоченных своим местоположением на координатной прямой. Рассмотрим следующую программу
#include <mpc.h >
#define N 5
int [*]main()
{
net SimpleNet(N) mynet;
[mynet]:
{
int my_coordinate;
my_coordinate = I coordof mynet;
if(my_coordinate%2==0)
MPC_Printf("Hello, even world!\n");
else
MPC_Printf("Hello, odd world!\n");
}
}
Она демонстрирует, каким образом можно запрограммировать выполнение различных вычислений виртуальными процессорами с различными координатами. В этой программе используется бинарная операция coordof, левым операндом которой, в нашем случае, является координатная переменная I, а правым - сеть mynet. Результатом операции будет целое значение, распределённое по сети mynet, такое, что его проекция на любой из виртуальных процессоров будет равна значению координаты I этого процессора в сети. После присваивания my_coordinate = I coordof mynet значения проекций переменной my_coordinate будут равны координатам соответствующих виртуальных процессоров в сети mynet. В результате, виртуальные процессоры с чётными координатами выведут на терминал пользователя приветствие "Hello, even world!", а виртуальные процессоры с нечётными координатами - приветствие "Hello, odd world!".
Ниже приведенная программа демонстрирует сеть, виртуальные процессоры которой привязаны к двумерной системе координат. Каждый из виртуальных процессоров сети выводит на терминал пользователя свои координаты в сети и имя компьютера, на котором его разместила программа. Заметим, что в качестве второго операнда операции coordof в этой программе используется не сеть, а переменная un.
#include <mpc.h>
#include <sys/utsname.h>
nettype Mesh(int m, int n)
{
coord I=m, J=n;
};
#define M 2
#define N 3
int [*]main() {
net Mesh(M,N) mynet;
[mynet]:
{
struct utsname un;
uname(&un);
MPC_Printf("I'm on \"%s\" and have coordinates (%d, %d).\n",
un.nodename, I coordof un, J coordof un);
}
}
В общем случае, если вторым операндом операции coordof является не сеть, а выражение, то это выражение не вычисляется, а используется лишь для определения сети, по которой распределено его значение, а операция выполняется так, как если бы в качестве второго операнда использовалась эта сеть.
Уже говорилось, что время жизни автоматической сети ограничено блоком, в котором она определена. При выходе из этого блока сеть прекращает своё существование, а процессы программы, захваченные под виртуальные процессоры сети, освобождаются и могут быть использованы при создании других сетей. Возникает вопрос, каким образом результаты вычислений, выполненных на такой автоматической сети, сохраняются и могут быть использованы в дальнейших вычислениях. В уже разобранных программах такой проблемы не возникало, так как единственным результатом вычислений на любой из сетей был вывод на терминал пользователя того или иного сообщения.
На самом деле, сети в языке mpC не являются абсолютно независимыми друг от друга. Каждая вновь создаваемая сеть имеет в точности один виртуальный процессор, общий с уже существующими на момент создания сетями. Этот виртуальный процессор называется родителем создаваемой сети и является тем связующим звеном, через которое передаются результаты вычислений на сети в случае прекращения её существования. Родитель сети явно или неявно специфицируется её определением.
До сих пор ни одна из сетей не была определена с явным указанием родителя. Во всех случаях родитель специфицировался неявно, и этим родителем был ни кто иной, как виртуальный хост - процессор. Следующая программа полностью эквивалентна предыдущей программе, с той разницей, что в определении сети неявная спецификация родителя сети заменена на явную.
#include <mpc.h>
#include <sys/utsname.h>
nettype Mesh(int m, int n)
{
coord I=m, J=n;
parent [0,0];
};
#define M 2
#define N 3
int [*]main() {
net Mesh(M,N) [host]mynet;
[mynet]:
{
struct utsname un;
uname(&un);
MPC_Printf("I'm on \"%s\" and have coordinates (%d, %d).\n",
un.nodename, I coordof un, J coordof un);
}
}
В любой из рассмотренных программ в каждый момент времени её выполнения одновременно существует не больше одной сети. Это не ограничение языка. Язык mpC позволяет писать программы с произвольным числом параллельно существующих сетей. Единственным ограничением является общее число процессов, составляющих параллельную программу, на которые отображаются виртуальные процессоры сетей.