Лабораторная работа 2
1. Задание
Разработка параллельного MPI приложения на языке Java. Необходимо разработать параллельный вариант алгоритма с применением MPI и замерить время его работы. Запуск каждого экземпляра MPI происходит в своём контейнере.
Вариант: "Определить минимальный элемент матрицы ниже главной диагонали"
2. Описание кода
package ru.kuznec;
import mpi.MPI;
public class MatrixMinBelowMainDiagonal {
public static void main(String[] args) {
MPI.Init(args);
int rank = MPI.COMM_WORLD.Rank();
int size = MPI.COMM_WORLD.Size();
int n = 4;
double[] matrix = new double[n * n];
if (rank == 0) {
double[][] m = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
for (int i = 0; i < n; i++) {
System.arraycopy(m[i], 0, matrix, i * n, n);
}
}
int[] dim = new int[]{n};
MPI.COMM_WORLD.Bcast(dim, 0, 1, MPI.INT, 0);
n = dim[0];
MPI.COMM_WORLD.Bcast(matrix, 0, n * n, MPI.DOUBLE, 0);
int rowsPerProc = n / size;
int extra = n % size;
int startRow, endRow;
if (rank < extra) {
startRow = rank * (rowsPerProc + 1);
endRow = startRow + rowsPerProc;
} else {
startRow = rank * rowsPerProc + extra;
endRow = startRow + rowsPerProc - 1;
}
double localMin = Double.MAX_VALUE;
for (int i = startRow; i <= endRow; i++) {
for (int j = 0; j < i; j++) {
double value = matrix[i * n + j];
if (value < localMin) {
localMin = value;
}
}
}
double[] sendBuf = new double[]{ localMin };
double[] globalMin = new double[1];
MPI.COMM_WORLD.Reduce(sendBuf, 0, globalMin, 0, 1, MPI.DOUBLE, MPI.MIN, 0);
if (rank == 0) {
if (globalMin[0] == Double.MAX_VALUE) {
System.out.println("Элементы ниже главной диагонали отсутствуют.");
} else {
System.out.println("Минимальный элемент ниже главной диагонали: " + globalMin[0]);
}
}
MPI.Finalize();
}
}
-
Метод
MPI.Init(args)запускает параллельную среду. Каждый процесс получает свой уникальный ранг и общее число процессов черезMPI.COMM_WORLD.Rank()иMPI.COMM_WORLD.Size(). -
Только процесс с рангом 0 (мастер-процесс) задаёт исходную матрицу. Для удобства обмена данными матрица преобразуется в одномерный массив с помощью
System.arraycopy. -
Сначала размер матрицы (n) передаётся всем процессам, затем вся матрица передаётся всем процессам с помощью коллективной операции
Bcast. -
Матрица разбивается на блоки строк для каждого процесса. Если число процессов не делит размер матрицы нацело, первые процессы получают на одну строку больше (
extra). -
Каждый процесс просматривает свои строки и ищет минимальный элемент среди элементов ниже главной диагонали (для строки с индексом
iобрабатываются столбцы от0доi-1). -
Операция
Reduceс операторомMPI.MINобъединяет локальные минимумы в глобальный, который выводится на экран процессом с рангом 0. -
После выполнения всех вычислений вызывается
MPI.Finalize(), что корректно завершает параллельную среду.
4. Действия, производимые на контейнерах
Для запуска этого MPI-приложения в распределённом режиме использовались контейнеры, каждый из которых настроен следующим образом:
-
Установка Java и MPJ Express:
На каждом контейнере установлена jdk-21 и распакована библиотека MPJ Express. ПеременнаяMPJ_HOMEнастроена на путь к MPJ Express, а каталогMPJ_HOME/binдобавлен в переменнуюPATH. -
Настройка сети и SSH:
Контейнеры настроены так, чтобы иметь возможность обмениваться данными по сети. Для работы MPJ Express настроен passwordless SSH между контейнерами. Файлmachinesсодержит IP-адреса всех контейнеров:192.168.10.101 192.168.10.102 192.168.10.103 192.168.10.104Это позволяет MPJ Express распределить процессы по разным контейнерам.
-
Запуск демонов MPJ Express:
На каждом контейнере запущен MPJ daemon, который слушает входящие соединения от мастера. Демоны обеспечивают обмен информацией о портах и корректное распределение процессов.$MPJ_HOME/bin/mpjdaemon -boot 192.168.10.101 $MPJ_HOME/bin/mpjdaemon -boot 192.168.10.102 $MPJ_HOME/bin/mpjdaemon -boot 192.168.10.103 $MPJ_HOME/bin/mpjdaemon -boot 192.168.10.104 -
Добавление хостов в каждый контейнер На каждом контейнере добавляем в файл
/etc/hostsкаждый хост, на одном из контейнеров это выглядит примерно так:# --- BEGIN PVE --- 192.168.10.101 node1.com node1 192.168.10.102 node2.com node2 192.168.10.103 node3.com node3 192.168.10.104 node4.com node4 # --- END PVE --- -
Запуск MPI-приложения:
Из одного из контейнеров запускается приложение с помощью команды:mpjrun.sh -np 4 -dev niodev MatrixMinBelowMainDiagonalЭта команда запускает 4 процесса, которые распределяются между контейнерами согласно файлу
machines.