Программа умножения матрицы на вектор
Результатом умножения матрицы на вектор является вектор результата. Для решения задачи используется алгоритм, в котором один процесс (главный) координирует работу других процессов (подчиненных). Для наглядности единая программа матрично-векторного умножения разбита на три части: общую часть , код главного процесса и код подчиненного процесса .
В общей части программы описываются основные объекты задачи: матрица А, вектор b, результирующий вектор с, определяется число процессов (не меньше двух). Задача разбивается на две части: главный процесс (master) и подчиненные процессы. В задаче умножения матрицы на вектор единица работы, которую нужно раздать процессам, состоит из скалярного произведения строки матрицы A на вектор b. Знаком ! отмечены комментарии.
program main
use mpi
integer MAX_ROWS, MAX_COLS, rows, cols
parameter (MAX_ROWS = 1000, MAX_COLS = 1000)
! матрица А, вектор b, результирующий вектор с
double precision a(MAX_ROWS,MAX_COLS), b(MAX_COLS), с(MAX_ROWS)
double precision buffer (MAX_COLS), ans /* ans – имя результата*/
integer myid, master, numprocs, ierr, status (MPI_STATUS_SIZE)
integer i, j, numsent, sender, anstype, row /* numsent – число посланных строк,
sender – имя процесса-отправителя, anstype – номер посланной строки*/
call MPI_INIT( ierr )
call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr )
call MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ierr )
! главный процесс – master
master = 0
! количество строк и столбцов матрицы А
rows = 100
cols = 100
if ( myid .eq. master ) then
! код главного процесса
else
! код подчиненного процесса
endif
call MPI_FINALIZE(ierr)
stop
end
Код главного процесса. Единицей работы подчиненного процесса является умножение строки матрицы на вектор.
! инициализация А и b
do 20 j = 1, cols
b(j) = j
do 10 i = 1, rows
a(i,j) = i
10 continue
20 continue
numsent = 0
! посылка b каждому подчиненному процессу
call MPI_BCAST(b, cols, MPI_DOUBLE_PRECISION, master,
MPI_COMM_WORLD, ierr)
! посылка строки каждому подчиненному процессу; в TAG номер строки = i
do 40 i = 1,min(numprocs-l, rows)
do 30 j = I, cols
buffer(j) = a(i,j)
30 continue
call MPI_SEND(buffer, cols, MPI_DOUBLE_PRECISION, i, i,
MPI_COMM_WORLD, ierr)
numsent = numsent + l
40 continue
! прием результата от подчиненного процесса
do 70 i = 1, rows
! MPI_ANY_TAG – указывает, что принимается любая строка
call MPI_RECV(ans, 1, MPI_DOUBLE_PRECISION, MPI_ANY_SOURCE,
MPI_ANY_TAG, MPI_COMM_WORLD, status, ierr)
sender = status (MPI_SOURCE)
anstype = status (MPI_TAG)
! определяем номер строки
c(anstype) = ans
if (numsent .lt. rows) then
! посылка следующей строки
do 50 j = 1, cols
buffer(j) = a(numsent+l, j)
50 continue
call MPI_SEND (buffer, cols, MPI_DOUBLE_PRECISION, sender,
numsent+l, MPI_COMM_WORLD, ierr)
numsent = numsent+l
else
! посылка признака конца работы
call MPI_SEND(MPI_BOTTQM, 0, MPI_DOUBLE_PRECISION,sender,
0, MPI_COMM_WORLD, ierr)
endif
70 continue
Сначала главный процесс передает вектор b в каждый подчиненный про-
цесс, затем пересылает одну строку матрицы A в каждый подчиненный процесс.
Главный процесс, получая результат от очередного подчиненного процесса, пе-
редает ему новую работу. Цикл заканчивается, когда все строки будут розданы
и получены результаты.
При передаче данных из главного процесса в параметре tagуказывается
номер передаваемой строки. Этот номер после вычисления произведения вме-
сте с результатом будет отправлен в главный процесс, чтобы главный процесс
знал, где размещать результат.
Подчиненные процессы посылают результаты в главный процесс и пара-
метр MPI_ANY_TAGв операции приема главного процесса указывает, что
главный процесс принимает строки в любой последовательности. Параметр
statusобеспечивает информацию, относящуюся к полученному сообщению. В
языке Fortran это – массив целых чисел размера MPI_STATUS_SIZE. Аргу-
мент SOURCEсодержит номер процесса, который послал сообщение, по этому
адресу главный процесс будет пересылать новую работу. Аргумент TAGхра-
нит номер обработанной строки, что обеспечивает размещение полученного ре-
зультата. После того как главной процесс разослал все строки матрицы А, на
запросы подчиненных процессов он отвечает сообщением с отметкой 0.
Код подчиненного процесса .
! прием вектора b всеми подчиненными процессами
call MPI_BCAST(b, cols, MPI_DOUBLE_PRECISION, master,
MPI_COMM_WORLD, ierr)
! выход, если процессов больше количества строк матрицы
if (numprocs .gt. rows) goto 200
! прием строки матрицы
90 call MPI_RECV(buffer, cols, MPI_DOUBLE_PRECISION, master,
MPI_ANY_TAG, MPI_COMM_WORLD, status, ierr)
if (status (MPI_TAG) .eq. 0) then go to 200
! конец работы
else
row = status (MPI_TAG)
! номер полученной строки
ans = 0.0
do 100 i = 1, cols
! скалярное произведение векторов
ans = ans+buffer(i)*b(i)
100 continue
! передача результата головному процессу
call MPI_SEND(ans,1,MPI_DOUBLE_PRECISION,master,row,
MPI_COMM_WORLD, ierr)
go to 90
! цикл для приема следующей строки матрицы
endif
200 continue
Каждый подчиненный процесс получает вектор b. Затем организуется цикл, состоящий в том, что подчиненный процесс получает очередную строку матрицы А, формирует скалярное произведение строки и вектора b, посылает результат главному процессу, получает новую строку и так далее.