Files

Лабораторная работа №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)
    apt install openjdk-21-jdk
    
  • MPJ Express
    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-пользователя.

ssh-keygen -t rsa -b 4096
ssh-copy-id sshuser@CT118 

3. Компиляция кода

В папке проекта должен лежать java-класс. Используем javac и библиотеку MPI для компиляции.

javac -cp $MPJ_HOME/lib/mpj.jar Main.java

4. Запуск программы

Запускаем daemon-ы на обоих контейнерах и вводим команду для запуска самого проекта.

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, лучше подходят для задач на одной машине благодаря низким накладным расходам (отсутствуют расходы на передачу сообщений между процессами) и использованию общей памяти.