- •10.1. Виды параллельной обработки
- •10.1.1. Классификация систем параллельной обработки
- •10.2. Матричная обработка данных
- •Массив процессорных элементов
- •10.3. Архитектура мультипроцессорных систем общего назначения
- •10.4. Коммуникационные сети
- •10.5. Организация памяти в мультипроцессорных системах
- •10.6. Программный параллелизм и общие переменные
- •10.6.1. Доступ к общим переменным
- •10.6.2. Согласованность кэша
- •10.6.3. Блокировка и согласованность кэш-памяти
- •10.7. Мультикомпьютерные системы
- •10.8. Общая память и передача сообщений
- •10.8.1. Система с общей памятью
- •10.8.2. Система с передачей сообщений
- •10.9. Производительность мультипроцессорных систем
- •10.9.1. Закон Амдала
- •10.9.2. Показатели производительности
10.8.2. Система с передачей сообщений
В случае распределенной памяти каждый процессор обращается к собственной памяти. Если выполнять нашу программу на двух процессорах в такой системе, векторы должны быть явно разделены на две части, каждая из которых будет храниться в памяти одного процессора. Каждая копия программы будет иметь доступ только к своим данным. Архитектура для выполнения таких приложений называется архитектурой с одной программой и несколькими потоками данных (Single Program, Multiple Data, SPMD). Читатель должен понимать различие между системой SPMD и описанной в разделе 10.1.1 системой SIMD, где все процессоры в конкретный момент времени выполняют одну и ту же команду.
Пример программы типа SPMD приведен на рис. 10.18. Вектор данных должен быть сначала загружен в локальные модули памяти двух процессоров. Программа, которой присвоен идентификатор 0, с помощью операционной системы считывает с диска первую половину вектора а и сохраняет данные в своей памяти под этим же именем. Затем она считывает вторую половину вектора а и помещает информацию в буфер памяти temparray. Далее она отсылает сообщение с данными из этого буфера процессору, выполняющему программу с идентификатором 1. Потом те же действия выполняются для данных, составляющих вектор b. Программа с идентификатором 1 получает вторую половину векторов а и b и сохраняет таковые в своей памяти под их собственными именами.
integer array a[l...N/2], b[l...N/2], temparray[l...N/2]
integer dot_product
integer id
integer temp
…..
id := mypid()
if (id = 0) then
read a[l...N/2] from vector_a
read temparray [1...N/2] from vector_a
send (temparray[l...N/2],l)
read b[l...N/2] from vectorjb
read temparray [1...N/2] from vector_b
send (temparray[l...N/2],l)
else receive (a(l...N/2], 0)
receive (b(l...N/2], 0)
end
dot_product :=0
do_dot(a, b)
if (id=0) send (dot_product, 0)
else receive (temp, 1)
dot_product := dot_product + temp
print dot_product
end
do_dot (integer array x[l...N/2], integer array y[l...N/2])
for k := 1 to N/2
dot_product := dot_product + x[k]*y[k]
end
end
Рис. 10.18. Программа с передачей сообщений для вычисления скалярного произведения векторов на двух процессорах
Далее процедура do_dot просто вычисляет скалярное произведение N/2 элементов. Граничные значения цикла для обоих процессоров одинаковы, так как каждый использует данные из собственной памяти. Функция передачи сообщений иллюстрируется еще одним действием, выполняемым, когда процессор завершает процедуру do_dot. Чтобы программа с идентификатором 0 могла вычислить и напечатать результирующее скалярное произведение, программа с идентификатором 1 должна прислать ей свое скалярное произведение. Программа с идентификатором 0 принимает его во временный буфер temp.
Этот пример, как и предыдущий, нетрудно распространить на большее количество процессоров. Векторы должны быть разделены на части, назначаемые разным процессорам. Один из процессоров, например тот, который выполняет программу с идентификатором 0, назначается для вычисления конечного результата на основе данных, полученных от других процессоров.
Затраты на организацию параллельного выполнения программы несколькими процессорами включают время, необходимое для загрузки копий программ разными процессорами, время на загрузку частей вектора в память этих процессоров и время, требуемое на передачу между процессорами остальных сообщений. Поэтому скорость выполнения программы при ее распараллеливании между несколькими процессорами повышается только в том случае, если длина вектора достаточно велика и количество процессоров подобрано оптимальным образом.
У систем c общей памятью и передачей сообщений имеются свои сильные и слабые стороны. Системы с общей памятью использовать проще, поскольку они являются непосредственным расширением однопроцессорной архитектуры. Однако если данные располагаются в удаленных модулях памяти, задержки на обращение к этой памяти могут быть достаточно заметными, поэтому важно минимизировать количество операций доступа к глобальным переменным. Если объем сетевого трафика значительно повысится, сеть, скорее всего, начнет хуже функционировать. Производительность приложения в значительной степени определяется тем, насколько корректно программист выполнил синхронизацию.
Написание программ для систем с передачей сообщений — задача непростая, поскольку каждый процессор имеет собственное адресное пространство. В таких системах на передачу сообщений уходит много времени, и программист должен стараться так структурировать программы, чтобы предельно сократить эти затраты. А вот скорость работы сети обмена едва ли сильно отразится на производительности, так как сообщения передаются относительно редко. Синхронизация процессов выполняется неявно с помощью сообщений. Пожалуй, самым большим преимуществом систем с передачей сообщений является то, что они могут создаваться на базе более доступного и распространенного аппаратного обеспечения.