Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_ТП.doc
Скачиваний:
35
Добавлен:
29.03.2015
Размер:
1.63 Mб
Скачать
  1. Программирование параллельных вычислений

    1. Введение

Язык программирования mpC - это расширение языка Си, разработанное специально для программирования параллельных вычислений на обычных сетях разнородных компьютеров [5]. Основной целью параллельных вычислений является ускорение решения задачи. Именно это отличает параллельные вычисления от распределённых, для которых основной целью является обеспечить совместную работу программных компонент, изначально размещённых на различных компьютерах. В случае параллельных вычислений разбиение программы на компоненты, размещаемые на разных компьютерах, является лишь средством для ускорения работы программы, а не врождённым свойством этой программы. Поэтому, основное внимание в языке mpC уделяется средствам, позволяющим максимально облегчить разработку как можно более эффективных программ для решения задач на обыкновенных сетях компьютеров.

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

Для начала рассмотрим простейшую программу, которая выводит на терминал пользователя текст "Hello, world!". Код этой программы мало отличается от кода программы на Си. Первое отличие - спецификатор [*] перед именем main в определении главной функции. Он специфицирует вид функции, говоря о том, что код этой функции выполняется всеми процессами параллельной программы. Функции, подобные main, называются в mpC базовыми. Корректная работа таких функций возможна только при условии их вызова всеми процессами параллельной программы. Контроль за корректностью вызовов базовых функций осуществляется компилятором.

#include <stdio.h>

int [*]main() {

[host] printf ("Hello, world.\n");

}

Второе отличие - это конструкция [host] перед именем функции printf в выражении, где эта стандартная библиотечная функция языка Си вызывается. В отличие от функции main, для корректной работы функции printf не требуется ее параллельного вызова всеми процессами параллельной программы. Такие функции называются в mpC узловыми. Язык предоставляет возможность вызова узловой функции, как отдельным процессом параллельной программы, так и её параллельного вызова группой процессов. В нашем случае, функция printf выполняется только одним процессом параллельной программы, а именно, процессом, связанным с терминалом пользователя, из которого он запускал на выполнение эту параллельную программу. Ключевое имя host жёстко связано в языке mpC именно с этим процессом. Изменим программу таким образом, чтобы все процессы параллельной программы выполняли вызов функции printf. Библиотечная узловая функция MPC_Printf языка mpC гарантирует вывод приветствия на терминал пользователя от каждого компьютера, участвующего в процессе параллельной программы.

#include <mpc.h>

int [*]main()

{

MPC_Printf(" Hello, world! \n");

}

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

#include <mpc.h>

#include <sys/utsname.h>

int [*]main()

{

struct utsname un;

uname(&un);

MPC_Printf("Hello world! I'm on \"%s\".\n",

un.nodename);

}

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

После выполнения вызова узловой (библиотечной) функции uname, поле nodename структуры un будет содержать ссылку на имя компьютера, на котором этот процесс выполняется.

Каждый из процессов параллельной программы выполняет вызов функции uname, после чего поле nodename соответствующей проекции распределённой структуры un содержит ссылку на имя того компьютера, на котором этот процесс выполняется.

Значения распределённых переменных и распределённых выражений, таких как un.nodename или &un, естественным образом распределены по процессам параллельной программы и называются распределённми значениями. Следующая программа расширяет вывод программы информацией об общем числе процессов параллельной программы.

#include <mpc.h>

#include <sys/utsname.h>

int [*]main()

{

struct utsname un;

repl int one;

repl int number_of_processes;

uname(&un);

one = 1;

number_of_processes = one[+];

MPC_Printf("Hello world! I'm one of %d processes"

"and run on \"%s\".\n",

number_of_processes, un.nodename);}

Для этого в программе определены две целые распределённые переменные one и number_of_processes. Сначала в результате выполнения присваивания one = 1 всем проекциям переменной one присваивается значение 1. Результатом применения постфиксной операции [+] к переменной one будет распределённое значение, проекция которого на любой из процессов равна сумме значений проекций переменной one. Другими словами, проекция значения выражения one [+] на любой из процессов параллельной программы будет равна их общему количеству. В результате присваивания этого распределённого значения распределенной переменной number_of_processes все проекции последней будут содержать одно и то же значение, а именно, общее число процессов параллельной программы.

Определение распределённой переменной one содержит ключевое слово repl (сокращение от replicated), которое информирует компилятор о том, что в любом выражении проекции значения этой переменной на разные процессы параллельной программы равны между собой. Такие распределённые переменные называются в mpC размазанными (соответственно, значение размазанной переменной называется размазанным значением). Размазанные переменные и выражения играют большую роль в mpC. Компилятор контролирует объявленное программистом свойство размазанности и предупреждает обо всех случаях, когда оно может быть нарушено.

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

#include <mpc.h>

#include <sys/utsname.h>

int [*]main()

{

struct utsname un;

uname(&un);

MPC_Printf("Hello world! I'm one of %d processes" "and run on \"%s\".\n",

MPC_Total_nodes(), un.nodename);

}

Эта программа эффективней предыдущей программы еще и тем, что параллельный вызов функции MPC_Total_nodes, в отличие от вычисления выражения one [+], не требует обмена данными между процессами программы.