forked from sevastyan_b/SSPR_25
Merge pull request 'PIbd-22_PotantsevD.P._LabWork2' (#72) from denisp222/SSPR_25:potantsev_denis_lab_2 into main
Reviewed-on: sevastyan_b/SSPR_25#72
This commit is contained in:
81
potantsev_denis_lab_2/Main.java
Normal file
81
potantsev_denis_lab_2/Main.java
Normal file
@@ -0,0 +1,81 @@
|
||||
import mpi.MPI;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Random;
|
||||
|
||||
public class MatrixSorter {
|
||||
public static void main(String[] args) throws Exception {
|
||||
MPI.Init(args);
|
||||
int rank = MPI.COMM_WORLD.Rank();
|
||||
int size = MPI.COMM_WORLD.Size();
|
||||
|
||||
String hostInfo = getHostName();
|
||||
System.out.println("Node " + rank + " executing on " + hostInfo);
|
||||
|
||||
int rows = 1000;
|
||||
int cols = 1000;
|
||||
int[][] matrix = new int[rows][cols];
|
||||
int chunkSize = rows / (size - 1);
|
||||
|
||||
if (rank == 0) {
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
matrix[i][j] = random.nextInt(1000);
|
||||
}
|
||||
}
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
for (int j = 0; j < chunkSize; j++) {
|
||||
MPI.COMM_WORLD.Send(matrix[(i - 1) * chunkSize + j], 0, cols, MPI.INT, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
for (int j = 0; j < chunkSize; j++) {
|
||||
MPI.COMM_WORLD.Recv(matrix[(i - 1) * chunkSize + j], 0, cols, MPI.INT, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.nanoTime();
|
||||
System.out.println("Sorted matrix (first 10 rows, first 10 elements each) with row sums:");
|
||||
printMatrix(matrix, 10);
|
||||
System.out.println("Sorting time: " + (endTime - startTime) / 1_000_000 + " ms");
|
||||
} else {
|
||||
int[][] subMatrix = new int[chunkSize][cols];
|
||||
for (int i = 0; i < chunkSize; i++) {
|
||||
MPI.COMM_WORLD.Recv(subMatrix[i], 0, cols, MPI.INT, 0, i);
|
||||
}
|
||||
|
||||
Arrays.sort(subMatrix, Comparator.comparingInt(MatrixSorter::rowSum));
|
||||
|
||||
for (int i = 0; i < chunkSize; i++) {
|
||||
MPI.COMM_WORLD.Send(subMatrix[i], 0, cols, MPI.INT, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
MPI.Finalize();
|
||||
}
|
||||
|
||||
private static int rowSum(int[] row) {
|
||||
return Arrays.stream(row).sum();
|
||||
}
|
||||
|
||||
private static void printMatrix(int[][] matrix, int rowsToShow) {
|
||||
for (int i = 0; i < Math.min(rowsToShow, matrix.length); i++) {
|
||||
int sum = rowSum(matrix[i]);
|
||||
System.out.println("Sum: " + sum + " -> " + Arrays.toString(Arrays.copyOf(matrix[i], 10))); // Ограничение вывода 10 элементами
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (Exception e) {
|
||||
return "Unknown Host";
|
||||
}
|
||||
}
|
||||
}
|
||||
144
potantsev_denis_lab_2/README.md
Normal file
144
potantsev_denis_lab_2/README.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Лабораторная работа №2
|
||||
|
||||
## Задание
|
||||
Разработка параллельного MPI приложения на языке Java.
|
||||
|
||||
**Необходимо:** разработать параллельный вариант алгоритма с применением MPI и
|
||||
замерить время его работы. В рамках работы программы должно быть две копии
|
||||
приложения, которые соединяются друг с другом по сети. Сообщения о статусе
|
||||
соединения (например, что соединение установлено) должны выводиться в консоль.
|
||||
Запуск каждого экземпляра MPI происходит в своём LXC контейнере.
|
||||
|
||||
**Вариант:** Упорядочить строки матрицы по возрастанию суммы их элементов (**18**).
|
||||
|
||||
---
|
||||
|
||||
## Описание
|
||||
Данный проект реализует распределённую сортировку строк матрицы по возрастанию суммы их элементов с использованием `MPI` (Message Passing Interface) и библиотеки `MPJ Express`.
|
||||
|
||||
Код запускается на нескольких узлах кластера (или контейнерах **LXC**) и использует механизм передачи данных через *MPI Send/Recv*, что позволяет параллельно сортировать большие матрицы.
|
||||
|
||||
### Технологии
|
||||
- **Java 21** — язык программирования.
|
||||
- **MPJ Express** – библиотека для работы с MPI в Java.
|
||||
- **LXC (Linux Containers)** – для имитации распределённой среды.
|
||||
- **SSH (Secure Shell)** – для аутентификации между узлами.
|
||||
|
||||
---
|
||||
|
||||
## Как запустить программу
|
||||
|
||||
### 1. Установка зависимостей
|
||||
Перед запуском программы нужно установить нужные пакеты:
|
||||
|
||||
- Java и JDK 21 (проверьте javac -version)
|
||||
```bash
|
||||
apt install openjdk-21-jdk
|
||||
```
|
||||
- MPJ Express
|
||||
```bash
|
||||
wget https://sourceforge.net/projects/mpjexpress/files/releases/mpj-v0_44.zip/download
|
||||
unzip mpj-v0_44.zip
|
||||
```
|
||||
Также можно сразу добавить MPJ Express в Path.
|
||||
|
||||
### 2. Создание SSH-пользователя
|
||||
MPJ Express (и другие реализации MPI) используют SSH для запуска процессов на удалённых узлах. Для коммуникации между двумя контейнерами по-хорошему нужно создать беспарольного ssh-пользователя.
|
||||
```bash
|
||||
ssh-keygen -t rsa -b 4096
|
||||
ssh-copy-id sshuser@CT118
|
||||
```
|
||||
|
||||
### 3. Компиляция кода
|
||||
В папке проекта должен лежать java-класс. Используем *javac* и библиотеку ```MPI``` для компиляции.
|
||||
```bash
|
||||
javac -cp $MPJ_HOME/lib/mpj.jar Main.java
|
||||
```
|
||||
|
||||
### 4. Запуск программы
|
||||
Запускаем daemon-ы на обоих контейнерах и вводим команду для запуска самого проекта.
|
||||
```bash
|
||||
mpjdaemon
|
||||
mpjrun.sh -np 2 -cp . Main
|
||||
```
|
||||
Где -np 2 – число процессов (включая главный).
|
||||
|
||||
***P.S. Предварительно, перед запуском, нужно настроить файл /etc/hosts, где нужно указать ip второго контейнера.***
|
||||
```
|
||||
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
|
||||
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
|
||||
# --- BEGIN PVE ---
|
||||
192.168.17.117 CT117.8.8.8.8 CT117
|
||||
192.168.17.118 CT118
|
||||
# --- END PVE ---
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Как работает программа
|
||||
1. Генерируется случайная матрица размера *1000×1000*.
|
||||
2. MPI-узлы обмениваются фрагментами матрицы:
|
||||
- Главный процесс (rank = 0) делит строки между рабочими узлами (rank = 1, 2, ...).
|
||||
- Каждый узел получает свою часть строк (половину) и сортирует их по сумме элементов.
|
||||
- Отправляет результат обратно главному процессу.
|
||||
3. Вывод результатов:
|
||||
- Выводятся первые 10 строк матрицы с их суммой (для удобства проверки).
|
||||
- Выводится время выполнения сортировки.
|
||||
|
||||
### Ключевые методы
|
||||
1. **main(String[] args)**
|
||||
- Инициализирует **MPI** (`MPI.Init(args)`).
|
||||
- Определяет ранг процесса (*rank*) и общее количество (*size*).
|
||||
- **Rank 0** (главный узел):
|
||||
- Генерирует *1000×1000* случайную матрицу.
|
||||
- Делит матрицу на части и отправляет их рабочим узлам (`Send`).
|
||||
- Принимает отсортированные данные (`Recv`).
|
||||
- Выводит итоговую матрицу и время выполнения.
|
||||
- **Rank > 0** (рабочие узлы):
|
||||
- Получает свою часть матрицы (`Recv`).
|
||||
- Сортирует строки по сумме элементов.
|
||||
- Отправляет данные обратно (`Send`).
|
||||
|
||||
2. **rowSum(int[] row)**: возвращает сумму элементов строки.
|
||||
|
||||
3. **printMatrix(int[][] matrix, int rowsToShow)**: выводит первые 10 строк матрицы с их суммой.
|
||||
|
||||
4. **getHostName()**: определяет имя узла.
|
||||
|
||||
---
|
||||
|
||||
## Тестирование
|
||||
После запуска приложения на двух контейнерах, видим такой вывод (на главном):
|
||||
|
||||
```
|
||||
MPJ Express (0.44) is started in the cluster configuration with niodev
|
||||
Starting process <0> on <CT117>
|
||||
Starting process <1> on <CT118>
|
||||
Node 0 executing on CT117
|
||||
Node 1 executing on CT118
|
||||
Sorted matrix (first 10 rows, first 10 elements each) with row sums:
|
||||
Sum: 470657 -> [908, 526, 945, 193, 501, 547, 564, 355, 259, 717]
|
||||
Sum: 470813 -> [342, 532, 190, 920, 164, 504, 170, 837, 988, 583]
|
||||
Sum: 473327 -> [674, 633, 205, 360, 882, 996, 151, 467, 41, 134]
|
||||
Sum: 474348 -> [665, 904, 777, 331, 574, 94, 938, 893, 659, 352]
|
||||
Sum: 474754 -> [37, 566, 213, 663, 916, 383, 733, 902, 239, 790]
|
||||
Sum: 477012 -> [887, 663, 634, 535, 909, 386, 967, 364, 741, 359]
|
||||
Sum: 477206 -> [784, 974, 714, 510, 556, 682, 502, 597, 689, 972]
|
||||
Sum: 477311 -> [239, 267, 394, 124, 732, 828, 840, 702, 930, 121]
|
||||
Sum: 477356 -> [419, 377, 788, 405, 516, 826, 377, 221, 22, 329]
|
||||
Sum: 477380 -> [765, 7, 696, 942, 902, 983, 310, 775, 443, 888]
|
||||
Sorting time: 988 ms
|
||||
Stopping Process <0> on <CT117>
|
||||
Stopping Process <1> on <CT118>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Выводы
|
||||
- MPI Express обладает рядом существенных преимуществ, как, например, горизонтальное масштабирование, позволяющее подключать большое количество узлов, тем самым максимально увеличивая производительность системы.
|
||||
|
||||
|
||||
- Также к серьезным плюсам MPI можно отнести оптимизацию под НРС, что делает MPI отличным инструментом для кластеризированных задач.
|
||||
|
||||
|
||||
- Если сравнивать технологию MPI с технологиями для многопоточных вычислений, такими как ThreadPoolExecutor и ForkJoinPool, MPI будет предпочтительнее в работе между кластерами по сети (для распределенных систем, где данные находятся на разных узлах), а многопоточные подходы, такие как ThreadPoolExecutor и ForkJoinPool, лучше подходят для задач на одной машине благодаря низким накладным расходам (отсутствуют расходы на передачу сообщений между процессами) и использованию общей памяти.
|
||||
Reference in New Issue
Block a user